From c8e5702127e507e82e6f68a4b8c546803accea9d Mon Sep 17 00:00:00 2001 From: Koen Kooi Date: Thu, 30 Jun 2005 08:19:37 +0000 Subject: import clean BK tree at cset 1.3670 --- packages/linux/linux-mtx-1-2.4.27/.mtn2git_empty | 0 .../linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff | 51503 ++++++++++++++++ .../linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff | 248 + .../03-mtd-erase-compiler-bug.diff | 21 + .../linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff | 15 + .../linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff | 18 + .../linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch | 5308 ++ .../07-zboot-zimage-flash-bin.diff | 11 + .../linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch | 3185 + .../linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff | 10 + .../linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff | 11 + .../11-mtd-proc-partition-rw.diff | 173 + .../12-openswan-2.2.0-nat-t.diff | 143 + .../linux-mtx-1-2.4.27/13-openswan-2.2.0.patch | 61293 +++++++++++++++++++ .../14-au1000-eth-link-beat.diff | 64 + .../15-au1000-pci-fixup-non-coherent-pre-ac.diff | 19 + packages/linux/linux-mtx-1-2.4.27/16-i2c.patch | 7482 +++ .../linux-mtx-1-2.4.27/17-lmsensors.2.8.8.patch | 40940 +++++++++++++ .../linux-mtx-1-2.4.27/18-i2c-au1x00gpio.patch | 466 + .../linux-mtx-1-2.4.27/19-kernel-make-depend.diff | 11 + .../20-au1x00_ethernet_tx_stats.diff | 64 + .../linux-mtx-1-2.4.27/21-mtx-1-watchdog.diff | 231 + 22 files changed, 171216 insertions(+) create mode 100644 packages/linux/linux-mtx-1-2.4.27/.mtn2git_empty (limited to 'packages/linux/linux-mtx-1-2.4.27') diff --git a/packages/linux/linux-mtx-1-2.4.27/.mtn2git_empty b/packages/linux/linux-mtx-1-2.4.27/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff b/packages/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff index e69de29bb2..6c3b3b9acb 100644 --- a/packages/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff +++ b/packages/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff @@ -0,0 +1,51503 @@ +diff -Nurb linux-mips-2.4.27/drivers/mtd/Config.in linux/drivers/mtd/Config.in +--- linux-mips-2.4.27/drivers/mtd/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/Config.in 2004-11-19 10:25:11.607244176 +0100 +@@ -1,5 +1,5 @@ + +-# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ ++# $Id: Config.in,v 1.75 2003/05/23 11:38:29 dwmw2 Exp $ + + mainmenu_option next_comment + comment 'Memory Technology Devices (MTD)' +@@ -30,6 +30,7 @@ + if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then + bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW + fi ++ dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD + + source drivers/mtd/chips/Config.in + +diff -Nurb linux-mips-2.4.27/drivers/mtd/Makefile linux/drivers/mtd/Makefile +--- linux-mips-2.4.27/drivers/mtd/Makefile 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/Makefile 2004-11-19 10:25:11.608244024 +0100 +@@ -1,30 +1,7 @@ + # + # Makefile for the memory technology device drivers. + # +-# Note! Dependencies are done automagically by 'make dep', which also +-# removes any old dependencies. DON'T put your own dependencies here +-# unless it's something special (ie not a .c file). +-# +-# Note 2! The CFLAGS definitions are now inherited from the +-# parent makes.. +-# +-# $Id: Makefile,v 1.65 2002/03/22 07:10:34 dwmw2 Exp $ +- +- +-obj-y += chips/chipslink.o maps/mapslink.o \ +- devices/devlink.o nand/nandlink.o +-obj-m := +-obj-n := +-obj- := +- +-O_TARGET := mtdlink.o +- +-export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o +-list-multi := nftl.o +- +-mod-subdirs := +-subdir-y := chips maps devices nand +-subdir-m := $(subdir-y) ++# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $ + + # *** BIG UGLY NOTE *** + # +@@ -52,15 +29,44 @@ + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o +-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +-obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o +-obj-$(CONFIG_FTL) += ftl.o +-obj-$(CONFIG_NFTL) += nftl.o ++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o ++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o ++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o ++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o ++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o + + nftl-objs := nftlcore.o nftlmount.o ++inftl-objs := inftlcore.o inftlmount.o ++ ++ifeq ($(PATCHLEVEL),4) ++ ++export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \ ++ mtdconcat.o mtd_blkdevs-24.o ++ ++mtd_blkdevs-objs := mtd_blkdevs-24.o ++ ++obj-y += chips/chipslink.o maps/mapslink.o \ ++ devices/devlink.o nand/nandlink.o ++ ++O_TARGET := mtdlink.o ++ ++list-multi := nftl.o inftl.o mtd_blkdevs.o ++ ++mod-subdirs := ++subdir-y := chips maps devices nand ++subdir-m := $(subdir-y) + + include $(TOPDIR)/Rules.make + + nftl.o: $(nftl-objs) + $(LD) -r -o $@ $(nftl-objs) + ++inftl.o: $(inftl-objs) ++ $(LD) -r -o $@ $(inftl-objs) ++ ++mtd_blkdevs.o: $(mtd_blkdevs-objs) ++ $(LD) -r -o $@ $(mtd_blkdevs-objs) ++ ++else ++obj-y += chips/ maps/ devices/ nand/ ++endif +diff -Nurb linux-mips-2.4.27/drivers/mtd/afs.c linux/drivers/mtd/afs.c +--- linux-mips-2.4.27/drivers/mtd/afs.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/afs.c 2004-11-19 10:25:11.610243720 +0100 +@@ -21,7 +21,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id: afs.c,v 1.8 2002/05/04 08:49:09 rmk Exp $ ++ $Id: afs.c,v 1.12 2003/06/13 15:31:06 rmk Exp $ + + ======================================================================*/ + +@@ -76,17 +76,19 @@ + return ret; + } + ++ ret = 1; ++ + /* + * Does it contain the magic number? + */ + if (fs.signature != 0xa0ffff9f) +- ret = 1; ++ ret = 0; + + /* + * Don't touch the SIB. + */ + if (fs.type == 2) +- ret = 1; ++ ret = 0; + + *iis_start = fs.image_info_base & mask; + *img_start = fs.image_start & mask; +@@ -96,14 +98,14 @@ + * be located after the footer structure. + */ + if (*iis_start >= ptr) +- ret = 1; ++ ret = 0; + + /* + * Check the start of this image. The image + * data can not be located after this block. + */ + if (*img_start > off) +- ret = 1; ++ ret = 0; + + return ret; + } +@@ -125,7 +127,9 @@ + return ret; + } + +-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts) ++static int parse_afs_partitions(struct mtd_info *mtd, ++ struct mtd_partition **pparts, ++ unsigned long origin) + { + struct mtd_partition *parts; + u_int mask, off, idx, sz; +@@ -150,7 +154,7 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + ret = afs_read_iis(mtd, &iis, iis_ptr); +@@ -183,7 +187,7 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + /* Read the image info block */ +@@ -227,7 +231,25 @@ + return idx ? idx : ret; + } + +-EXPORT_SYMBOL(parse_afs_partitions); ++static struct mtd_part_parser afs_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_afs_partitions, ++ .name = "afs", ++}; ++ ++static int __init afs_parser_init(void) ++{ ++ return register_mtd_parser(&afs_parser); ++} ++ ++static void __exit afs_parser_exit(void) ++{ ++ deregister_mtd_parser(&afs_parser); ++} ++ ++module_init(afs_parser_init); ++module_exit(afs_parser_exit); ++ + + MODULE_AUTHOR("ARM Ltd"); + MODULE_DESCRIPTION("ARM Firmware Suite partition parser"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Config.in linux/drivers/mtd/chips/Config.in +--- linux-mips-2.4.27/drivers/mtd/chips/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/Config.in 2004-11-19 10:25:11.712228216 +0100 +@@ -1,6 +1,6 @@ + # drivers/mtd/chips/Config.in + +-# $Id: Config.in,v 1.16 2002/09/03 13:30:43 joern Exp $ ++# $Id: Config.in,v 1.17 2003/09/25 14:40:34 thayne Exp $ + + mainmenu_option next_comment + +@@ -11,13 +11,12 @@ + + if [ "$CONFIG_MTD_CFI" = "y" -o "$CONFIG_MTD_JEDECPROBE" = "y" ]; then + define_bool CONFIG_MTD_GEN_PROBE y +-else +- if [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then ++elif [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then + define_bool CONFIG_MTD_GEN_PROBE m +- else ++else + define_bool CONFIG_MTD_GEN_PROBE n +- fi + fi ++ + if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then + bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS + if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then +@@ -44,8 +43,27 @@ + fi + dep_tristate ' Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE + dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE ++if [ "$CONFIG_MTD_CFI_AMDSTD" = "y" -o "$CONFIG_MTD_CFI_AMDSTD" = "m" ]; then ++ bool ' Retry failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY n ++ if [ "$CONFIG_MTD_CFI_AMDSTD_RETRY" = "y" ]; then ++ int ' Max retries of failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0 ++ fi ++fi ++ + dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE + ++if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL y ++elif [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL m ++else ++ define_bool CONFIG_MTD_CFI_UTIL n ++fi ++ + dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD + dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD + dep_tristate ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile +--- linux-mips-2.4.27/drivers/mtd/chips/Makefile 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/chips/Makefile 2004-11-19 10:25:11.714227912 +0100 +@@ -1,11 +1,12 @@ + # + # linux/drivers/chips/Makefile + # +-# $Id: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $ ++# $Id: Makefile.common,v 1.3 2003/09/25 14:40:34 thayne Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := chipslink.o +- +-export-objs := chipreg.o gen_probe.o ++export-objs := chipreg.o gen_probe.o cfi_util.o ++endif + + # *** BIG UGLY NOTE *** + # +@@ -17,6 +18,7 @@ + obj-$(CONFIG_MTD) += chipreg.o + obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o + obj-$(CONFIG_MTD_CFI) += cfi_probe.o ++obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o + obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o + obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o + obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o +@@ -28,4 +30,4 @@ + obj-$(CONFIG_MTD_SHARP) += sharp.o + obj-$(CONFIG_MTD_ABSENT) += map_absent.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c +--- linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/amd_flash.c 2004-11-19 10:25:11.722226696 +0100 +@@ -3,7 +3,7 @@ + * + * Author: Jonas Holmberg + * +- * $Id: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $ ++ * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $ + * + * Copyright (c) 2001 Axis Communications AB + * +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -125,10 +126,10 @@ + + + static struct mtd_chip_driver amd_flash_chipdrv = { +- probe: amd_flash_probe, +- destroy: amd_flash_destroy, +- name: "amd_flash", +- module: THIS_MODULE ++ .probe = amd_flash_probe, ++ .destroy = amd_flash_destroy, ++ .name = "amd_flash", ++ .module = THIS_MODULE + }; + + +@@ -140,11 +141,11 @@ + static inline __u32 wide_read(struct map_info *map, __u32 addr) + { + if (map->buswidth == 1) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (map->buswidth == 2) { +- return map->read16(map, addr); ++ return map_read16(map, addr); + } else if (map->buswidth == 4) { +- return map->read32(map, addr); ++ return map_read32(map, addr); + } + + return 0; +@@ -153,11 +154,11 @@ + static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) + { + if (map->buswidth == 1) { +- map->write8(map, val, addr); ++ map_write8(map, val, addr); + } else if (map->buswidth == 2) { +- map->write16(map, val, addr); ++ map_write16(map, val, addr); + } else if (map->buswidth == 4) { +- map->write32(map, val, addr); ++ map_write32(map, val, addr); + } + } + +@@ -424,231 +425,228 @@ + + static struct mtd_info *amd_flash_probe(struct map_info *map) + { +- /* Keep this table on the stack so that it gets deallocated after the +- * probe is done. +- */ +- const struct amd_flash_info table[] = { ++ static const struct amd_flash_info table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BB, +- name: "Fujitsu MBM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BB, ++ .name = "Fujitsu MBM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W800T, ++ .name = "ST M29W800T", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS323D, +- name: "AMD AM29BDS323D", +- size: 0x00400000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 48 }, +- { offset: 0x300000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29BDS323D, ++ .name = "AMD AM29BDS323D", ++ .size = 0x00400000, ++ .numeraseregions = 3, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, ++ { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS643D, +- name: "AMD AM29BDS643D", +- size: 0x00800000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 96 }, +- { offset: 0x600000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29BDS643D, ++ .name = "AMD AM29BDS643D", ++ .size = 0x00800000, ++ .numeraseregions = 3, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, ++ { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16x, +- name: "Atmel AT49xV16x", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x02000, numblocks: 8 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16x, ++ .name = "Atmel AT49xV16x", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16xT, +- name: "Atmel AT49xV16xT", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16xT, ++ .name = "Atmel AT49xV16xT", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } + } + } + }; +@@ -822,7 +820,7 @@ + + chip->state = FL_READY; + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +@@ -984,7 +982,7 @@ + u_char tmp_buf[4]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + bus_ofs + private->chips[chipnum].start, + map->buswidth); + while (len && i < map->buswidth) +@@ -1057,7 +1055,7 @@ + u_char tmp_buf[2]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + ofs + private->chips[chipnum].start, + map->buswidth); + while (len--) { +@@ -1178,7 +1176,7 @@ + __u8 verify; + + for (address = adr; address < (adr + size); address++) { +- if ((verify = map->read8(map, address)) != 0xFF) { ++ if ((verify = map_read8(map, address)) != 0xFF) { + error = 1; + break; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-19 10:25:11.733225024 +0100 +@@ -4,7 +4,7 @@ + * + * (C) 2000 Red Hat. GPL'd + * +- * $Id: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $ ++ * $Id: cfi_cmdset_0001.c,v 1.132 2003/11/04 19:34:22 thayne Exp $ + * + * + * 10/10/2000 Nicolas Pitre +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -29,10 +30,14 @@ + #include + #include + #include +-#include ++#include + #include ++#include ++ ++/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ + +-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE ++// debugging, turns off buffer write mode if set to 1 ++#define FORCE_WORD_WRITE 0 + + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -52,16 +57,21 @@ + + static struct mtd_info *cfi_intelext_setup (struct map_info *); + +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf); +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len); + ++ ++/* ++ * *********** SETUP AND PROBE BITS *********** ++ */ ++ + static struct mtd_chip_driver cfi_intelext_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_intelext_destroy, +- name: "cfi_cmdset_0001", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_intelext_destroy, ++ .name = "cfi_cmdset_0001", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -102,13 +112,63 @@ + } + + printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", +- extp->VccOptimal >> 8, extp->VccOptimal & 0xf); ++ extp->VccOptimal >> 4, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", +- extp->VppOptimal >> 8, extp->VppOptimal & 0xf); ++ extp->VppOptimal >> 4, extp->VppOptimal & 0xf); + } + #endif + ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ ++static void fixup_intel_strataflash(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ ++ printk(KERN_WARNING "cfi_cmdset_0001: Suspend " ++ "erase on write disabled.\n"); ++ extp->SuspendCmdSupport &= ~1; ++} ++#endif ++ ++static void fixup_st_m28w320ct(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ ++ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ ++} ++ ++static void fixup_st_m28w320cb(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ /* Note this is done after the region info is endian swapped */ ++ cfi->cfiq->EraseRegionInfo[1] = ++ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; ++}; ++ ++static struct cfi_fixup fixup_table[] = { ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++ { ++ CFI_MFR_ANY, CFI_ID_ANY, ++ fixup_intel_strataflash, NULL ++ }, ++#endif ++ { ++ 0x0020, /* STMicroelectronics */ ++ 0x00ba, /* M28W320CT */ ++ fixup_st_m28w320ct, NULL ++ }, { ++ 0x0020, /* STMicroelectronics */ ++ 0x00bb, /* M28W320CB */ ++ fixup_st_m28w320cb, NULL ++ }, { ++ 0, 0, NULL, NULL ++ } ++}; ++ + /* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The +@@ -120,7 +180,6 @@ + { + struct cfi_private *cfi = map->fldrv_priv; + int i; +- __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode == CFI_MODE_CFI) { + /* +@@ -130,59 +189,29 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; + +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); +- if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); ++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp"); ++ if (!extp) + return NULL; +- } +- +- /* Read in the Extended Query Table */ +- for (i=0; iMajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { +- printk(KERN_WARNING " Unknown IntelExt Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); +- return NULL; +- } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); + extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; ++ ++ cfi_fixup(map, fixup_table); ++ + #ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); + #endif + + if(extp->SuspendCmdSupport & 1) { +-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ +- printk(KERN_WARNING "cfi_cmdset_0001: Suspend " +- "erase on write disabled.\n"); +- extp->SuspendCmdSupport &= ~1; +-#else + printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); +-#endif + } +- /* Install our own private info structure */ +- cfi->cmdset_priv = extp; + } + + for (i=0; i< cfi->numchips; i++) { +@@ -194,8 +223,6 @@ + + map->fldrv = &cfi_intelext_chipdrv; + +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_intelext_setup(map); + } + +@@ -261,20 +288,16 @@ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + +- if(map->point && map->unpoint){ +- mtd->point = do_point; +- mtd->unpoint = do_unpoint; ++ if (map_is_linear(map)) { ++ mtd->point = cfi_intelext_point; ++ mtd->unpoint = cfi_intelext_unpoint; + } + +-#ifndef FORCE_WORD_WRITE +- if ( cfi->cfiq->BufWriteTimeoutTyp ) { +- printk("Using buffer write method\n" ); ++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { ++ printk(KERN_INFO "Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } else { +-#else +- { +-#endif +- printk("Using word write method\n" ); ++ printk(KERN_INFO "Using word write method\n" ); + mtd->write = cfi_intelext_write_words; + } + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; +@@ -286,8 +309,8 @@ + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_intelext_chipdrv; +- MOD_INC_USE_COUNT; + mtd->name = map->name; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -301,78 +324,170 @@ + return NULL; + } + +-static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++/* ++ * *********** CHIP ACCESS FUNCTIONS *********** ++ */ ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) + { +- cfi_word status, status_OK; +- unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); +- unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word status, status_OK = CMD(0x80); ++ unsigned long timeo; ++ struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv; + +- adr += chip->start; +- +- /* Ensure cmd read/writes are aligned. */ +- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); +- +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- ++ resettime: + timeo = jiffies + HZ; + retry: +- spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ + switch (chip->state) { + +- case FL_READY: +- case FL_POINT: ++ case FL_STATUS: ++ for (;;) { ++ status = cfi_read(map, adr); ++ if ((status & status_OK) == status_OK) + break; + ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", ++ (long long)status); ++ spin_unlock(chip->mutex); ++ return -EIO; ++ } ++ spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } ++ ++ case FL_READY: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; ++ return 0; + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; ++ case FL_ERASING: ++ if (!(cfip->FeatureSupport & 2) || ++ !(mode == FL_READY || mode == FL_POINT || ++ (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) ++ goto sleep; ++ ++ ++ /* Erase suspend */ ++ cfi_write(map, CMD(0xB0), adr); ++ ++ /* If the flash has finished erasing, then 'erase suspend' ++ * appears to make some (28F320) flash devices switch to ++ * 'read' mode. Make sure that we switch to 'read status' ++ * mode so we get the right data. --rmk ++ */ ++ cfi_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ status = cfi_read(map, adr); ++ if ((status & status_OK) == status_OK) + break; +- } + +- /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); ++ /* Urgh. Resume and pretend we weren't here. */ ++ cfi_write(map, CMD(0xd0), adr); ++ /* Make sure we're in 'read status' mode if it had finished */ ++ cfi_write(map, CMD(0x70), adr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "Chip not ready after erase " ++ "suspended: status = 0x%x\n", status); + return -EIO; + } + +- /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); + cfi_udelay(1); +- goto retry; ++ spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_STATUS; ++ return 0; ++ ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; + + default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ ++ sleep: + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ spin_lock(chip->mutex); ++ goto resettime; + } ++} ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ /* What if one interleaved chip has finished and the ++ other hasn't? The old code would leave the finished ++ one in READY mode. That's bad, and caused -EROFS ++ errors to be returned from do_erase_oneblock because ++ that's the only bit it checked for at the time. ++ As the state machine appears to explicitly allow ++ sending the 0x70 (Read Status) command to an erasing ++ chip and expecting it to be ignored, that's what we ++ do. */ ++ cfi_write(map, CMD(0xd0), adr); ++ cfi_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate); ++ } ++ wake_up(&chip->wq); ++} ++ ++static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret = 0; ++ ++ adr += chip->start; ++ ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ ++ spin_lock(chip->mutex); ++ ++ ret = get_chip(map, chip, cmd_addr, FL_POINT); ++ ++ if (!ret) { ++ if (chip->state != FL_POINT && chip->state != FL_READY) ++ cfi_write(map, CMD(0xff), cmd_addr); + + chip->state = FL_POINT; + chip->ref_point_counter++; ++ } + spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) ++ ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +@@ -380,12 +495,10 @@ + int chipnum; + int ret = 0; + +- if (from + len > mtd->size) ++ if (!map->virt || (from + len > mtd->size)) + return -EINVAL; + +- *mtdbuf = map->point(map, from, len); +- if(*mtdbuf == NULL) +- return -EINVAL; /* can not point this region */ ++ *mtdbuf = (void *)map->virt + from; + *retlen = 0; + + /* Now lock the chip(s) to POINT state */ +@@ -418,14 +531,13 @@ + return 0; + } + +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + +- map->unpoint(map, addr, from, len); + /* Now unlock the chip(s) POINT state */ + + /* ofs: offset within the first chip that the first read should start */ +@@ -446,13 +558,14 @@ + thislen = len; + + spin_lock(chip->mutex); +- if(chip->state == FL_POINT){ ++ if (chip->state == FL_POINT) { + chip->ref_point_counter--; + if(chip->ref_point_counter == 0) + chip->state = FL_READY; + } else +- printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ +- wake_up(&chip->wq); ++ printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ ++ ++ put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + + len -= thislen; +@@ -463,136 +576,32 @@ + + static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int suspended = 0; + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ +- switch (chip->state) { +- case FL_ERASING: +- if (!cfi->cmdset_priv || +- !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), cmd_addr); +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- // printk("Erase suspending at 0x%lx\n", cmd_addr); +- for (;;) { +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%llx\n", (__u64)status); +- return -EIO; +- } +- ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { + spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); ++ return ret; + } + +- suspended = 1; ++ if (chip->state != FL_POINT && chip->state != FL_READY) { + cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; +- break; +- +-#if 0 +- case FL_WRITING: +- /* Not quite yet */ +-#endif +- +- case FL_READY: +- case FL_POINT: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; +- break; + } + +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); +- return -EIO; +- } ++ map_copy_from(map, buf, adr, len); + +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; ++ put_chip(map, chip, cmd_addr); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- +- map->copy_from(map, buf, adr, len); +- +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- cfi_write(map, CMD(0x70), cmd_addr); +- } +- +- wake_up(&chip->wq); + spin_unlock(chip->mutex); + return 0; + } +@@ -640,70 +649,52 @@ + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp=cfi->cmdset_priv; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int count=len; ++ struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; +- int chip_num,offst; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ int count = len; ++ int chip_num, offst; ++ int ret; + +- chip=0; +- /* Calculate which chip & protection register offset we need */ +- chip_num=((unsigned int)from/reg_sz); +- offst=from-(reg_sz*chip_num)+base_offst; ++ chip_num = ((unsigned int)from/reg_sz); ++ offst = from - (reg_sz*chip_num)+base_offst; + +- while(count){ ++ while (count) { ++ /* Calculate which chip & protection register offset we need */ + +- if(chip_num>=cfi->numchips) ++ if (chip_num >= cfi->numchips) + goto out; + +- /* Make sure that the chip is in the right state */ ++ chip = &cfi->chips[chip_num]; + +- timeo = jiffies + HZ; +- chip=&cfi->chips[chip_num]; +- retry: + spin_lock(chip->mutex); +- +- switch (chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- break; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return (len-count)?:ret; + } + +- /* Now read the data required from this flash */ ++ if (chip->state != FL_JEDEC_QUERY) { ++ cfi_write(map, CMD(0x90), chip->start); ++ chip->state = FL_JEDEC_QUERY; ++ } + +- cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); +- while(count && ((offst-base_offst)read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); ++ while (count && ((offst-base_offst) < reg_sz)) { ++ *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); + buf++; + offst++; + count--; + } + +- chip->state=FL_CFI_QUERY; ++ put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); ++ + /* Move on to the next chip */ + chip_num++; +- offst=base_offst; +- ++ offst = base_offst; + } + + out: +- wake_up(&chip->wq); + return len-count; + } + +@@ -749,103 +740,20 @@ + static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int z, suspended=0, ret=0; +- +- adr += chip->start; +- +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- +- timeo = jiffies + HZ; +- retry: +- spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; ++ cfi_word status, status_OK; ++ unsigned long timeo; ++ int z, ret=0; + +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; +- } ++ adr += chip->start; + +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; ++ /* Let's determine this according to the interleave only once */ ++ status_OK = CMD(0x80); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -862,6 +770,8 @@ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -879,7 +789,6 @@ + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); + ret = -EIO; + goto out; +@@ -908,27 +817,11 @@ + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } + out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ +- +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); ++ + return ret; + } + +@@ -1059,11 +952,9 @@ + unsigned long adr, const u_char *buf, int len) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; + cfi_word status, status_OK; + unsigned long cmd_adr, timeo; +- DECLARE_WAITQUEUE(wait, current); +- int wbufsize, z, suspended=0, ret=0; ++ int wbufsize, z, ret=0, bytes, words; + + wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; +@@ -1072,91 +963,18 @@ + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) +- break; +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { ++ ret = get_chip(map, chip, cmd_adr, FL_WRITING); ++ if (ret) { + spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n"); +- return -EIO; ++ return ret; + } + +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; +- } ++ if (chip->state != FL_STATUS) ++ cfi_write(map, CMD(0x70), cmd_adr); + +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; ++ status = cfi_read(map, cmd_adr); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- /* We know we're now in FL_STATUS mode, and 'status' is current */ + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set + [...], the device will not accept any more Write to Buffer commands". + So we must check here and reset those bits if they're set. Otherwise +@@ -1185,7 +1003,6 @@ + /* Argh. Not ready for write to buffer */ + cfi_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr)); + /* Odd. Clear status bits */ + cfi_write(map, CMD(0x50), cmd_adr); +@@ -1196,20 +1013,42 @@ + } + + /* Write length of data to come */ +- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); ++ bytes = len & (CFIDEV_BUSWIDTH-1); ++ words = len / CFIDEV_BUSWIDTH; ++ cfi_write(map, CMD(words - !bytes), cmd_adr ); + + /* Write data */ +- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { ++ z = 0; ++ while(z < words * CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); ++ map_write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); ++ map_write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); ++ map_write32 (map, *((__u32*)buf)++, adr+z); + } else if (cfi_buswidth_is_8()) { +- map->write64 (map, *((__u64*)buf)++, adr+z); ++ map_write64 (map, *((__u64*)buf)++, adr+z); ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ z += CFIDEV_BUSWIDTH; ++ } ++ if (bytes) { ++ int i = 0, n = 0; ++ u_char tmp_buf[8], *tmp_p = tmp_buf; ++ ++ while (bytes--) ++ tmp_buf[i++] = buf[n++]; ++ while (i < CFIDEV_BUSWIDTH) ++ tmp_buf[i++] = 0xff; ++ if (cfi_buswidth_is_2()) { ++ map_write16 (map, *((__u16*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ map_write32 (map, *((__u32*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_8()) { ++ map_write64 (map, *((__u64*)tmp_p)++, adr+z); + } else { +- DISABLE_VPP(map); + ret = -EINVAL; + goto out; + } +@@ -1227,6 +1066,7 @@ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -1244,7 +1084,6 @@ + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); + ret = -EIO; + goto out; +@@ -1266,6 +1105,7 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; ++ + /* check for lock bit */ + if (status & CMD(0x02)) { + /* clear status */ +@@ -1273,26 +1113,10 @@ + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } +- out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ + +- wake_up(&chip->wq); ++ out: ++ put_chip(map, chip, cmd_adr); + spin_unlock(chip->mutex); + return ret; + } +@@ -1336,12 +1160,12 @@ + } + + /* Write buffer is worth it only if more than one word to write... */ +- while(len > CFIDEV_BUSWIDTH) { ++ while(len) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) +- size = len & ~(CFIDEV_BUSWIDTH-1); ++ size = len; + ret = do_write_buffer(map, &cfi->chips[chipnum], + ofs, buf, size); + if (ret) +@@ -1359,17 +1183,6 @@ + return 0; + } + } +- +- /* ... and write the remaining bytes */ +- if (len > 0) { +- size_t local_retlen; +- ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift), +- len, &local_retlen, buf); +- if (ret) +- return ret; +- (*retlen) += local_retlen; +- } +- + return 0; + } + +@@ -1479,45 +1292,12 @@ + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: ++ retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -1528,7 +1308,7 @@ + cfi_write(map, CMD(0x20), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + + spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); +@@ -1550,11 +1330,11 @@ + spin_lock(chip->mutex); + continue; + } +- if (chip->oldstate) { ++ if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + } + + status = cfi_read(map, adr); +@@ -1658,39 +1438,22 @@ + int i; + struct flchip *chip; + int ret = 0; +- DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + +- retry: + spin_lock(chip->mutex); ++ ret = get_chip(map, chip, chip->start, FL_SYNCING); + +- switch(chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: ++ if (!ret) { + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_SYNCING: +- spin_unlock(chip->mutex); +- break; +- +- default: +- /* Not an idle state */ +- add_wait_queue(&chip->wq, &wait); +- +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- +- goto retry; + } ++ spin_unlock(chip->mutex); + } + + /* Unlock the chips again */ +@@ -1731,52 +1494,18 @@ + struct cfi_private *cfi = map->fldrv_priv; + cfi_word status, status_OK; + unsigned long timeo = jiffies + HZ; +- DECLARE_WAITQUEUE(wait, current); ++ int ret; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -1823,8 +1552,7 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; +- DISABLE_VPP(map); +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); + return 0; + } +@@ -1889,22 +1617,23 @@ + + spin_lock(chip->mutex); + +- switch(chip->state) { ++ switch (chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: ++ if (chip->oldstate == FL_READY) { + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_PM_SUSPENDED: ++ } + break; +- + default: + ret = -EAGAIN; ++ case FL_PM_SUSPENDED: + break; + } + spin_unlock(chip->mutex); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_cmdset_0002.c 2004-11-19 10:25:11.745223200 +0100 +@@ -6,16 +6,22 @@ + * + * 2_by_8 routines added by Simon Munton + * ++ * 4_by_16 work by Carolyn J. Smith ++ * ++ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com ++ * + * This code is GPL + * +- * $Id: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $ ++ * $Id: cfi_cmdset_0002.c,v 1.94 2004/01/27 10:16:20 dvrabel Exp $ + * + */ + ++#include + #include + #include + #include + #include ++#include + #include + #include + +@@ -23,16 +29,50 @@ + #include + #include + #include ++#include + #include ++#include + #include + + #define AMD_BOOTLOC_BUG ++#define FORCE_WORD_WRITE 0 ++ ++ ++/* ++ * This is an attempt to coalesce the retry logic in one place - that way ++ * there aren't #ifdefs scattered throughout. ++ */ ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ ++#ifndef CONFIG_MTD_CFI_AMDSTD_RETRY_MAX ++#define CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0 ++#endif ++ ++#define RETRY_CMD_LABEL retry_cmd: do {} while (0) ++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, retry_cmd_cnt, adr, datum, prev_oldstatus, prev_status, oldstatus, status) ++static int retry_cmd_max = CONFIG_MTD_CFI_AMDSTD_RETRY_MAX; ++#define DECLARE_RETRY_CMD_CNT() int retry_cmd_cnt = 0 ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++#define CHECK_RETRIES() do { if (++retry_cmd_cnt <= retry_cmd_max) goto retry_cmd; } while (0) ++#endif ++ ++#else ++ ++#define RETRY_CMD_LABEL do {} while (0) ++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, adr, datum, prev_oldstatus, prev_status, oldstatus, status) ++#define DECLARE_RETRY_CMD_CNT() ++#define CHECK_RETRIES() ++ ++#endif /* !defined(CONFIG_MTD_CFI_AMDSTD_RETRY) */ ++ + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); +-static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *); + static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); ++static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t); ++static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t); + static void cfi_amdstd_sync (struct mtd_info *); + static int cfi_amdstd_suspend (struct mtd_info *); + static void cfi_amdstd_resume (struct mtd_info *); +@@ -45,55 +85,136 @@ + + + static struct mtd_chip_driver cfi_amdstd_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_amdstd_destroy, +- name: "cfi_cmdset_0002", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_amdstd_destroy, ++ .name = "cfi_cmdset_0002", ++ .module = THIS_MODULE + }; + +-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++ ++/* #define DEBUG_LOCK_BITS */ ++/* #define DEBUG_CFI_FEATURES */ ++ ++ ++#ifdef DEBUG_CFI_FEATURES ++static void cfi_tell_features(struct cfi_pri_amdstd *extp) + { +- struct cfi_private *cfi = map->fldrv_priv; +- unsigned char bootloc; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int i; +- __u8 major, minor; +- __u32 base = cfi->chips[0].start; ++ const char* erase_suspend[3] = { ++ "Not supported", "Read only", "Read/write" ++ }; ++ const char* top_bottom[6] = { ++ "No WP", "8x8KiB sectors at top & bottom, no WP", ++ "Bottom boot", "Top boot", ++ "Uniform, Bottom WP", "Uniform, Top WP" ++ }; ++ ++ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); ++ printk(" Address sensitive unlock: %s\n", ++ (extp->SiliconRevision & 1) ? "Not required" : "Required"); + +- if (cfi->cfi_mode==CFI_MODE_CFI){ +- __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) ++ printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); ++ else ++ printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); + +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ if (extp->BlkProt == 0) ++ printk(" Block protection: Not supported\n"); ++ else ++ printk(" Block protection: %d sectors per group\n", extp->BlkProt); ++ ++ ++ printk(" Temporary block unprotect: %s\n", ++ extp->TmpBlkUnprotect ? "Supported" : "Not supported"); ++ printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); ++ printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); ++ printk(" Burst mode: %s\n", ++ extp->BurstMode ? "Supported" : "Not supported"); ++ if (extp->PageMode == 0) ++ printk(" Page mode: Not supported\n"); ++ else ++ printk(" Page mode: %d word page\n", extp->PageMode << 2); + +- major = cfi_read_query(map, base + (adr+3)*ofs_factor); +- minor = cfi_read_query(map, base + (adr+4)*ofs_factor); ++ printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMin >> 4, extp->VppMin & 0xf); ++ printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMax >> 4, extp->VppMax & 0xf); + +- printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", +- major, minor, adr); +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi->mfr = cfi_read_query(map, base); +- cfi->id = cfi_read_query(map, base + ofs_factor); ++ if (extp->TopBottom < ARRAY_SIZE(top_bottom)) ++ printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); ++ else ++ printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); ++} ++#endif + +- /* Wheee. Bring me the head of someone at AMD. */ + #ifdef AMD_BOOTLOC_BUG ++/* Wheee. Bring me the head of someone at AMD. */ ++static void fixup_amd_bootblock(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ __u8 major = extp->MajorVersion; ++ __u8 minor = extp->MinorVersion; ++ + if (((major << 8) | minor) < 0x3131) { + /* CFI version 1.0 => don't trust bootloc */ + if (cfi->id & 0x80) { + printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); +- bootloc = 3; /* top boot */ ++ extp->TopBottom = 3; /* top boot */ + } else { +- bootloc = 2; /* bottom boot */ ++ extp->TopBottom = 2; /* bottom boot */ + } +- } else ++ } ++} + #endif ++ ++static struct cfi_fixup fixup_table[] = { ++#ifdef AMD_BOOTLOC_BUG + { +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); ++ 0x0001, /* AMD */ ++ CFI_ID_ANY, ++ fixup_amd_bootblock, NULL ++ }, ++#endif ++ { 0, 0, NULL, NULL } ++}; ++ ++ ++struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ unsigned char bootloc; ++ int i; ++ ++ if (cfi->cfi_mode==CFI_MODE_CFI){ ++ /* ++ * It's a real CFI chip, not one for which the probe ++ * routine faked a CFI structure. So we read the feature ++ * table from it. ++ */ ++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ struct cfi_pri_amdstd *extp; ++ ++ extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); ++ if (!extp) ++ return NULL; ++ ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; ++ ++ cfi_fixup(map, fixup_table); ++ ++#ifdef DEBUG_CFI_FEATURES ++ /* Tell the user about it in lots of lovely detail */ ++ cfi_tell_features(extp); ++#endif ++ ++ bootloc = extp->TopBottom; ++ if ((bootloc != 2) && (bootloc != 3)) { ++ printk(KERN_WARNING "%s: CFI does not contain boot " ++ "bank location. Assuming top.\n", map->name); ++ bootloc = 2; + } ++ + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + +@@ -106,6 +227,11 @@ + cfi->cfiq->EraseRegionInfo[j] = swap; + } + } ++ /* ++ * These might already be setup (more correctly) by ++ * jedec_probe.c - still need it for cfi_probe.c path. ++ */ ++ if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) { + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: + cfi->addr_unlock1 = 0x555; +@@ -125,9 +251,13 @@ + cfi->addr_unlock2 = 0xaaa; + break; + default: +- printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type); ++ printk(KERN_WARNING ++ "MTD %s(): Unsupported device type %d\n", ++ __func__, cfi->device_type); + return NULL; + } ++ } ++ + } /* CFI mode */ + + for (i=0; i< cfi->numchips; i++) { +@@ -138,15 +268,17 @@ + + map->fldrv = &cfi_amdstd_chipdrv; + +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_amdstd_setup(map); + } + ++ + static struct mtd_info *cfi_amdstd_setup(struct map_info *map) + { + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; ++ unsigned long offset = 0; ++ int i,j; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + printk(KERN_NOTICE "number of %s chips: %d\n", +@@ -163,15 +295,9 @@ + /* Also select the correct geometry setup too */ + mtd->size = devsize * cfi->numchips; + +- if (cfi->cfiq->NumEraseRegions == 1) { +- /* No need to muck about with multiple erase sizes */ +- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; +- } else { +- unsigned long offset = 0; +- int i,j; +- + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; +- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); ++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) ++ * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); + goto setup_err; +@@ -206,39 +332,52 @@ + mtd->eraseregions[i].numblocks); + } + #endif +- } + + switch (CFIDEV_BUSWIDTH) + { + case 1: + case 2: + case 4: +-#if 1 +- if (mtd->numeraseregions > 1) +- mtd->erase = cfi_amdstd_erase_varsize; +- else ++#ifdef CFI_WORD_64 ++ case 8: + #endif +- if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) ++ if (mtd->numeraseregions == 1 ++ && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) { + mtd->erase = cfi_amdstd_erase_chip; +- else +- mtd->erase = cfi_amdstd_erase_onesize; ++ } else { ++ mtd->erase = cfi_amdstd_erase_varsize; ++ mtd->lock = cfi_amdstd_lock_varsize; ++ mtd->unlock = cfi_amdstd_unlock_varsize; ++ } ++ ++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { ++ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); ++ mtd->write = cfi_amdstd_write_buffers; ++ } else { ++ DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" ); ++ mtd->write = cfi_amdstd_write_words; ++ } ++ + mtd->read = cfi_amdstd_read; +- mtd->write = cfi_amdstd_write; + break; + + default: +- printk(KERN_WARNING "Unsupported buswidth\n"); ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); + goto setup_err; + break; + } + if (cfi->fast_prog) { +- /* In cfi_amdstd_write() we frob the protection stuff ++ /* In cfi_amdstd_write_words() we frob the protection stuff + without paying any attention to the state machine. + This upsets in-progress erases. So we turn this flag + off for now till the code gets fixed. */ + printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); + cfi->fast_prog = 0; + } ++ /* FIXME: erase-suspend-program is broken. See ++ http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ ++ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); + + + /* does this chip have a secsi area? */ +@@ -266,7 +405,7 @@ + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_amdstd_chipdrv; + mtd->name = map->name; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -280,46 +419,210 @@ + return NULL; + } + +-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++ ++/* This is more work to coalesce the retry #ifdefs in one location */ ++static inline void handle_wacky_state(const char *func, ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ int retry_cmd_cnt, ++#endif ++ unsigned long adr, ++ cfi_word datum, ++ cfi_word prev_oldstatus, ++ cfi_word prev_status, ++ cfi_word oldstatus, ++ cfi_word status) ++{ ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ if ( retry_cmd_cnt == retry_cmd_max ) { ++#endif ++ printk(KERN_WARNING ++ "MTD %s(): Wacky! Unable to decode failure status\n" ++ "Possible buggy device - try " ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ "increasing retry_cmd_max from %d\n" ++#else ++ "enabling CONFIG_MTD_CFI_AMDSTD_RETRY\n" ++ "in your kernel config and setting driver retry_cmd_max\n" ++#endif ++ , func ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ , retry_cmd_max ++#endif ++ ); ++ ++ printk(KERN_WARNING ++ "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", ++ func, adr, datum, ++ prev_oldstatus, prev_status, ++ oldstatus, status); ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ } ++#endif ++} ++ ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) + { + DECLARE_WAITQUEUE(wait, current); +- unsigned long timeo = jiffies + HZ; ++ struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word status, oldstatus; ++ cfi_word dq6 = CMD(1<<6); ++ cfi_word dq2 = CMD(1<<2); ++ unsigned long timeo; ++ struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; + ++ resettime: ++ timeo = jiffies + HZ; + retry: ++ switch (chip->state) { ++ ++ case FL_STATUS: ++ for (;;) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ if (((oldstatus ^ status) & (dq6 | dq2)) == 0) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", ++ (long long)status); ++ cfi_spin_unlock(chip->mutex); ++ return -EIO; ++ } ++ cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); + cfi_spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } + +- if (chip->state != FL_READY){ +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ case FL_READY: ++ case FL_CFI_QUERY: ++ case FL_JEDEC_QUERY: ++ return 0; ++ ++ case FL_ERASING: ++ if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ ++ goto sleep; ++ ++ if (!(mode == FL_READY || mode == FL_POINT ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) ++ goto sleep; ++ ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ if ((oldstatus ^ status) & dq2) { ++ printk(KERN_ERR "Can't suspend erase -- block in progress\n"); ++ goto sleep; ++ } ++ ++ /* Erase suspend */ ++ /* FIXME - is there a way to verify suspend? */ ++ cfi_write(map, CMD(0xB0), chip->in_progress_block_addr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ oldstatus = cfi_read(map, chip->in_progress_block_addr); ++ status = cfi_read(map, chip->in_progress_block_addr); ++ if (((oldstatus ^ status) & dq6) == 0) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ /* Urgh. Resume and pretend we weren't here. */ ++ /* FIXME - is there a way to verify resume? */ ++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "Chip not ready after erase " ++ "suspended: status = 0x%x\n", status); ++ return -EIO; ++ } + + cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ cfi_spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_READY; ++ return 0; ++ ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; + ++ default: ++ sleep: ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; ++ cfi_spin_lock(chip->mutex); ++ goto resettime; ++ } ++} + +- goto retry; ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); + } ++ wake_up(&chip->wq); ++} ++ ++ ++static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ if (chip->state != FL_POINT && chip->state != FL_READY) { ++ cfi_write(map, CMD(0xf0), cmd_addr); + chip->state = FL_READY; ++ } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); ++ put_chip(map, chip, cmd_addr); + ++ cfi_spin_unlock(chip->mutex); + return 0; + } + ++ + static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { + struct map_info *map = mtd->priv; +@@ -361,6 +664,7 @@ + return ret; + } + ++ + static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { + DECLARE_WAITQUEUE(wait, current); +@@ -394,12 +698,14 @@ + + chip->state = FL_READY; + ++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + ++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); +@@ -454,125 +760,241 @@ + return ret; + } + +-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) ++ ++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum, int fast) + { +- unsigned long timeo = jiffies + HZ; +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); ++ unsigned long timeo = jiffies + HZ; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ /* ++ * We use a 1ms + 1 jiffies generic timeout for writes (most devices ++ * have a max write time of a few hundreds usec). However, we should ++ * use the maximum timeout value given by the chip at probe time ++ * instead. Unfortunately, struct flchip does have a field for ++ * maximum timeout, only for typical which can be far too short ++ * depending of the conditions. The ' + 1' is to avoid having a ++ * timeout of 0 jiffies if HZ is smaller than 1000. ++ */ ++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + int ret = 0; ++ int ta = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- retry: +- cfi_spin_lock(chip->mutex); +- +- if (chip->state != FL_READY) { +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr += chip->start; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- printk(KERN_DEBUG "Wake up to write:\n"); +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; +- +- goto retry; ++ return ret; + } + +- chip->state = FL_WRITING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", ++ __func__, adr, datum ); ++ ++ /* ++ * Check for a NOP for the case when the datum to write is already ++ * present - it saves time and works around buggy chips that corrupt ++ * data at other locations when 0xff is written to a location that ++ * already contains 0xff. ++ */ ++ status = cfi_read(map, adr); ++ if (status == datum) { ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP 0x%.8x == 0x%.8x\n", ++ __func__, status, datum ); ++ goto op_done; ++ } + +- adr += chip->start; + ENABLE_VPP(map); + if (fast) { /* Unlock bypass */ + cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); +- } +- else { ++ } else { ++ /* ++ * The CFI_DEVICETYPE_X8 argument is needed even when ++ * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for ++ * command sequences don't scale even when the device is ++ * wider. This is the case for many of the cfi_send_gen_cmd() ++ * below. I'm not sure, however, why some use ++ * cfi->device_type. ++ */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + } +- + cfi_write(map, datum, adr); ++ chip->state = FL_WRITING; + + cfi_spin_unlock(chip->mutex); + cfi_udelay(chip->word_write_time); + cfi_spin_lock(chip->mutex); + +- /* Polling toggle bits instead of reading back many times +- This ensures that write operation is really completed, +- or tells us why it failed. */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); +- timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */ ++ /* ++ * Polling toggle bits instead of reading back many times This ensures ++ * that write operation is really completed, or tells us why it ++ * failed. ++ * ++ * It may appear that the polling and decoding of error state might be ++ * simplified. Don't do it unless you really know what you are doing. ++ * ++ * You must remember that JESD21-C 3.5.3 states that the status must ++ * be read back an _additional_ two times before a failure is ++ * determined. This is because these devices have internal state ++ * machines that are asynchronous to the external data bus. During an ++ * erase or write the read-back status of the polling bits might be ++ * transitioning internaly when the external read-back occurs. This ++ * means that the bits aren't in the final state and they might appear ++ * to report an error as they are in a transient state: dq7 is ++ * asynchronous with dq6 and other status bits. ++ * ++ * This asynchronous behaviour can cause infrequent errors that will ++ * usually disappear the next time an erase or write happens (Try ++ * tracking those errors down!). To ensure that the bits are not in ++ * transition, the location must be read-back two more times and ++ * compared against what was written - BOTH reads MUST match what was ++ * written. Don't think this can be simplified to only the last read ++ * matching the datum written: status bits *can* match the datum ++ * written. ++ * ++ * If the final comparison fails, error state can *then* be decoded. ++ * ++ * - Thayne Harbaugh ++ */ ++ /* See comment above for timeout value. */ ++ timeo = jiffies + uWriteTimeout; ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ timeo = jiffies + (HZ / 2); /* FIXME */ ++ cfi_spin_lock(chip->mutex); ++ continue; ++ } + + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- while( (status & dq6) != (oldstatus & dq6) && +- (status & dq5) != dq5 && +- !time_after(jiffies, timeo) ) { ++ /* ++ * This only checks if dq6 is still toggling and that our ++ * timer hasn't expired. We purposefully ignore the chip's ++ * internal timer that will assert dq5 and leave dq6 toggling. ++ * This is done for a variety of reasons: ++ * ++ * 1) Not all chips support dq5. ++ * ++ * 2) Dealing with asynchronous status bit and data updates ++ * and reading a device two more times creates _messy_ logic ++ * when trying to deal with interleaved devices - some may be ++ * changing while others are still busy. ++ * ++ * 3) Checking dq5 only helps to optimize an error case that ++ * should at worst be infrequent and at best non-existent. ++ * ++ * If our timeout occurs _then_ we will check dq5 to see if ++ * the device also had an internal timeout. ++ */ ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) ++ break; + +- if (need_resched()) { ++ /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- yield(); ++ cfi_udelay(1); + cfi_spin_lock(chip->mutex); +- } else +- udelay(1); +- +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); + } + +- if( (status & dq6) != (oldstatus & dq6) ) { +- /* The erasing didn't stop?? */ +- if( (status & dq5) == dq5 ) { +- /* When DQ5 raises, we must check once again +- if DQ6 is toggling. If not, the erase has been +- completed OK. If not, reset chip. */ ++ /* ++ * Something kicked us out of the read-back loop. We'll check success ++ * befor checking failure. Even though dq6 might be true data, it is ++ * unkown if all of the other bits have changed to true data due to ++ * the asynchronous nature of the internal state machine. We will ++ * read two more times and use this to either verify that the write ++ * completed successfully or that something really went wrong. BOTH ++ * reads must match what was written - this certifies that bits aren't ++ * still changing and that the status bits erroneously match the datum ++ * that was written. ++ */ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) { +- printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" ); ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n", ++ __func__, ++ status & dq5mask, status, datum ); + } else { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" ); ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); ++ } ++ goto op_failed; + } +- } else { +- printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword."); + ++ /* ++ * If we get to here then it means that something ++ * is wrong and it's not a timeout. Something ++ * is seriously wacky! Dump some debug info. ++ */ ++ /* ++ * Found a clue about the chips that reach this state. ++ * Some flash chips (SST >cough<) ++ * are horribly broken. They do not ignore traffic that is ++ * destined to other devices. This happens because some solutions ++ * are on shared busses, the erase and program sequences have ++ * have multiple commands, and the sequence is interspersed with ++ * commands destined to other devices. A good flash chip will ++ * examine the command and destination address and will ignore ++ * commands that are for other devices. ++ */ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- ret = -EIO; +- } +- } +- +- DISABLE_VPP(map); +- chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + + return ret; + } + +-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) ++ ++static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs, chipstart; ++ DECLARE_WAITQUEUE(wait, current); + + *retlen = 0; + if (!len) +@@ -587,19 +1009,52 @@ + unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + int i = ofs - bus_ofs; + int n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ u_char tmp_buf[8]; ++ cfi_word datum; ++ ++ retry: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); ++ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry; ++ } ++ ++ map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + +- map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +- while (len && i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = buf[n++], len--; ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ while (len && i < CFIDEV_BUSWIDTH) { ++ tmp_buf[i++] = buf[n++]; ++ len--; ++ } + ++ /* already know that buswidth > 1 */ + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)tmp_buf; ++#endif + } else { +- return -EINVAL; /* should never happen, but be safe */ ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -628,7 +1083,7 @@ + + /* We are now aligned, write as much as possible */ + while(len >= CFIDEV_BUSWIDTH) { +- __u32 datum; ++ cfi_word datum; + + if (cfi_buswidth_is_1()) { + datum = *(__u8*)buf; +@@ -636,7 +1091,13 @@ + datum = *(__u16*)buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)buf; ++#endif + } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); + return -EINVAL; + } + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -685,10 +1146,34 @@ + /* Write the trailing bytes if any */ + if (len & (CFIDEV_BUSWIDTH-1)) { + int i = 0, n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ u_char tmp_buf[8]; ++ cfi_word datum; ++ ++ retry1: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); ++ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry1; ++ } ++ ++ map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); + +- map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + while (len--) + tmp_buf[i++] = buf[n++]; + +@@ -696,8 +1181,14 @@ + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)tmp_buf; ++#endif + } else { +- return -EINVAL; /* should never happen, but be safe */ ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -711,289 +1202,446 @@ + return 0; + } + +-static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ++ ++/* ++ * FIXME: interleaved mode not tested, and probably not supported! ++ */ ++static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ++ unsigned long adr, const u_char *buf, int len) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; +- unsigned int adr; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); +- +- retry: +- cfi_spin_lock(chip->mutex); ++ unsigned long timeo = jiffies + HZ; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ /* see comments in do_write_oneword() regarding uWriteTimeo. */ ++ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; ++ int ret = -EIO; ++ int ta = 0; ++ unsigned long cmd_adr; ++ int z, bytes, words; ++ cfi_word datum; ++ DECLARE_RETRY_CMD_CNT(); + +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr += chip->start; ++ cmd_adr = adr; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; ++ if (cfi_buswidth_is_1()) { ++ datum = *(__u8*)buf; ++ } else if (cfi_buswidth_is_2()) { ++ datum = *(__u16*)buf; ++ } else if (cfi_buswidth_is_4()) { ++ datum = *(__u32*)buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)buf; + #endif +- timeo = jiffies + HZ; +- +- goto retry; ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + +- chip->state = FL_ERASING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", ++ __func__, adr, datum ); + +- /* Handle devices with one erase region, that only implement +- * the chip erase command. +- */ + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- timeo = jiffies + (HZ*20); +- adr = cfi->addr_unlock1; ++ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. +- */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++ /* Write Buffer Load */ ++ cfi_write(map, CMD(0x25), cmd_adr); + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ chip->state = FL_WRITING_TO_BUFFER; ++ ++ /* Write length of data to come */ ++ bytes = len & (CFIDEV_BUSWIDTH-1); ++ words = len / CFIDEV_BUSWIDTH; ++ cfi_write(map, CMD(words - !bytes), cmd_adr ); ++ /* Write data */ ++ z = 0; ++ while(z < words * CFIDEV_BUSWIDTH) { ++ if (cfi_buswidth_is_1()) { ++ datum = *((__u8*)buf); ++ map_write8 (map, *((__u8*)buf)++, adr+z); ++ } else if (cfi_buswidth_is_2()) { ++ datum = *((__u16*)buf); ++ map_write16 (map, *((__u16*)buf)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ datum = *((__u32*)buf); ++ map_write32 (map, *((__u32*)buf)++, adr+z); ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *((__u64*)buf); ++ map_write64 (map, *((__u64*)buf)++, adr+z); ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ ret = -EINVAL; ++ goto op_failed; ++ } ++ z += CFIDEV_BUSWIDTH; ++ } ++ if (bytes) { ++ int i = 0, n = 0; ++ u_char tmp_buf[8], *tmp_p = tmp_buf; ++ ++ while (bytes--) ++ tmp_buf[i++] = buf[n++]; ++ while (i < CFIDEV_BUSWIDTH) ++ tmp_buf[i++] = 0xff; ++ if (cfi_buswidth_is_2()) { ++ datum = *((__u16*)tmp_p); ++ map_write16 (map, *((__u16*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ datum = *((__u32*)tmp_p); ++ map_write32 (map, *((__u32*)tmp_p)++, adr+z); ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *((__u64*)tmp_p); ++ map_write64 (map, *((__u64*)tmp_p)++, adr+z); ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ ret = -EINVAL; ++ goto op_failed; ++ } ++ } else if (words > 0) { ++ z -= CFIDEV_BUSWIDTH; ++ } ++ ++ adr += z; ++ ++ /* Write Buffer Program Confirm: GO GO GO */ ++ cfi_write(map, CMD(0x29), cmd_adr); ++ chip->state = FL_WRITING; + +- /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ cfi_udelay(chip->buffer_write_time); + cfi_spin_lock(chip->mutex); + +- if (chip->state != FL_ERASING) { +- /* Someone's suspended the erase. Sleep */ ++ timeo = jiffies + uWriteTimeout; ++ ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk("erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ ++ timeo = jiffies + (HZ / 2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ /* See comments in do_write_oneword() about checking status */ ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) { ++ break; ++ } + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- + cfi_udelay(1); +- + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } ++ ++ /* See comments in do_write_oneword() about "two more checks" */ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n", ++ __func__, ++ status & dq5mask, status, datum ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } +- if ((status & dq6) != (oldstatus & dq6)) { +- /* The erasing didn't stop?? */ +- if ((status & dq5) == dq5) { +- /* dq5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); ++ goto op_failed; + } ++ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); +- printk("waiting for erase to complete timed out."); +- DISABLE_VPP(map); +- return -EIO; ++ ++ return ret; ++} ++ ++ ++static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ int ret = 0; ++ int chipnum; ++ unsigned long ofs; ++ ++ *retlen = 0; ++ if (!len) ++ return 0; ++ ++ chipnum = to >> cfi->chipshift; ++ ofs = to - (chipnum << cfi->chipshift); ++ ++ /* If it's not bus-aligned, do the first word write */ ++ if (ofs & (CFIDEV_BUSWIDTH-1)) { ++ size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); ++ if (local_len > len) ++ local_len = len; ++ ret = cfi_amdstd_write_words(mtd, to, local_len, ++ retlen, buf); ++ if (ret) ++ return ret; ++ ofs += local_len; ++ buf += local_len; ++ len -= local_len; ++ ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } ++ } ++ ++ /* Write buffer is worth it only if more than one word to write... */ ++ while (len) { ++ /* We must not cross write block boundaries */ ++ int size = wbufsize - (ofs & (wbufsize-1)); ++ ++ if (size > len) ++ size = len; ++ ret = do_write_buffer(map, &cfi->chips[chipnum], ++ ofs, buf, size); ++ if (ret) ++ return ret; ++ ++ ofs += size; ++ buf += size; ++ (*retlen) += size; ++ len -= size; ++ ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } + } +- DISABLE_VPP(map); +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); + + return 0; + } + +-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) ++ ++/* ++ * Handle devices with one erase region, that only implement ++ * the chip erase command. ++ */ ++static inline int do_erase_chip(struct map_info *map, struct flchip *chip) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; + struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ unsigned long timeo = jiffies + HZ; ++ unsigned long int adr; + DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; ++ int ta = 0; ++ cfi_word datum = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- retry: +- cfi_spin_lock(chip->mutex); +- +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr = cfi->addr_unlock1; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; +- +- goto retry; ++ return ret; + } + +- chip->state = FL_ERASING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, chip->start ); + +- adr += chip->start; + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_write(map, CMD(0x30), adr); +- +- timeo = jiffies + (HZ*20); +- +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. +- */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++ cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; + +- /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((chip->erase_time*HZ)/(2*1000)); + cfi_spin_lock(chip->mutex); + ++ timeo = jiffies + (HZ*20); ++ ++ for (;;) { + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk(KERN_DEBUG "erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) ++ break; + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- +- cfi_udelay(1); +- ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } ++ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); +- } +- if( (status & dq6) != (oldstatus & dq6) ) +- { +- /* The erasing didn't stop?? */ +- if( ( status & dq5 ) == dq5 ) +- { +- /* When DQ5 raises, we must check once again if DQ6 is toggling. +- If not, the erase has been completed OK. If not, reset chip. */ +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) ) +- { +- printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" ); +- } +- else +- { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" ); ++ if ( cfi_buswidth_is_1() ) { ++ datum = (__u8)~0; ++ } else if ( cfi_buswidth_is_2() ) { ++ datum = (__u16)~0; ++ } else if ( cfi_buswidth_is_4() ) { ++ datum = (__u32)~0; ++#ifdef CFI_WORD_64 ++ } else if ( cfi_buswidth_is_8() ) { ++ datum = (__u64)~0; ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ goto op_failed; ++ } ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x\n", ++ __func__, ++ status & dq5mask ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } ++ goto op_failed; + } +- else +- { +- printk( "Waiting for erase to complete timed out in do_erase_oneblock."); + +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- return -EIO; +- } +- } ++ HANDLE_WACKY_STATE(); + +- DISABLE_VPP(map); ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } + +-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++ ++typedef int (*frob_t)(struct map_info *map, struct flchip *chip, ++ unsigned long adr, void *thunk); ++ ++ ++static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, ++ loff_t ofs, size_t len, void *thunk) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; ++ unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + +- if (instr->addr > mtd->size) ++ if (ofs > mtd->size) + return -EINVAL; + +- if ((instr->len + instr->addr) > mtd->size) ++ if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are +@@ -1008,7 +1656,7 @@ + start of the requested erase, and then go back one. + */ + +- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) ++ while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + +@@ -1018,7 +1666,7 @@ + effect here. + */ + +- if (instr->addr & (regions[i].erasesize-1)) ++ if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ +@@ -1028,7 +1676,7 @@ + * with the erase region at that address. + */ + +- while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) ++ while (inumeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which +@@ -1036,17 +1684,16 @@ + */ + i--; + +- if ((instr->addr + instr->len) & (regions[i].erasesize-1)) ++ if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); +- len = instr->len; ++ chipnum = ofs >> cfi->chipshift; ++ adr = ofs - (chipnum << cfi->chipshift); + + i=first; + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); ++ while (len) { ++ ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); + + if (ret) + return ret; +@@ -1066,50 +1713,171 @@ + } + } + +- instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); +- + return 0; + } + +-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) ++ ++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) + { +- struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; +- int chipnum, ret = 0; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ unsigned long timeo = jiffies + HZ; ++ DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; ++ int ta = 0; ++ cfi_word datum = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- if (instr->addr & (mtd->erasesize - 1)) +- return -EINVAL; ++ adr += chip->start; + +- if (instr->len & (mtd->erasesize -1)) +- return -EINVAL; ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- if ((instr->len + instr->addr) > mtd->size) +- return -EINVAL; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, adr ); + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); +- len = instr->len; ++ ENABLE_VPP(map); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_write(map, CMD(0x30), adr); + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; + +- if (ret) +- return ret; ++ cfi_spin_unlock(chip->mutex); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((chip->erase_time*HZ)/(2*1000)); ++ cfi_spin_lock(chip->mutex); + +- adr += mtd->erasesize; +- len -= mtd->erasesize; ++ timeo = jiffies + (HZ*20); + +- if (adr >> cfi->chipshift) { +- adr = 0; +- chipnum++; ++ /* Wait for the end of programing/erasure by using the toggle method. ++ * As long as there is a programming procedure going on, bit 6 is ++ * toggling its state with each consecutive read. The toggling stops ++ * as soon as the procedure is completed. ++ * ++ * If the process has gone on for too long on the chip, bit 5 gets ++ * set. After bit5 is set you can kill the operation by sending a ++ * reset command to the chip. ++ */ ++ /* See comments in do_write_oneword(). */ + +- if (chipnum >= cfi->numchips) ++ for (;;) { ++ if (chip->state != FL_ERASING) { ++ /* Someone's suspended the erase. Sleep */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ cfi_spin_lock(chip->mutex); ++ continue; ++ } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } ++ ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) + break; ++ ++ /* Latency issues. Drop the lock, wait a while and retry */ ++ cfi_spin_unlock(chip->mutex); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); ++ cfi_spin_lock(chip->mutex); + } ++ ++ prev_oldstatus = oldstatus; ++ prev_status = status; ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ if ( cfi_buswidth_is_1() ) { ++ datum = (__u8)~0; ++ } else if ( cfi_buswidth_is_2() ) { ++ datum = (__u16)~0; ++ } else if ( cfi_buswidth_is_4() ) { ++ datum = (__u32)~0; ++#ifdef CFI_WORD_64 ++ } else if ( cfi_buswidth_is_8() ) { ++ datum = (__u64)~0; ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ goto op_failed; ++ } ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x\n", ++ __func__, ++ status & dq5mask ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } ++ goto op_failed; ++ } ++ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++} ++ ++ ++int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ unsigned long ofs, len; ++ int ret; ++ ++ ofs = instr->addr; ++ len = instr->len; ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); ++ if (ret) ++ return ret; + + instr->state = MTD_ERASE_DONE; + if (instr->callback) +@@ -1118,6 +1886,7 @@ + return 0; + } + ++ + static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) + { + struct map_info *map = mtd->priv; +@@ -1141,6 +1910,7 @@ + return 0; + } + ++ + static void cfi_amdstd_sync (struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1254,6 +2024,7 @@ + return ret; + } + ++ + static void cfi_amdstd_resume(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1279,6 +2050,137 @@ + } + } + ++ ++#ifdef DEBUG_LOCK_BITS ++ ++static int do_printlockstatus_oneblock(struct map_info *map, ++ struct flchip *chip, ++ unsigned long adr, ++ void *thunk) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ ++ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); ++ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", ++ adr, cfi_read_query(map, adr+(2*ofs_factor))); ++ cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); ++ ++ return 0; ++} ++ ++ ++#define debug_dump_locks(mtd, frob, ofs, len, thunk) \ ++ cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk)) ++ ++#else ++ ++#define debug_dump_locks(...) ++ ++#endif /* DEBUG_LOCK_BITS */ ++ ++ ++struct xxlock_thunk { ++ cfi_word val; ++ flstate_t state; ++}; ++ ++ ++#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING}) ++#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING}) ++ ++ ++/* ++ * FIXME - this is *very* specific to a particular chip. It likely won't ++ * work for all chips that require unlock. It also hasn't been tested ++ * with interleaved chips. ++ */ ++static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk; ++ int ret; ++ ++ /* ++ * This is easy because these are writes to registers and not writes ++ * to flash memory - that means that we don't have to check status ++ * and timeout. ++ */ ++ ++ adr += chip->start; ++ /* ++ * lock block registers: ++ * - on 64k boundariesand ++ * - bit 1 set high ++ * - block lock registers are 4MiB lower - overflow subtract (danger) ++ */ ++ adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff; ++ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ chip->state = xxlt->state; ++ cfi_write(map, CMD(xxlt->val), adr); ++ ++ /* Done and happy. */ ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); ++ return 0; ++} ++ ++ ++static int cfi_amdstd_lock_varsize(struct mtd_info *mtd, ++ loff_t ofs, ++ size_t len) ++{ ++ int ret; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", ++ __func__, ofs, len); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, ++ (void *)&DO_XXLOCK_ONEBLOCK_LOCK); ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status after, ret=%d\n", ++ __func__, ret); ++ ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ return ret; ++} ++ ++ ++static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd, ++ loff_t ofs, ++ size_t len) ++{ ++ int ret; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", ++ __func__, ofs, len); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, ++ (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK); ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status after, ret=%d\n", ++ __func__, ret); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ return ret; ++} ++ ++ + static void cfi_amdstd_destroy(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1291,17 +2193,20 @@ + + static char im_name[]="cfi_cmdset_0002"; + ++ + int __init cfi_amdstd_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); + return 0; + } + ++ + static void __exit cfi_amdstd_exit(void) + { + inter_module_unregister(im_name); + } + ++ + module_init(cfi_amdstd_init); + module_exit(cfi_amdstd_exit); + +@@ -1309,3 +2214,7 @@ + MODULE_AUTHOR("Crossnet Co. et al."); + MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); + ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++MODULE_PARM(retry_cmd_max, "i"); ++MODULE_PARM_DESC(retry_cmd_max, "Number of times to retry an erase or program command if it fails - should only be needed by buggy hardware: default 0"); ++#endif +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c linux/drivers/mtd/chips/cfi_cmdset_0020.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_cmdset_0020.c 2004-11-19 10:25:11.747222896 +0100 +@@ -21,16 +21,19 @@ + #include + #include + #include ++#include + #include + #include + + #include ++#include + #include + #include + #include ++#include + #include + #include +-#include ++#include + + + static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -51,10 +54,10 @@ + static struct mtd_info *cfi_staa_setup (struct map_info *); + + static struct mtd_chip_driver cfi_staa_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_staa_destroy, +- name: "cfi_cmdset_0020", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_staa_destroy, ++ .name = "cfi_cmdset_0020", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -113,7 +116,6 @@ + { + struct cfi_private *cfi = map->fldrv_priv; + int i; +- __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode) { + /* +@@ -123,35 +125,10 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; +- +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); +- if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); +- return NULL; +- } +- +- /* Read in the Extended Query Table */ +- for (i=0; iMajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { +- printk(KERN_WARNING " Unknown staa Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); ++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); ++ if (!extp) + return NULL; +- } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); +@@ -172,11 +149,6 @@ + cfi->chips[i].erase_time = 1024; + } + +- map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; +- +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_staa_setup(map); + } + +@@ -208,6 +180,7 @@ + if (!mtd->eraseregions) { + printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -232,6 +205,7 @@ + printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + kfree(mtd->eraseregions); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -256,7 +230,7 @@ + mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */ + mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ + map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + mtd->name = map->name; + return mtd; + } +@@ -288,7 +262,7 @@ + */ + switch (chip->state) { + case FL_ERASING: +- if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) ++ if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) + goto sleep; /* We don't support erase suspend */ + + cfi_write (map, CMD(0xb0), cmd_addr); +@@ -374,7 +348,7 @@ + goto retry; + } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + if (suspended) { + chip->state = chip->oldstate; +@@ -540,11 +514,11 @@ + /* Write data */ + for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); ++ map_write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); ++ map_write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); ++ map_write32 (map, *((__u32*)buf)++, adr+z); + } else { + DISABLE_VPP(map); + return -EINVAL; +@@ -1436,13 +1410,13 @@ + + static char im_name[]="cfi_cmdset_0020"; + +-mod_init_t cfi_staa_init(void) ++int __init cfi_staa_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); + return 0; + } + +-mod_exit_t cfi_staa_exit(void) ++static void __exit cfi_staa_exit(void) + { + inter_module_unregister(im_name); + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c linux/drivers/mtd/chips/cfi_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_probe.c 2004-11-19 10:25:11.766220008 +0100 +@@ -1,13 +1,14 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id: cfi_probe.c,v 1.69 2002/05/11 22:13:03 dwmw2 Exp $ ++ $Id: cfi_probe.c,v 1.73 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -25,7 +26,7 @@ + #endif + + static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); + + struct mtd_info *cfi_probe(struct map_info *map); +@@ -48,7 +49,7 @@ + } + + static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++ unsigned long *chip_map, struct cfi_private *cfi) + { + int i; + +@@ -77,18 +78,24 @@ + } + + /* Check each previous chip to see if it's an alias */ +- for (i=0; inumchips; i++) { ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ /* Skip location; no valid chip at this address */ ++ continue; ++ } ++ start = i << cfi->chipshift; + /* This chip should be in read mode if it's one + we've already touched. */ +- if (qry_present(map,chips[i].start,cfi)) { ++ if (qry_present(map, start, cfi)) { + /* Eep. This chip also had the QRY marker. + * Is it an alias for the new one? */ +- cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL); + + /* If the QRY marker goes away, it's an alias */ +- if (!qry_present(map, chips[i].start, cfi)) { ++ if (!qry_present(map, start, cfi)) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + /* Yes, it's actually got QRY for data. Most +@@ -99,7 +106,7 @@ + + if (qry_present(map, base, cfi)) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -107,13 +114,7 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + /* Put it back into Read Mode */ +@@ -179,9 +180,28 @@ + (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1); + #endif + } ++ ++ /* Note we put the device back into Read Mode BEFORE going into Auto ++ * Select Mode, as some devices support nesting of modes, others ++ * don't. This way should always work. ++ * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and ++ * so should be treated as nops or illegal (and so put the device ++ * back into Read Mode, which is a nop in this case). ++ */ ++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi->mfr = cfi_read_query(map, base); ++ cfi->id = cfi_read_query(map, base + ofs_factor); ++ + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + ++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", ++ map->name, cfi->interleave, cfi->device_type*8, base, ++ map->buswidth*8); ++ + return 1; + } + +@@ -240,11 +260,11 @@ + printk("No Alternate Algorithm Table\n"); + + +- printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); +- printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); ++ printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); ++ printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + if (cfip->VppMin) { +- printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); +- printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); ++ printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); ++ printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + } + else + printk("No Vpp line\n"); +@@ -303,8 +323,8 @@ + #endif /* DEBUG_CFI */ + + static struct chip_probe cfi_chip_probe = { +- name: "CFI", +- probe_chip: cfi_probe_chip ++ .name = "CFI", ++ .probe_chip = cfi_probe_chip + }; + + struct mtd_info *cfi_probe(struct map_info *map) +@@ -317,9 +337,9 @@ + } + + static struct mtd_chip_driver cfi_chipdrv = { +- probe: cfi_probe, +- name: "cfi_probe", +- module: THIS_MODULE ++ .probe = cfi_probe, ++ .name = "cfi_probe", ++ .module = THIS_MODULE + }; + + int __init cfi_probe_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c linux/drivers/mtd/chips/cfi_util.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_util.c 2004-11-19 10:25:11.767219856 +0100 +@@ -0,0 +1,91 @@ ++/* ++ * Common Flash Interface support: ++ * Generic utility functions not dependant on command set ++ * ++ * Copyright (C) 2002 Red Hat ++ * Copyright (C) 2003 STMicroelectronics Limited ++ * ++ * This code is covered by the GPL. ++ * ++ * $Id: cfi_util.c,v 1.3 2003/11/14 19:50:03 thayne Exp $ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct cfi_extquery * ++cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ __u32 base = 0; // cfi->chips[0].start; ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ int i; ++ struct cfi_extquery *extp = NULL; ++ ++ printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); ++ if (!adr) ++ goto out; ++ ++ /* Switch it into Query Mode */ ++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ ++ extp = kmalloc(size, GFP_KERNEL); ++ if (!extp) { ++ printk(KERN_ERR "Failed to allocate memory\n"); ++ goto out; ++ } ++ ++ /* Read in the Extended Query Table */ ++ for (i=0; iMajorVersion != '1' || ++ (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { ++ printk(KERN_WARNING " Unknown %s Extended Query " ++ "version %c.%c.\n", name, extp->MajorVersion, ++ extp->MinorVersion); ++ kfree(extp); ++ extp = NULL; ++ goto out; ++ } ++ ++out: ++ /* Make sure it's in read mode */ ++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ return extp; ++} ++ ++EXPORT_SYMBOL(cfi_read_pri); ++ ++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_fixup *f; ++ ++ for (f=fixups; f->fixup; f++) { ++ if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && ++ ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { ++ f->fixup(map, f->param); ++ } ++ } ++} ++ ++EXPORT_SYMBOL(cfi_fixup); ++ ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/chipreg.c linux/drivers/mtd/chips/chipreg.c +--- linux-mips-2.4.27/drivers/mtd/chips/chipreg.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/chipreg.c 2004-11-19 10:25:11.769219552 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: chipreg.c,v 1.13 2002/02/21 08:26:58 dwmw2 Exp $ ++ * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $ + * + * Registration for chip drivers + * +@@ -7,10 +7,13 @@ + + #include + #include ++#include + #include + #include +-#include ++#include + #include ++#include ++#include + + spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; + static LIST_HEAD(chip_drvs_list); +@@ -44,10 +47,8 @@ + break; + } + } +- if (ret && !try_inc_mod_count(ret->module)) { +- /* Eep. Failed. */ ++ if (ret && !try_module_get(ret->module)) + ret = NULL; +- } + + spin_unlock(&chip_drvs_lock); + +@@ -64,32 +65,46 @@ + + drv = get_mtd_chip_driver(name); + +- if (!drv && !request_module(name)) ++ if (!drv && !request_module("%s", name)) + drv = get_mtd_chip_driver(name); + + if (!drv) + return NULL; + + ret = drv->probe(map); +-#ifdef CONFIG_MODULES ++ + /* We decrease the use count here. It may have been a + probe-only module, which is no longer required from this + point, having given us a handle on (and increased the use + count of) the actual driver code. + */ +- if(drv->module) +- __MOD_DEC_USE_COUNT(drv->module); +-#endif ++ module_put(drv->module); + + if (ret) + return ret; + + return NULL; + } ++/* ++ * Destroy an MTD device which was created for a map device. ++ * Make sure the MTD device is already unregistered before calling this ++ */ ++void map_destroy(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ ++ if (map->fldrv->destroy) ++ map->fldrv->destroy(mtd); ++ ++ module_put(map->fldrv->module); ++ ++ kfree(mtd); ++} + + EXPORT_SYMBOL(register_mtd_chip_driver); + EXPORT_SYMBOL(unregister_mtd_chip_driver); + EXPORT_SYMBOL(do_map_probe); ++EXPORT_SYMBOL(map_destroy); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("David Woodhouse "); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c linux/drivers/mtd/chips/gen_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c 2003-08-13 19:19:18.000000000 +0200 ++++ linux/drivers/mtd/chips/gen_probe.c 2004-11-19 10:25:11.770219400 +0100 +@@ -1,14 +1,17 @@ + /* + * Routines common to all CFI-type probes. +- * (C) 2001, 2001 Red Hat, Inc. ++ * (C) 2001-2003 Red Hat, Inc. + * GPL'd +- * $Id: gen_probe.c,v 1.9 2002/09/05 05:15:32 acurtis Exp $ ++ * $Id: gen_probe.c,v 1.14 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #include ++#include ++#include + #include + #include + #include ++#include + #include + + static struct mtd_info *check_cmd_set(struct map_info *, int); +@@ -50,11 +53,11 @@ + + struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) + { +- unsigned long base=0; + struct cfi_private cfi; + struct cfi_private *retcfi; +- struct flchip chip[MAX_CFI_CHIPS]; +- int i; ++ unsigned long *chip_map; ++ int i, j; ++ int max_chips; + + memset(&cfi, 0, sizeof(cfi)); + +@@ -77,8 +80,6 @@ + return NULL; + } + #endif +- chip[0].start = 0; +- chip[0].state = FL_READY; + cfi.chipshift = cfi.cfiq->DevSize; + + switch(cfi.interleave) { +@@ -103,20 +104,28 @@ + cfi.numchips = 1; + + /* ++ * Allocate memory for bitmap of valid chips. ++ * Align bitmap storage size to full byte. ++ */ ++ max_chips = map->size >> cfi.chipshift; ++ chip_map = kmalloc((max_chips / 8) + ((max_chips % 8) ? 1 : 0), GFP_KERNEL); ++ if (!chip_map) { ++ printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); ++ kfree(cfi.cfiq); ++ return NULL; ++ } ++ ++ set_bit(0, chip_map); /* Mark first chip valid */ ++ ++ /* + * Now probe for other chips, checking sensibly for aliases while + * we're at it. The new_chip probe above should have let the first + * chip in read mode. +- * +- * NOTE: Here, we're checking if there is room for another chip +- * the same size within the mapping. Therefore, +- * base + chipsize <= map->size is the correct thing to do, +- * because, base + chipsize would be the _first_ byte of the +- * next chip, not the one we're currently pondering. + */ + +- for (base = (1<size; +- base += (1<probe_chip(map, base, &chip[0], &cfi); ++ for (i = 1; i < max_chips; i++) { ++ cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); ++ } + + /* + * Now allocate the space for the structures we need to return to +@@ -128,19 +137,26 @@ + if (!retcfi) { + printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); + kfree(cfi.cfiq); ++ kfree(chip_map); + return NULL; + } + + memcpy(retcfi, &cfi, sizeof(cfi)); +- memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); ++ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); + +- /* Fix up the stuff that breaks when you move it */ +- for (i=0; i< retcfi->numchips; i++) { +- init_waitqueue_head(&retcfi->chips[i].wq); +- spin_lock_init(&retcfi->chips[i]._spinlock); +- retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock; ++ for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { ++ if(test_bit(i, chip_map)) { ++ struct flchip *pchip = &retcfi->chips[j++]; ++ ++ pchip->start = (i << cfi.chipshift); ++ pchip->state = FL_READY; ++ init_waitqueue_head(&pchip->wq); ++ spin_lock_init(&pchip->_spinlock); ++ pchip->mutex = &pchip->_spinlock; ++ } + } + ++ kfree(chip_map); + return retcfi; + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec.c linux/drivers/mtd/chips/jedec.c +--- linux-mips-2.4.27/drivers/mtd/chips/jedec.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/jedec.c 2004-11-19 10:25:11.772219096 +0100 +@@ -11,10 +11,16 @@ + * not going to guess how to send commands to them, plus I expect they will + * all speak CFI.. + * +- * $Id: jedec.c,v 1.14 2002/06/27 02:19:12 dwmw2 Exp $ ++ * $Id: jedec.c,v 1.19 2003/05/29 09:25:23 dwmw2 Exp $ + */ + ++#include ++#include ++#include + #include ++#include ++#include ++#include + + static struct mtd_info *jedec_probe(struct map_info *); + static int jedec_probe8(struct map_info *map,unsigned long base, +@@ -33,14 +39,51 @@ + + /* Listing of parts and sizes. We need this table to learn the sector + size of the chip and the total length */ +-static const struct JEDECTable JEDEC_table[] = +- {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {}}; ++static const struct JEDECTable JEDEC_table[] = { ++ { ++ .jedec = 0x013D, ++ .name = "AMD Am29F017D", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01AD, ++ .name = "AMD Am29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01D5, ++ .name = "AMD Am29F080", ++ .size = 1*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01A4, ++ .name = "AMD Am29F040", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x20E3, ++ .name = "AMD Am29W040B", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0xC2AD, ++ .name = "Macronix MX29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { .jedec = 0x0 } ++}; + + static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id); + static void jedec_sync(struct mtd_info *mtd) {}; +@@ -54,9 +97,9 @@ + + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec", ++ .module = THIS_MODULE + }; + + /* Probe entry point */ +@@ -131,8 +174,7 @@ + /* Generate a part name that includes the number of different chips and + other configuration information */ + count = 1; +- strncpy(Part,map->name,sizeof(Part)-10); +- Part[sizeof(Part)-11] = 0; ++ strlcpy(Part,map->name,sizeof(Part)-10); + strcat(Part," "); + Uniq = 0; + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) +@@ -209,8 +251,7 @@ + // printk("Part: '%s'\n",Part); + + memset(MTD,0,sizeof(*MTD)); +- // strncpy(MTD->name,Part,sizeof(MTD->name)); +- // MTD->name[sizeof(MTD->name)-1] = 0; ++ // strlcpy(MTD->name,Part,sizeof(MTD->name)); + MTD->name = map->name; + MTD->type = MTD_NORFLASH; + MTD->flags = MTD_CAP_NORFLASH; +@@ -229,7 +270,7 @@ + MTD->priv = map; + map->fldrv_priv = priv; + map->fldrv = &jedec_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return MTD; + } + +@@ -351,8 +392,8 @@ + static int jedec_probe8(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read8(map,base+x) +- #define flwrite(v,x) map->write8(map,v,base+x) ++ #define flread(x) map_read8(map,base+x) ++ #define flwrite(v,x) map_write8(map,v,base+x) + + const unsigned long AutoSel1 = 0xAA; + const unsigned long AutoSel2 = 0x55; +@@ -411,8 +452,8 @@ + static int jedec_probe32(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read32(map,base+((x)<<2)) +- #define flwrite(v,x) map->write32(map,v,base+((x)<<2)) ++ #define flread(x) map_read32(map,base+((x)<<2)) ++ #define flwrite(v,x) map_write32(map,v,base+((x)<<2)) + + const unsigned long AutoSel1 = 0xAAAAAAAA; + const unsigned long AutoSel2 = 0x55555555; +@@ -490,7 +531,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -514,7 +555,7 @@ + get = priv->bank_fill[0] - offset; + + bank /= priv->bank_fill[0]; +- map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); ++ map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); + + len -= get; + *retlen += get; +@@ -545,8 +586,8 @@ + static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) + { + // Does IO to the currently selected chip +- #define flread(x) map->read8(map,chip->base+((x)<addrshift)) +- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<addrshift)) ++ #define flread(x) map_read8(map,chip->base+((x)<addrshift)) ++ #define flwrite(v,x) map_write8(map,v,chip->base+((x)<addrshift)) + + unsigned long Time = 0; + unsigned long NoTime = 0; +@@ -608,7 +649,7 @@ + + /* Poll the flash for erasure completion, specs say this can take as long + as 480 seconds to do all the sectors (for a 2 meg flash). +- Erasure time is dependant on chip age, temp and wear.. */ ++ Erasure time is dependent on chip age, temp and wear.. */ + + /* This being a generic routine assumes a 32 bit bus. It does read32s + and bundles interleved chips into the same grouping. This will work +@@ -651,19 +692,19 @@ + or this is not really flash ;> */ + switch (map->buswidth) { + case 1: +- Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 3: +- Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count = 3; +@@ -699,13 +740,13 @@ + + switch (map->buswidth) { + case 1: +- Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 4: +- Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count++; +@@ -755,10 +796,10 @@ + size_t *retlen, const u_char *buf) + { + /* Does IO to the currently selected chip. It takes the bank addressing +- base (which is divisable by the chip size) adds the necesary lower bits +- of addrshift (interleve index) and then adds the control register index. */ +- #define flread(x) map->read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) +- #define flwrite(v,x) map->write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) ++ base (which is divisible by the chip size) adds the necessary lower bits ++ of addrshift (interleave index) and then adds the control register index. */ ++ #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) ++ #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) + + struct map_info *map = (struct map_info *)mtd->priv; + struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; +@@ -794,7 +835,7 @@ + // Loop over this page + for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) + { +- unsigned char oldbyte = map->read8(map,base+off); ++ unsigned char oldbyte = map_read8(map,base+off); + unsigned char Last[4]; + unsigned long Count = 0; + +@@ -809,10 +850,10 @@ + flwrite(0xAA,0x555); + flwrite(0x55,0x2AA); + flwrite(0xA0,0x555); +- map->write8(map,*buf,base + off); +- Last[0] = map->read8(map,base + off); +- Last[1] = map->read8(map,base + off); +- Last[2] = map->read8(map,base + off); ++ map_write8(map,*buf,base + off); ++ Last[0] = map_read8(map,base + off); ++ Last[1] = map_read8(map,base + off); ++ Last[2] = map_read8(map,base + off); + + /* Wait for the flash to finish the operation. We store the last 4 + status bytes that have been retrieved so we can determine why +@@ -820,7 +861,7 @@ + failure */ + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && + Count < 10000; Count++) +- Last[Count % 4] = map->read8(map,base + off); ++ Last[Count % 4] = map_read8(map,base + off); + if (Last[(Count - 1) % 4] != *buf) + { + jedec_flash_failed(Last[(Count - 3) % 4]); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/jedec_probe.c 2004-11-19 10:25:11.774218792 +0100 +@@ -1,9 +1,11 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id: jedec_probe.c,v 1.19 2002/11/12 13:12:10 dwmw2 Exp $ ++ $Id: jedec_probe.c,v 1.44 2003/11/17 15:57:35 thayne Exp $ + See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) + for the standard this probe goes back to. ++ ++ Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + */ + + #include +@@ -15,7 +17,9 @@ + #include + #include + #include ++#include + ++#include + #include + #include + #include +@@ -26,20 +30,24 @@ + #define MANUFACTURER_FUJITSU 0x0004 + #define MANUFACTURER_INTEL 0x0089 + #define MANUFACTURER_MACRONIX 0x00C2 +-#define MANUFACTURER_ST 0x0020 ++#define MANUFACTURER_PMC 0x009D + #define MANUFACTURER_SST 0x00BF ++#define MANUFACTURER_ST 0x0020 + #define MANUFACTURER_TOSHIBA 0x0098 ++#define MANUFACTURER_WINBOND 0x00da + + + /* AMD */ + #define AM29F800BB 0x2258 + #define AM29F800BT 0x22D6 ++#define AM29LV400BB 0x22BA ++#define AM29LV400BT 0x22B9 + #define AM29LV800BB 0x225B + #define AM29LV800BT 0x22DA + #define AM29LV160DT 0x22C4 + #define AM29LV160DB 0x2249 + #define AM29F017D 0x003D +-#define AM29F016 0x00AD ++#define AM29F016D 0x00AD + #define AM29F080 0x00D5 + #define AM29F040 0x00A4 + #define AM29LV040B 0x004F +@@ -54,6 +62,7 @@ + #define AT49BV32XT 0x00C9 + + /* Fujitsu */ ++#define MBM29F040C 0x00A4 + #define MBM29LV650UE 0x22D7 + #define MBM29LV320TE 0x22F6 + #define MBM29LV320BE 0x22F9 +@@ -61,6 +70,9 @@ + #define MBM29LV160BE 0x2249 + #define MBM29LV800BA 0x225B + #define MBM29LV800TA 0x22DA ++#define MBM29LV400TC 0x22B9 ++#define MBM29LV400BC 0x22BA ++ + + /* Intel */ + #define I28F004B3T 0x00d4 +@@ -93,8 +105,14 @@ + #define MX29F004T 0x0045 + #define MX29F004B 0x0046 + ++/* PMC */ ++#define PM49FL002 0x006D ++#define PM49FL004 0x006E ++#define PM49FL008 0x006A ++ + /* ST - www.st.com */ +-#define M29W800T 0x00D7 ++#define M29W800DT 0x00D7 ++#define M29W800DB 0x005B + #define M29W160DT 0x22C4 + #define M29W160DB 0x2249 + #define M29W040B 0x00E3 +@@ -110,6 +128,7 @@ + #define SST39LF040 0x00D7 + #define SST39SF010A 0x00B5 + #define SST39SF020A 0x00B6 ++#define SST49LF004B 0x0060 + #define SST49LF030A 0x001C + #define SST49LF040A 0x0051 + #define SST49LF080A 0x005B +@@ -122,15 +141,87 @@ + #define TC58FVT641 0x0093 + #define TC58FVB641 0x0095 + ++/* Winbond */ ++#define W49V002A 0x00b0 ++ ++ ++/* ++ * Unlock address sets for AMD command sets. ++ * Intel command sets use the MTD_UADDR_UNNECESSARY. ++ * Each identifier, except MTD_UADDR_UNNECESSARY, and ++ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[]. ++ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure ++ * initialization need not require initializing all of the ++ * unlock addresses for all bit widths. ++ */ ++enum uaddr { ++ MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */ ++ MTD_UADDR_0x0555_0x02AA, ++ MTD_UADDR_0x0555_0x0AAA, ++ MTD_UADDR_0x5555_0x2AAA, ++ MTD_UADDR_0x0AAA_0x0555, ++ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ ++ MTD_UADDR_UNNECESSARY, /* Does not require any address */ ++}; ++ ++ ++struct unlock_addr { ++ int addr1; ++ int addr2; ++}; ++ ++ ++/* ++ * I don't like the fact that the first entry in unlock_addrs[] ++ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore, ++ * should not be used. The problem is that structures with ++ * initializers have extra fields initialized to 0. It is _very_ ++ * desireable to have the unlock address entries for unsupported ++ * data widths automatically initialized - that means that ++ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here ++ * must go unused. ++ */ ++static const struct unlock_addr unlock_addrs[] = { ++ [MTD_UADDR_NOT_SUPPORTED] = { ++ .addr1 = 0xffff, ++ .addr2 = 0xffff ++ }, ++ ++ [MTD_UADDR_0x0555_0x02AA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x02aa ++ }, ++ ++ [MTD_UADDR_0x0555_0x0AAA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x0aaa ++ }, ++ ++ [MTD_UADDR_0x5555_0x2AAA] = { ++ .addr1 = 0x5555, ++ .addr2 = 0x2aaa ++ }, ++ ++ [MTD_UADDR_0x0AAA_0x0555] = { ++ .addr1 = 0x0AAA, ++ .addr2 = 0x0555 ++ }, ++ ++ [MTD_UADDR_DONT_CARE] = { ++ .addr1 = 0x0000, /* Doesn't matter which address */ ++ .addr2 = 0x0000 /* is used - must be last entry */ ++ } ++}; ++ + + struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const int DevSize; +- const int InterfaceDesc; + const int NumEraseRegions; + const int CmdSet; ++ const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ + const ulong regions[4]; + }; + +@@ -145,760 +236,1214 @@ + #define SIZE_4MiB 22 + #define SIZE_8MiB 23 + ++ ++/* ++ * Please keep this list ordered by manufacturer! ++ * Fortunately, the list isn't searched often and so a ++ * slow, linear search isn't so bad. ++ */ + static const struct amd_flash_info jedec_table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F032B, +- name: "AMD AM29F032B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,64) +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F032B, ++ .name = "AMD AM29F032B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,64) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BB, ++ .name = "AMD AM29LV400BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BT, ++ .name = "AMD AM29LV400BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ ERASEINFO(0x10000,15), + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB321, +- name: "Toshiba TC58FVB321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,15), + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT321, +- name: "Toshiba TC58FVT321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB641, +- name: "Toshiba TC58FVB641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,127) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT641, +- name: "Toshiba TC58FVT641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,127), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F017D, ++ .name = "AMD AM29F017D", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F016D, ++ .name = "AMD AM29F016D", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F080, ++ .name = "AMD AM29F080", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F040, ++ .name = "AMD AM29F040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV040B, ++ .name = "AMD AM29LV040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV512, ++ .name = "Atmel AT49BV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT29LV512, ++ .name = "Atmel AT29LV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x80,256), ++ ERASEINFO(0x80,256) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16X, ++ .name = "Atmel AT49BV16X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV650UE, +- name: "Fujitsu MBM29LV650UE", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,128) +- } +- }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320TE, +- name: "Fujitsu MBM29LV320TE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16XT, ++ .name = "Atmel AT49BV16XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320BE, +- name: "Fujitsu MBM29LV320BE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32X, ++ .name = "Atmel AT49BV32X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32XT, ++ .name = "Atmel AT49BV32XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29F040C, ++ .name = "Fujitsu MBM29F040C", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV650UE, ++ .name = "Fujitsu MBM29LV650UE", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,128) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320TE, ++ .name = "Fujitsu MBM29LV320TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BA, +- name: "Fujitsu MBM29LV800BA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15) ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320BE, ++ .name = "Fujitsu MBM29LV320BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800TA, +- name: "Fujitsu MBM29LV800TA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BA, ++ .name = "Fujitsu MBM29LV800BA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), ++ ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800TA, ++ .name = "Fujitsu MBM29LV800TA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), +- ERASEINFO(0x08000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400BC, ++ .name = "Fujitsu MBM29LV400BC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400TC, ++ .name = "Fujitsu MBM29LV400TC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3B, +- name: "Intel 28F004B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3B, ++ .name = "Intel 28F004B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3T, +- name: "Intel 28F004B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3T, ++ .name = "Intel 28F004B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3B, +- name: "Intel 28F400B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3B, ++ .name = "Intel 28F400B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3T, +- name: "Intel 28F400B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3T, ++ .name = "Intel 28F400B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3B, +- name: "Intel 28F008B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3B, ++ .name = "Intel 28F008B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3T, +- name: "Intel 28F008B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3T, ++ .name = "Intel 28F008B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008S5, +- name: "Intel 28F008S5", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S5, +- name: "Intel 28F016S5", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008SA, +- name: "Intel 28F008SA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008S5, ++ .name = "Intel 28F008S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S5, ++ .name = "Intel 28F016S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008SA, ++ .name = "Intel 28F008SA", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 16), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3B, +- name: "Intel 28F800B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3B, ++ .name = "Intel 28F800B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3T, +- name: "Intel 28F800B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3T, ++ .name = "Intel 28F800B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3B, +- name: "Intel 28F016B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3B, ++ .name = "Intel 28F016B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S3, +- name: "Intel I28F016S3", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S3, ++ .name = "Intel I28F016S3", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 32), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3T, +- name: "Intel 28F016B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3T, ++ .name = "Intel 28F016B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3B, +- name: "Intel 28F160B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3B, ++ .name = "Intel 28F160B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3T, +- name: "Intel 28F160B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3T, ++ .name = "Intel 28F160B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3B, +- name: "Intel 28F320B3B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3B, ++ .name = "Intel 28F320B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 63), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3T, +- name: "Intel 28F320B3T", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3T, ++ .name = "Intel 28F320B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 63), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3B, +- name: "Intel 28F640B3B", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3B, ++ .name = "Intel 28F640B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 127), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3T, +- name: "Intel 28F640B3T", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3T, ++ .name = "Intel 28F640B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AB, +- name: "Intel 82802AB", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AC, +- name: "Intel 82802AC", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AB, ++ .name = "Intel 82802AB", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AC, ++ .name = "Intel 82802AC", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160T, ++ .name = "MXIC MX29LV160T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160B, ++ .name = "MXIC MX29LV160B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV512, +- name: "Atmel AT49BV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,1) +- } +- }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT29LV512, +- name: "Atmel AT29LV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: { +- ERASEINFO(0x80,256), +- ERASEINFO(0x80,256) +- } +- }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16X, +- name: "Atmel AT49BV16X", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,31) ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F016, ++ .name = "Macronix MX29F016", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004T, ++ .name = "Macronix MX29F004T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16XT, +- name: "Atmel AT49BV16XT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004B, ++ .name = "Macronix MX29F004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32X, +- name: "Atmel AT49BV32X", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL002, ++ .name = "PMC Pm49FL002", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 64 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL004, ++ .name = "PMC Pm49FL004", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 128 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL008, ++ .name = "PMC Pm49FL008", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 256 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF512, ++ .name = "SST 39LF512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF010, ++ .name = "SST 39LF010", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF020, ++ .name = "SST 39LF020", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF040, ++ .name = "SST 39LF040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF010A, ++ .name = "SST 39SF010A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF020A, ++ .name = "SST 39SF020A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF004B, ++ .name = "SST 49LF004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF030A, ++ .name = "SST 49LF030A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,96), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF040A, ++ .name = "SST 49LF040A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF080A, ++ .name = "SST 49LF080A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,256), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DT, ++ .name = "ST M29W800DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32XT, +- name: "Atmel AT49BV32XT", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DB, ++ .name = "ST M29W800DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F017D, +- name: "AMD AM29F017D", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F016, +- name: "AMD AM29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F080, +- name: "AMD AM29F080", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F040, +- name: "AMD AM29F040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV040B, +- name: "AMD AM29LV040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W040B, +- name: "ST M29W040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160T, +- name: "MXIC MX29LV160T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160B, +- name: "MXIC MX29LV160B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F016, +- name: "Macronix MX29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004T, +- name: "Macronix MX29F004T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,7), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W040B, ++ .name = "ST M29W040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004B, +- name: "Macronix MX29F004B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,7), ++ ERASEINFO(0x10000,31) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB321, ++ .name = "Toshiba TC58FVB321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT321, ++ .name = "Toshiba TC58FVT321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB641, ++ .name = "Toshiba TC58FVB641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,127) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT641, ++ .name = "Toshiba TC58FVT641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,127), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF512, +- name: "SST 39LF512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,16), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF010, +- name: "SST 39LF010", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF020, +- name: "SST 39LF020", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF040, +- name: "SST 39LF040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF010A, +- name: "SST 39SF010A", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF020A, +- name: "SST 39SF020A", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF030A, +- name: "SST 49LF030A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,96), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF040A, +- name: "SST 49LF040A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF080A, +- name: "SST 49LF080A", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,256), ++ .mfr_id = MANUFACTURER_WINBOND, ++ .dev_id = W49V002A, ++ .name = "Winbond W49V002A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000, 3), ++ ERASEINFO(0x08000, 1), ++ ERASEINFO(0x02000, 2), ++ ERASEINFO(0x04000, 1), + } + } + }; +@@ -907,7 +1452,7 @@ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); + + static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + + struct mtd_info *jedec_probe(struct map_info *map); + +@@ -944,11 +1489,43 @@ + * this should be safe. + */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ /* FIXME - should have reset delay before continuing */ ++} + ++ ++static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type) ++{ ++ int uaddr_idx; ++ __u8 uaddr = MTD_UADDR_NOT_SUPPORTED; ++ ++ switch ( device_type ) { ++ case CFI_DEVICETYPE_X8: uaddr_idx = 0; break; ++ case CFI_DEVICETYPE_X16: uaddr_idx = 1; break; ++ case CFI_DEVICETYPE_X32: uaddr_idx = 2; break; ++ default: ++ printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n", ++ __func__, device_type); ++ goto uaddr_done; ++ } ++ ++ uaddr = finfo->uaddr[uaddr_idx]; ++ ++ if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { ++ /* ASSERT("The unlock addresses for non-8-bit mode ++ are bollocks. We don't really need an array."); */ ++ uaddr = finfo->uaddr[0]; ++ } ++ ++ uaddr_done: ++ return uaddr; + } ++ ++ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) + { + int i,num_erase_regions; ++ unsigned long mask; ++ __u8 uaddr; + + printk("Found: %s\n",jedec_table[index].name); + +@@ -971,41 +1548,170 @@ + p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; + } + p_cfi->cmdset_priv = 0; ++ ++ /* This may be redundant for some cases, but it doesn't hurt */ ++ p_cfi->mfr = jedec_table[index].mfr_id; ++ p_cfi->id = jedec_table[index].dev_id; ++ ++ uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ kfree( p_cfi->cfiq ); ++ return 0; ++ } ++ ++ /* Mask out address bits which are smaller than the device type */ ++ mask = ~(p_cfi->device_type-1); ++ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask; ++ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask; ++ + return 1; /* ok */ + } + +-static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++ ++/* ++ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing ++ * the mapped address, unlock addresses, and proper chip ID. This function ++ * attempts to minimize errors. It is doubtfull that this probe will ever ++ * be perfect - consequently there should be some module parameters that ++ * could be manually specified to force the chip info. ++ */ ++static inline int jedec_match( __u32 base, ++ struct map_info *map, ++ struct cfi_private *cfi, ++ const struct amd_flash_info *finfo ) + { +- int i; +- int unlockpass = 0; ++ int rc = 0; /* failure until all tests pass */ ++ u32 mfr, id; ++ __u8 uaddr; ++ unsigned long mask; + +- if (!cfi->numchips) { ++ /* ++ * The IDs must match. For X16 and X32 devices operating in ++ * a lower width ( X8 or X16 ), the device ID's are usually just ++ * the lower byte(s) of the larger device ID for wider mode. If ++ * a part is found that doesn't fit this assumption (device id for ++ * smaller width mode is completely unrealated to full-width mode) ++ * then the jedec_table[] will have to be augmented with the IDs ++ * for different widths. ++ */ + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: +- cfi->addr_unlock1 = 0x555; +- cfi->addr_unlock2 = 0x2aa; ++ mfr = (__u8)finfo->mfr_id; ++ id = (__u8)finfo->dev_id; + break; + case CFI_DEVICETYPE_X16: +- cfi->addr_unlock1 = 0xaaa; +- if (map->buswidth == cfi->interleave) { +- /* X16 chip(s) in X8 mode */ +- cfi->addr_unlock2 = 0x555; +- } else { +- cfi->addr_unlock2 = 0x554; +- } ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u16)finfo->dev_id; + break; + case CFI_DEVICETYPE_X32: +- cfi->addr_unlock1 = 0x1555; +- cfi->addr_unlock2 = 0xaaa; ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u32)finfo->dev_id; + break; + default: +- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type); +- return 0; ++ printk(KERN_WARNING ++ "MTD %s(): Unsupported device type %d\n", ++ __func__, cfi->device_type); ++ goto match_done; ++ } ++ if ( cfi->mfr != mfr || cfi->id != id ) { ++ goto match_done; ++ } ++ ++ /* the part size must fit in the memory window */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", ++ __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); ++ if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", ++ __func__, finfo->mfr_id, finfo->dev_id, ++ 1 << finfo->DevSize ); ++ goto match_done; ++ } ++ ++ uaddr = finfo_uaddr(finfo, cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ goto match_done; ++ } ++ ++ mask = ~(cfi->device_type-1); ++ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", ++ __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); ++ if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr ++ && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 || ++ (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x did not match\n", ++ __func__, ++ unlock_addrs[uaddr].addr1 & mask, ++ unlock_addrs[uaddr].addr2 & mask); ++ goto match_done; + } ++ ++ /* ++ * Make sure the ID's dissappear when the device is taken out of ++ * ID mode. The only time this should fail when it should succeed ++ * is when the ID's are written as data to the same ++ * addresses. For this rare and unfortunate case the chip ++ * cannot be probed correctly. ++ * FIXME - write a driver that takes all of the chip info as ++ * module parameters, doesn't probe but forces a load. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): check ID's disappear when not in ID mode\n", ++ __func__ ); ++ jedec_reset( base, map, cfi ); ++ mfr = jedec_read_mfr( map, base, cfi ); ++ id = jedec_read_id( map, base, cfi ); ++ if ( mfr == cfi->mfr && id == cfi->id ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n" ++ "You might need to manually specify JEDEC parameters.\n", ++ __func__, cfi->mfr, cfi->id ); ++ goto match_done; ++ } ++ ++ /* all tests passed - mark as success */ ++ rc = 1; ++ ++ /* ++ * Put the device back in ID mode - only need to do this if we ++ * were truly frobbing a real device. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); ++ if(cfi->addr_unlock1) { ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } ++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ /* FIXME - should have a delay before continuing */ ++ ++ match_done: ++ return rc; ++} ++ ++ ++static int jedec_probe_chip(struct map_info *map, __u32 base, ++ unsigned long *chip_map, struct cfi_private *cfi) ++{ ++ int i; ++ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; + + retry: ++ if (!cfi->numchips) { ++ unsigned long mask = ~(cfi->device_type-1); ++ ++ uaddr_idx++; ++ ++ if (MTD_UADDR_UNNECESSARY == uaddr_idx) ++ return 0; ++ ++ /* Mask out address bits which are smaller than the device type */ ++ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask; ++ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask; ++ } ++ + /* Make certain we aren't probing past the end of map */ + if (base >= map->size) { + printk(KERN_NOTICE +@@ -1038,6 +1744,7 @@ + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ /* FIXME - should have a delay before continuing */ + + if (!cfi->numchips) { + /* This is the first time we're called. Set up the CFI +@@ -1045,26 +1752,21 @@ + + cfi->mfr = jedec_read_mfr(map, base, cfi); + cfi->id = jedec_read_id(map, base, cfi); +- printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "Search for id:(%02x %02x) interleave(%d) type(%d)\n", + cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); + for (i=0; imfr == jedec_table[i].mfr_id && +- cfi->id == jedec_table[i].dev_id) { ++ if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n", ++ __func__, cfi->mfr, cfi->id, ++ cfi->addr_unlock1, cfi->addr_unlock2 ); + if (!cfi_jedec_setup(cfi, i)) + return 0; + goto ok_out; + } + } +- switch(unlockpass++) { +- case 0: +- cfi->addr_unlock1 |= cfi->addr_unlock1 << 4; +- cfi->addr_unlock2 |= cfi->addr_unlock2 << 4; +- goto retry; +- case 1: +- cfi->addr_unlock1 = cfi->addr_unlock2 = 0; + goto retry; +- } +- return 0; + } else { + __u16 mfr; + __u16 id; +@@ -1081,21 +1783,24 @@ + } + } + +- /* Check each previous chip to see if it's an alias */ +- for (i=0; inumchips; i++) { +- /* This chip should be in read mode if it's one +- we've already touched. */ +- if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && +- jedec_read_id(map, chips[i].start, cfi) == cfi->id) { ++ /* Check each previous chip locations to see if it's an alias */ ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ continue; /* Skip location; no valid chip at this address */ ++ } ++ start = i << cfi->chipshift; ++ if (jedec_read_mfr(map, start, cfi) == cfi->mfr && ++ jedec_read_id(map, start, cfi) == cfi->id) { + /* Eep. This chip also looks like it's in autoselect mode. + Is it an alias for the new one? */ +- jedec_reset(chips[i].start, map, cfi); ++ jedec_reset(start, map, cfi); + + /* If the device IDs go away, it's an alias */ + if (jedec_read_mfr(map, base, cfi) != cfi->mfr || + jedec_read_id(map, base, cfi) != cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + +@@ -1107,7 +1812,7 @@ + if (jedec_read_mfr(map, base, cfi) == cfi->mfr && + jedec_read_id(map, base, cfi) == cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -1115,13 +1820,7 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + ok_out: +@@ -1136,8 +1835,8 @@ + } + + static struct chip_probe jedec_chip_probe = { +- name: "JEDEC", +- probe_chip: jedec_probe_chip ++ .name = "JEDEC", ++ .probe_chip = jedec_probe_chip + }; + + struct mtd_info *jedec_probe(struct map_info *map) +@@ -1150,9 +1849,9 @@ + } + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec_probe", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec_probe", ++ .module = THIS_MODULE + }; + + int __init jedec_probe_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_absent.c linux/drivers/mtd/chips/map_absent.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_absent.c 2001-11-05 21:15:51.000000000 +0100 ++++ linux/drivers/mtd/chips/map_absent.c 2004-11-19 10:25:11.776218488 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle absent "placeholder" devices + * Copyright 2001 Resilience Corporation +- * $Id: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_absent.c,v 1.4 2003/05/28 12:51:49 dwmw2 Exp $ + * + * This map driver is used to allocate "placeholder" MTD + * devices on systems that have socketed/removable media. +@@ -23,9 +23,10 @@ + #include + #include + #include +- ++#include ++#include + #include +- ++#include + + static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -36,10 +37,10 @@ + + + static struct mtd_chip_driver map_absent_chipdrv = { +- probe: map_absent_probe, +- destroy: map_absent_destroy, +- name: "map_absent", +- module: THIS_MODULE ++ .probe = map_absent_probe, ++ .destroy = map_absent_destroy, ++ .name = "map_absent", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_absent_probe(struct map_info *map) +@@ -65,7 +66,7 @@ + mtd->flags = 0; + mtd->erasesize = PAGE_SIZE; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_ram.c linux/drivers/mtd/chips/map_ram.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_ram.c 2001-11-05 21:15:51.000000000 +0100 ++++ linux/drivers/mtd/chips/map_ram.c 2004-11-19 10:25:11.777218336 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle map devices which are simple RAM + * (C) 2000 Red Hat. GPL'd. +- * $Id: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $ + */ + + #include +@@ -11,8 +11,10 @@ + #include + #include + #include +- ++#include ++#include + #include ++#include + + + static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -23,9 +25,9 @@ + + + static struct mtd_chip_driver mapram_chipdrv = { +- probe: map_ram_probe, +- name: "map_ram", +- module: THIS_MODULE ++ .probe = map_ram_probe, ++ .name = "map_ram", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_ram_probe(struct map_info *map) +@@ -34,21 +36,21 @@ + + /* Check the first byte is RAM */ + #if 0 +- map->write8(map, 0x55, 0); +- if (map->read8(map, 0) != 0x55) ++ map_write8(map, 0x55, 0); ++ if (map_read8(map, 0) != 0x55) + return NULL; + +- map->write8(map, 0xAA, 0); +- if (map->read8(map, 0) != 0xAA) ++ map_write8(map, 0xAA, 0); ++ if (map_read8(map, 0) != 0xAA) + return NULL; + + /* Check the last byte is RAM */ +- map->write8(map, 0x55, map->size-1); +- if (map->read8(map, map->size-1) != 0x55) ++ map_write8(map, 0x55, map->size-1); ++ if (map_read8(map, map->size-1) != 0x55) + return NULL; + +- map->write8(map, 0xAA, map->size-1); +- if (map->read8(map, map->size-1) != 0xAA) ++ map_write8(map, 0xAA, map->size-1); ++ if (map_read8(map, map->size-1) != 0xAA) + return NULL; + #endif + /* OK. It seems to be RAM. */ +@@ -74,7 +76,7 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -83,7 +85,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -92,7 +94,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_to(map, to, buf, len); ++ map_copy_to(map, to, buf, len); + *retlen = len; + return 0; + } +@@ -105,7 +107,7 @@ + unsigned long i; + + for (i=0; ilen; i++) +- map->write8(map, 0xFF, instr->addr + i); ++ map_write8(map, 0xFF, instr->addr + i); + + if (instr->callback) + instr->callback(instr); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_rom.c linux/drivers/mtd/chips/map_rom.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_rom.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/chips/map_rom.c 2004-11-19 10:25:11.778218184 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle map devices which are simple ROM + * (C) 2000 Red Hat. GPL'd. +- * $Id: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $ + */ + + #include +@@ -12,8 +12,10 @@ + #include + #include + #include +- ++#include ++#include + #include ++#include + + static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -21,9 +23,9 @@ + struct mtd_info *map_rom_probe(struct map_info *map); + + static struct mtd_chip_driver maprom_chipdrv = { +- probe: map_rom_probe, +- name: "map_rom", +- module: THIS_MODULE ++ .probe = map_rom_probe, ++ .name = "map_rom", ++ .module = THIS_MODULE + }; + + struct mtd_info *map_rom_probe(struct map_info *map) +@@ -49,7 +51,7 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -58,7 +60,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c +--- linux-mips-2.4.27/drivers/mtd/chips/sharp.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/sharp.c 2004-11-19 10:25:11.792216056 +0100 +@@ -4,7 +4,7 @@ + * Copyright 2000,2001 David A. Schleef + * 2000,2001 Lineo, Inc. + * +- * $Id: sharp.c,v 1.8 2002/05/17 08:59:19 dwmw2 Exp $ ++ * $Id: sharp.c,v 1.12 2003/05/28 15:39:52 dwmw2 Exp $ + * + * Devices supported: + * LH28F016SCT Symmetrical block flash memory, 2Mx8 +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -98,10 +99,10 @@ + static void sharp_destroy(struct mtd_info *mtd); + + static struct mtd_chip_driver sharp_chipdrv = { +- probe: sharp_probe, +- destroy: sharp_destroy, +- name: "sharp", +- module: THIS_MODULE ++ .probe = sharp_probe, ++ .destroy = sharp_destroy, ++ .name = "sharp", ++ .module = THIS_MODULE + }; + + +@@ -116,8 +117,10 @@ + return NULL; + + sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); +- if(!sharp) ++ if(!sharp) { ++ kfree(mtd); + return NULL; ++ } + + memset(mtd, 0, sizeof(*mtd)); + +@@ -163,12 +166,12 @@ + u32 read0, read4; + int width = 4; + +- tmp = map->read32(map, base+0); ++ tmp = map_read32(map, base+0); + +- map->write32(map, CMD_READ_ID, base+0); ++ map_write32(map, CMD_READ_ID, base+0); + +- read0=map->read32(map, base+0); +- read4=map->read32(map, base+4); ++ read0=map_read32(map, base+0); ++ read4=map_read32(map, base+4); + if(read0 == 0x89898989){ + printk("Looks like sharp flash\n"); + switch(read4){ +@@ -196,10 +199,10 @@ + printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n", + read0,read4); + } +- }else if((map->read32(map, base+0) == CMD_READ_ID)){ ++ }else if((map_read32(map, base+0) == CMD_READ_ID)){ + /* RAM, probably */ + printk("Looks like RAM\n"); +- map->write32(map, tmp, base+0); ++ map_write32(map, tmp, base+0); + }else{ + printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n", + read0,read4); +@@ -221,10 +224,10 @@ + + switch(chip->state){ + case FL_READY: +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + chip->state = FL_STATUS; + case FL_STATUS: +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + //printk("status=%08x\n",status); + + udelay(100); +@@ -252,7 +255,7 @@ + goto retry; + } + +- map->write32(map,CMD_RESET, adr); ++ map_write32(map,CMD_RESET, adr); + + chip->state = FL_READY; + +@@ -293,7 +296,7 @@ + if(ret<0) + break; + +- map->copy_from(map,buf,ofs,thislen); ++ map_copy_from(map,buf,ofs,thislen); + + sharp_release(&sharp->chips[chipnum]); + +@@ -354,17 +357,17 @@ + ret = sharp_wait(map,chip); + + for(try=0;try<10;try++){ +- map->write32(map,CMD_BYTE_WRITE,adr); ++ map_write32(map,CMD_BYTE_WRITE,adr); + /* cpu_to_le32 -> hack to fix the writel be->le conversion */ +- map->write32(map,cpu_to_le32(datum),adr); ++ map_write32(map,cpu_to_le32(datum),adr); + + chip->state = FL_WRITING; + + timeo = jiffies + (HZ/2); + +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + for(i=0;i<100;i++){ +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + } +@@ -377,9 +380,9 @@ + + printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status); + +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + + wake_up(&chip->wq); +@@ -436,14 +439,14 @@ + int status; + DECLARE_WAITQUEUE(wait, current); + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + timeo = jiffies + HZ; + + while(time_before(jiffies, timeo)){ +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY){ + ret = 0; + goto out; +@@ -485,26 +488,26 @@ + sharp_unlock_oneblock(map,chip,adr); + #endif + +- map->write32(map,CMD_BLOCK_ERASE_1,adr); +- map->write32(map,CMD_BLOCK_ERASE_2,adr); ++ map_write32(map,CMD_BLOCK_ERASE_1,adr); ++ map_write32(map,CMD_BLOCK_ERASE_2,adr); + + chip->state = FL_ERASING; + + ret = sharp_do_wait_for_ready(map,chip,adr); + if(ret<0)return ret; + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + //spin_unlock_bh(chip->mutex); + return 0; + } + + printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + + //spin_unlock_bh(chip->mutex); + +@@ -518,17 +521,17 @@ + int i; + int status; + +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); + + udelay(100); + +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + printk("status=%08x\n",status); + + for(i=0;i<1000;i++){ +- //map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ //map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + udelay(100); +@@ -538,13 +541,13 @@ + } + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + return; + } + + printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } + #endif + +diff -Nurb linux-mips-2.4.27/drivers/mtd/cmdlinepart.c linux/drivers/mtd/cmdlinepart.c +--- linux-mips-2.4.27/drivers/mtd/cmdlinepart.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/cmdlinepart.c 2004-11-19 10:25:11.628240984 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $ ++ * $Id: cmdlinepart.c,v 1.11 2003/10/23 08:32:45 dwmw2 Exp $ + * + * Read flash partition table from command line + * +@@ -28,7 +28,7 @@ + + #include + #include +-#include ++#include + #include + + /* error message prefix */ +@@ -178,8 +178,7 @@ + parts[this_part].mask_flags = mask_flags; + if (name) + { +- strncpy(extra_mem, name, name_len); +- extra_mem[name_len] = 0; ++ strlcpy(extra_mem, name, name_len + 1); + } + else + { +@@ -258,8 +257,7 @@ + this_mtd->parts = parts; + this_mtd->num_parts = num_parts; + this_mtd->mtd_id = (char*)(this_mtd + 1); +- strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); +- this_mtd->mtd_id[mtd_id_len] = 0; ++ strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); + + /* link into chain */ + this_mtd->next = partitions; +@@ -291,13 +289,14 @@ + * information. It returns partitions for the requested mtd device, or + * the first one in the chain if a NULL mtd_id is passed in. + */ +-int parse_cmdline_partitions(struct mtd_info *master, ++static int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, +- const char *mtd_id) ++ unsigned long origin) + { + unsigned long offset; + int i; + struct cmdline_mtd_partition *part; ++ char *mtd_id = master->name; + + if(!cmdline) + return -EINVAL; +@@ -349,7 +348,25 @@ + + __setup("mtdparts=", mtdpart_setup); + +-EXPORT_SYMBOL(parse_cmdline_partitions); ++static struct mtd_part_parser cmdline_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_cmdline_partitions, ++ .name = "cmdlinepart", ++}; ++ ++static int __init cmdline_parser_init(void) ++{ ++ return register_mtd_parser(&cmdline_parser); ++} ++ ++static void __exit cmdline_parser_exit(void) ++{ ++ deregister_mtd_parser(&cmdline_parser); ++} ++ ++module_init(cmdline_parser_init); ++module_exit(cmdline_parser_exit); ++ + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Marius Groeger "); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Config.in linux/drivers/mtd/devices/Config.in +--- linux-mips-2.4.27/drivers/mtd/devices/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/Config.in 2004-11-19 10:25:11.811213168 +0100 +@@ -1,6 +1,6 @@ +-# drivers/mtd/maps/Config.in ++# drivers/mtd/devices/Config.in + +-# $Id: Config.in,v 1.8 2003/01/24 23:25:14 dwmw2 Exp $ ++# $Id: Config.in,v 1.11 2003/05/28 10:54:23 dwmw2 Exp $ + + mainmenu_option next_comment + +@@ -28,13 +28,13 @@ + dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD + + comment 'Disk-On-Chip Device Drivers' +- dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD +- if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then ++ dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then + define_bool CONFIG_MTD_DOCPROBE y + else +- if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then + define_bool CONFIG_MTD_DOCPROBE m + else + define_bool CONFIG_MTD_DOCPROBE n +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Makefile linux/drivers/mtd/devices/Makefile +--- linux-mips-2.4.27/drivers/mtd/devices/Makefile 2002-03-30 09:15:50.000000000 +0100 ++++ linux/drivers/mtd/devices/Makefile 2004-11-19 10:25:11.813212864 +0100 +@@ -1,9 +1,12 @@ + # + # linux/drivers/devices/Makefile + # +-# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $ ++# $Id: Makefile.common,v 1.4 2003/08/21 17:52:29 joern Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := devlink.o ++export-objs := docecc.o ++endif + + # *** BIG UGLY NOTE *** + # +@@ -12,15 +15,16 @@ + # here where previously there was none. We now have to ensure that + # doc200[01].o are linked before docprobe.o + +-obj-$(CONFIG_MTD_DOC1000) += doc1000.o + obj-$(CONFIG_MTD_DOC2000) += doc2000.o + obj-$(CONFIG_MTD_DOC2001) += doc2001.o ++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o + obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o + obj-$(CONFIG_MTD_SLRAM) += slram.o ++obj-$(CONFIG_MTD_PHRAM) += phram.o + obj-$(CONFIG_MTD_PMC551) += pmc551.o + obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o + obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + obj-$(CONFIG_MTD_LART) += lart.o + obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c linux/drivers/mtd/devices/blkmtd-25.c +--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/blkmtd-25.c 2004-11-19 10:25:11.814212712 +0100 +@@ -0,0 +1,827 @@ ++/* ++ * $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $ ++ * ++ * blkmtd.c - use a block device as a fake MTD ++ * ++ * Author: Simon Evans ++ * ++ * Copyright (C) 2001,2002 Simon Evans ++ * ++ * Licence: GPL ++ * ++ * How it works: ++ * The driver uses raw/io to read/write the device and the page ++ * cache to cache access. Writes update the page cache with the ++ * new data and mark it dirty and add the page into a BIO which ++ * is then written out. ++ * ++ * It can be loaded Read-Only to prevent erases and writes to the ++ * medium. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) ++#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) ++ ++ ++/* Default erase size in K, always make it a multiple of PAGE_SIZE */ ++#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ ++#define VERSION "$Revision: 1.5 $" ++ ++/* Info for the block device */ ++struct blkmtd_dev { ++ struct list_head list; ++ struct block_device *blkdev; ++ struct mtd_info mtd_info; ++ struct semaphore wrbuf_mutex; ++}; ++ ++ ++/* Static info about the MTD, used in cleanup_module */ ++static LIST_HEAD(blkmtd_device_list); ++ ++ ++static void blkmtd_sync(struct mtd_info *mtd); ++ ++#define MAX_DEVICES 4 ++ ++/* Module parameters passed by insmod/modprobe */ ++char *device[MAX_DEVICES]; /* the block device to use */ ++int erasesz[MAX_DEVICES]; /* optional default erase size */ ++int ro[MAX_DEVICES]; /* optional read only flag */ ++int sync; ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Simon Evans "); ++MODULE_DESCRIPTION("Emulate an MTD using a block device"); ++MODULE_PARM(device, "1-4s"); ++MODULE_PARM_DESC(device, "block device to use"); ++MODULE_PARM(erasesz, "1-4i"); ++MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); ++MODULE_PARM(ro, "1-4i"); ++MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); ++MODULE_PARM(sync, "i"); ++MODULE_PARM_DESC(sync, "1=Synchronous writes"); ++ ++ ++/* completion handler for BIO reads */ ++static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) ++{ ++ if (bio->bi_size) ++ return 1; ++ ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} ++ ++ ++/* completion handler for BIO writes */ ++static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) ++{ ++ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); ++ struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; ++ ++ if (bio->bi_size) ++ return 1; ++ ++ if(!uptodate) ++ err("bi_write_complete: not uptodate\n"); ++ ++ do { ++ struct page *page = bvec->bv_page; ++ DEBUG(3, "Cleaning up page %ld\n", page->index); ++ if (--bvec >= bio->bi_io_vec) ++ prefetchw(&bvec->bv_page->flags); ++ ++ if (uptodate) { ++ SetPageUptodate(page); ++ } else { ++ ClearPageUptodate(page); ++ SetPageError(page); ++ } ++ ClearPageDirty(page); ++ unlock_page(page); ++ page_cache_release(page); ++ } while (bvec >= bio->bi_io_vec); ++ ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} ++ ++ ++/* read one page from the block device */ ++static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) ++{ ++ struct bio *bio; ++ struct completion event; ++ int err = -ENOMEM; ++ ++ if(PageUptodate(page)) { ++ DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); ++ unlock_page(page); ++ return 0; ++ } ++ ++ ClearPageUptodate(page); ++ ClearPageError(page); ++ ++ bio = bio_alloc(GFP_KERNEL, 1); ++ if(bio) { ++ init_completion(&event); ++ bio->bi_bdev = dev->blkdev; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_read_complete; ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { ++ submit_bio(READ, bio); ++ blk_run_queues(); ++ wait_for_completion(&event); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); ++ } ++ } ++ ++ if(err) ++ SetPageError(page); ++ else ++ SetPageUptodate(page); ++ flush_dcache_page(page); ++ unlock_page(page); ++ return err; ++} ++ ++ ++/* write out the current BIO and wait for it to finish */ ++static int blkmtd_write_out(struct bio *bio) ++{ ++ struct completion event; ++ int err; ++ ++ if(!bio->bi_vcnt) { ++ bio_put(bio); ++ return 0; ++ } ++ ++ init_completion(&event); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_write_complete; ++ submit_bio(WRITE, bio); ++ blk_run_queues(); ++ wait_for_completion(&event); ++ DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); ++ return err; ++} ++ ++ ++/** ++ * blkmtd_add_page - add a page to the current BIO ++ * @bio: bio to add to (NULL to alloc initial bio) ++ * @blkdev: block device ++ * @page: page to add ++ * @pagecnt: pages left to add ++ * ++ * Adds a page to the current bio, allocating it if necessary. If it cannot be ++ * added, the current bio is written out and a new one is allocated. Returns ++ * the new bio to add or NULL on error ++ */ ++static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, ++ struct page *page, int pagecnt) ++{ ++ ++ retry: ++ if(!bio) { ++ bio = bio_alloc(GFP_KERNEL, pagecnt); ++ if(!bio) ++ return NULL; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_bdev = blkdev; ++ } ++ ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { ++ blkmtd_write_out(bio); ++ bio = NULL; ++ goto retry; ++ } ++ return bio; ++} ++ ++ ++/** ++ * write_pages - write block of data to device via the page cache ++ * @dev: device to write to ++ * @buf: data source or NULL if erase (output is set to 0xff) ++ * @to: offset into output device ++ * @len: amount to data to write ++ * @retlen: amount of data written ++ * ++ * Grab pages from the page cache and fill them with the source data. ++ * Non page aligned start and end result in a readin of the page and ++ * part of the page being modified. Pages are added to the bio and then written ++ * out. ++ */ ++static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, ++ size_t len, size_t *retlen) ++{ ++ int pagenr, offset; ++ size_t start_len = 0, end_len; ++ int pagecnt = 0; ++ int err = 0; ++ struct bio *bio = NULL; ++ size_t thislen = 0; ++ ++ pagenr = to >> PAGE_SHIFT; ++ offset = to & ~PAGE_MASK; ++ ++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n", ++ buf, (long)to, len, pagenr, offset); ++ ++ /* see if we have to do a partial write at the start */ ++ if(offset) { ++ start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; ++ len -= start_len; ++ } ++ ++ /* calculate the length of the other two regions */ ++ end_len = len & ~PAGE_MASK; ++ len -= end_len; ++ ++ if(start_len) ++ pagecnt++; ++ ++ if(len) ++ pagecnt += len >> PAGE_SHIFT; ++ ++ if(end_len) ++ pagecnt++; ++ ++ down(&dev->wrbuf_mutex); ++ ++ DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n", ++ start_len, len, end_len, pagecnt); ++ ++ if(start_len) { ++ /* do partial start region */ ++ struct page *page; ++ ++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", ++ pagenr, start_len, offset); ++ ++ BUG_ON(!buf); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); ++ BUG(); ++ } ++ memcpy(page_address(page)+offset, buf, start_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ buf += start_len; ++ thislen = start_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagecnt--; ++ pagenr++; ++ } ++ ++ /* Now do the main loop to a page aligned, n page sized output */ ++ if(len) { ++ int pagesc = len >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", ++ pagenr, pagesc); ++ while(pagesc) { ++ struct page *page; ++ ++ /* see if page is in the page cache */ ++ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); ++ page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); ++ if(PageDirty(page)) { ++ BUG(); ++ } ++ if(!page) { ++ warn("write: cannot grab cache page %d", pagenr); ++ err = -ENOMEM; ++ goto write_err; ++ } ++ if(!buf) { ++ memset(page_address(page), 0xff, PAGE_SIZE); ++ } else { ++ memcpy(page_address(page), buf, PAGE_SIZE); ++ buf += PAGE_SIZE; ++ } ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagenr++; ++ pagecnt--; ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ pagesc--; ++ thislen += PAGE_SIZE; ++ } ++ } ++ ++ if(end_len) { ++ /* do the third region */ ++ struct page *page; ++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", ++ pagenr, end_len); ++ BUG_ON(!buf); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); ++ BUG(); ++ } ++ memcpy(page_address(page), buf, end_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ DEBUG(3, "blkmtd: write: writing out partial end\n"); ++ thislen += end_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagenr++; ++ } ++ ++ DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); ++ write_err: ++ if(bio) ++ blkmtd_write_out(bio); ++ ++ DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err); ++ up(&dev->wrbuf_mutex); ++ ++ if(retlen) ++ *retlen = thislen; ++ return err; ++} ++ ++ ++/* erase a specified part of the device */ ++static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ struct mtd_erase_region_info *einfo = mtd->eraseregions; ++ int numregions = mtd->numeraseregions; ++ size_t from; ++ u_long len; ++ int err = -EIO; ++ int retlen; ++ ++ instr->state = MTD_ERASING; ++ from = instr->addr; ++ len = instr->len; ++ ++ /* check erase region has valid start and length */ ++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n", ++ mtd->name+9, from, len); ++ while(numregions) { ++ DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", ++ einfo->offset, einfo->erasesize, einfo->numblocks); ++ if(from >= einfo->offset ++ && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { ++ if(len == einfo->erasesize ++ && ( (from - einfo->offset) % einfo->erasesize == 0)) ++ break; ++ } ++ numregions--; ++ einfo++; ++ } ++ ++ if(!numregions) { ++ /* Not a valid erase block */ ++ err("erase: invalid erase request 0x%lX @ 0x%08X", len, from); ++ instr->state = MTD_ERASE_FAILED; ++ err = -EIO; ++ } ++ ++ if(instr->state != MTD_ERASE_FAILED) { ++ /* do the erase */ ++ DEBUG(3, "Doing erase from = %d len = %ld\n", from, len); ++ err = write_pages(dev, NULL, from, len, &retlen); ++ if(err || retlen != len) { ++ err("erase failed err = %d", err); ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ } ++ } ++ ++ DEBUG(3, "blkmtd: erase: checking callback\n"); ++ if (instr->callback) { ++ (*(instr->callback))(instr); ++ } ++ DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); ++ return err; ++} ++ ++ ++/* read a range of the data via the page cache */ ++static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err = 0; ++ int offset; ++ int pagenr, pages; ++ size_t thislen = 0; ++ ++ DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n", ++ mtd->name+9, (long int)from, len, buf); ++ ++ if(from > mtd->size) ++ return -EINVAL; ++ if(from + len > mtd->size) ++ len = mtd->size - from; ++ ++ pagenr = from >> PAGE_SHIFT; ++ offset = from - (pagenr << PAGE_SHIFT); ++ ++ pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", ++ pagenr, offset, pages); ++ ++ while(pages) { ++ struct page *page; ++ int cpylen; ++ ++ DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ if(IS_ERR(page)) { ++ err = -EIO; ++ goto readerr; ++ } ++ ++ cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; ++ if(offset+cpylen > PAGE_SIZE) ++ cpylen = PAGE_SIZE-offset; ++ ++ memcpy(buf + thislen, page_address(page) + offset, cpylen); ++ offset = 0; ++ len -= cpylen; ++ thislen += cpylen; ++ pagenr++; ++ pages--; ++ if(!PageDirty(page)) ++ page_cache_release(page); ++ } ++ ++ readerr: ++ if(retlen) ++ *retlen = thislen; ++ DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", thislen, err); ++ return err; ++} ++ ++ ++/* write data to the underlying device */ ++static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err; ++ ++ if(!len) ++ return 0; ++ ++ DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n", ++ mtd->name+9, (long int)to, len, buf); ++ ++ if(to >= mtd->size) { ++ return -ENOSPC; ++ } ++ ++ if(to + len > mtd->size) { ++ len = mtd->size - to; ++ } ++ ++ err = write_pages(dev, buf, to, len, retlen); ++ if(err > 0) ++ err = 0; ++ DEBUG(2, "blkmtd: write: end, err = %d\n", err); ++ return err; ++} ++ ++ ++/* sync the device - wait until the write queue is empty */ ++static void blkmtd_sync(struct mtd_info *mtd) ++{ ++ /* Currently all writes are synchronous */ ++} ++ ++ ++static void free_device(struct blkmtd_dev *dev) ++{ ++ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); ++ if(dev) { ++ if(dev->mtd_info.eraseregions) ++ kfree(dev->mtd_info.eraseregions); ++ if(dev->mtd_info.name) ++ kfree(dev->mtd_info.name); ++ ++ if(dev->blkdev) { ++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); ++ close_bdev_excl(dev->blkdev, BDEV_RAW); ++ } ++ kfree(dev); ++ } ++} ++ ++ ++/* For a given size and initial erase size, calculate the number ++ * and size of each erase region. Goes round the loop twice, ++ * once to find out how many regions, then allocates space, ++ * then round the loop again to fill it in. ++ */ ++static struct mtd_erase_region_info *calc_erase_regions( ++ size_t erase_size, size_t total_size, int *regions) ++{ ++ struct mtd_erase_region_info *info = NULL; ++ ++ DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n", ++ erase_size, total_size, *regions); ++ /* Make any user specified erasesize be a power of 2 ++ and at least PAGE_SIZE */ ++ if(erase_size) { ++ int es = erase_size; ++ erase_size = 1; ++ while(es != 1) { ++ es >>= 1; ++ erase_size <<= 1; ++ } ++ if(erase_size < PAGE_SIZE) ++ erase_size = PAGE_SIZE; ++ } else { ++ erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; ++ } ++ ++ *regions = 0; ++ ++ do { ++ int tot_size = total_size; ++ int er_size = erase_size; ++ int count = 0, offset = 0, regcnt = 0; ++ ++ while(tot_size) { ++ count = tot_size / er_size; ++ if(count) { ++ tot_size = tot_size % er_size; ++ if(info) { ++ DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", ++ offset, er_size, count); ++ (info+regcnt)->offset = offset; ++ (info+regcnt)->erasesize = er_size; ++ (info+regcnt)->numblocks = count; ++ (*regions)++; ++ } ++ regcnt++; ++ offset += (count * er_size); ++ } ++ while(er_size > tot_size) ++ er_size >>= 1; ++ } ++ if(info == NULL) { ++ info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if(!info) ++ break; ++ } ++ } while(!(*regions)); ++ DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n", ++ erase_size, total_size, *regions); ++ return info; ++} ++ ++ ++extern dev_t __init name_to_dev_t(const char *line); ++ ++static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) ++{ ++ struct block_device *bdev; ++ int mode; ++ struct blkmtd_dev *dev; ++ ++ if(!devname) ++ return NULL; ++ ++ /* Get a handle on the device */ ++ ++ ++#ifdef MODULE ++ mode = (readonly) ? O_RDONLY : O_RDWR; ++ bdev = open_bdev_excl(devname, mode, BDEV_RAW, NULL); ++#else ++ mode = (readonly) ? FMODE_READ : FMODE_WRITE; ++ bdev = open_by_devnum(name_to_dev_t(devname), mode, BDEV_RAW); ++#endif ++ if(IS_ERR(bdev)) { ++ err("error: cannot open device %s", devname); ++ DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); ++ return NULL; ++ } ++ ++ DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", ++ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); ++ ++ if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { ++ err("attempting to use an MTD device as a block device"); ++ blkdev_put(bdev, BDEV_RAW); ++ return NULL; ++ } ++ ++ dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); ++ if(dev == NULL) { ++ blkdev_put(bdev, BDEV_RAW); ++ return NULL; ++ } ++ ++ memset(dev, 0, sizeof(struct blkmtd_dev)); ++ if(!readonly) { ++ init_MUTEX(&dev->wrbuf_mutex); ++ } ++ ++ dev->blkdev = bdev; ++ dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; ++ ++ /* Setup the MTD structure */ ++ /* make the name contain the block device in */ ++ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); ++ if(dev->mtd_info.name == NULL) ++ goto devinit_err; ++ ++ sprintf(dev->mtd_info.name, "blkmtd: %s", devname); ++ dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, ++ &dev->mtd_info.numeraseregions); ++ if(dev->mtd_info.eraseregions == NULL) ++ goto devinit_err; ++ ++ dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; ++ DEBUG(1, "blkmtd: init: found %d erase regions\n", ++ dev->mtd_info.numeraseregions); ++ ++ if(readonly) { ++ dev->mtd_info.type = MTD_ROM; ++ dev->mtd_info.flags = MTD_CAP_ROM; ++ } else { ++ dev->mtd_info.type = MTD_RAM; ++ dev->mtd_info.flags = MTD_CAP_RAM; ++ dev->mtd_info.erase = blkmtd_erase; ++ dev->mtd_info.write = blkmtd_write; ++ dev->mtd_info.writev = default_mtd_writev; ++ dev->mtd_info.sync = blkmtd_sync; ++ } ++ dev->mtd_info.read = blkmtd_read; ++ dev->mtd_info.readv = default_mtd_readv; ++ dev->mtd_info.priv = dev; ++ dev->mtd_info.owner = THIS_MODULE; ++ ++ list_add(&dev->list, &blkmtd_device_list); ++ if (add_mtd_device(&dev->mtd_info)) { ++ /* Device didnt get added, so free the entry */ ++ list_del(&dev->list); ++ goto devinit_err; ++ } else { ++ info("mtd%d: [%s] erase_size = %dKiB %s", ++ dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), ++ dev->mtd_info.erasesize >> 10, ++ readonly ? "(read-only)" : ""); ++ } ++ ++ return dev; ++ ++ devinit_err: ++ free_device(dev); ++ return NULL; ++} ++ ++ ++/* Cleanup and exit - sync the device and kill of the kernel thread */ ++static void __devexit cleanup_blkmtd(void) ++{ ++ struct list_head *temp1, *temp2; ++ ++ /* Remove the MTD devices */ ++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { ++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, ++ list); ++ blkmtd_sync(&dev->mtd_info); ++ del_mtd_device(&dev->mtd_info); ++ info("mtd%d: [%s] removed", dev->mtd_info.index, ++ dev->mtd_info.name + strlen("blkmtd: ")); ++ list_del(&dev->list); ++ free_device(dev); ++ } ++} ++ ++#ifndef MODULE ++ ++/* Handle kernel boot params */ ++ ++ ++static int __init param_blkmtd_device(char *str) ++{ ++ int i; ++ ++ for(i = 0; i < MAX_DEVICES; i++) { ++ device[i] = str; ++ DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); ++ strsep(&str, ","); ++ } ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_erasesz(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ erasesz[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_ro(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ ro[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_sync(char *str) ++{ ++ if(str[0] == '1') ++ sync = 1; ++ return 1; ++} ++ ++__setup("blkmtd_device=", param_blkmtd_device); ++__setup("blkmtd_erasesz=", param_blkmtd_erasesz); ++__setup("blkmtd_ro=", param_blkmtd_ro); ++__setup("blkmtd_sync=", param_blkmtd_sync); ++ ++#endif ++ ++ ++/* Startup */ ++static int __init init_blkmtd(void) ++{ ++ int i; ++ ++ info("version " VERSION); ++ /* Check args - device[0] is the bare minimum*/ ++ if(!device[0]) { ++ err("error: missing `device' name\n"); ++ return -EINVAL; ++ } ++ ++ for(i = 0; i < MAX_DEVICES; i++) ++ add_device(device[i], ro[i], erasesz[i] << 10); ++ ++ if(list_empty(&blkmtd_device_list)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++module_init(init_blkmtd); ++module_exit(cleanup_blkmtd); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c +--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/blkmtd.c 2004-11-19 10:25:11.816212408 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $ ++ * $Id: blkmtd.c,v 1.20 2003/06/27 15:10:35 dwmw2 Exp $ + * + * blkmtd.c - use a block device as a fake MTD + * +@@ -143,7 +143,7 @@ + for(cnt = 0; cnt < pages; cnt++) { + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); + pagelst[cnt] = page; +- if(!PageUptodate(page)) { ++ if(!Page_Uptodate(page)) { + iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; + iobuf->maplist[iobuf->nr_pages++] = page; + } +@@ -912,7 +912,7 @@ + dev->mtd_info.point = 0; + dev->mtd_info.unpoint = 0; + dev->mtd_info.priv = dev; +- dev->mtd_info.module = THIS_MODULE; ++ dev->mtd_info.owner = THIS_MODULE; + + list_add(&dev->list, &blkmtd_device_list); + if (add_mtd_device(&dev->mtd_info)) { +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2000.c linux/drivers/mtd/devices/doc2000.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2000.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2000.c 2004-11-19 10:25:11.818212104 +0100 +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * +- * $Id: doc2000.c,v 1.50 2002/12/10 15:05:42 gleixner Exp $ ++ * $Id: doc2000.c,v 1.58 2003/11/05 16:42:25 dwmw2 Exp $ + */ + + #include +@@ -25,6 +25,7 @@ + #include + + #define DOC_SUPPORT_2000 ++#define DOC_SUPPORT_2000TSOP + #define DOC_SUPPORT_MILLENNIUM + + #ifdef DOC_SUPPORT_2000 +@@ -33,7 +34,7 @@ + #define DoC_is_2000(doc) (0) + #endif + +-#ifdef DOC_SUPPORT_MILLENNIUM ++#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) + #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) + #else + #define DoC_is_Millennium(doc) (0) +@@ -56,6 +57,9 @@ + size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); + static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel); + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); + static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, +@@ -92,6 +96,10 @@ + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ /* issue 2 read from NOP register after reading from CDSNControl register ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(doc, 2); ++ + if (time_after(jiffies, timeo)) { + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + return -EIO; +@@ -145,6 +153,8 @@ + + /* Send the command */ + WriteDOC_(command, docptr, doc->ioreg); ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(command, docptr, WritePipeTerm); + + /* Lower the CLE line */ + WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); +@@ -206,6 +216,9 @@ + } + } + ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(ofs & 0xff, docptr, WritePipeTerm); ++ + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ + + /* FIXME: The SlowIO's for millennium could be replaced by +@@ -344,15 +357,25 @@ + + /* Read the manufacturer and device id codes from the device */ + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ if (DoC_is_Millennium(doc)) { ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ mfr = ReadDOC(doc->virtadr, LastDataRead); ++ ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ id = ReadDOC(doc->virtadr, LastDataRead); ++ } else { ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); ++ } + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) +@@ -410,20 +433,16 @@ + + /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +-static void DoC_ScanChips(struct DiskOnChip *this) ++static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) + { + int floor, chip; + int numchips[MAX_FLOORS]; +- int maxchips = MAX_CHIPS; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + +- if (DoC_is_Millennium(this)) +- maxchips = MAX_CHIPS_MIL; +- + /* For each floor, find the number of valid chips it contains */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + ret = 1; +@@ -515,6 +534,7 @@ + { + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *old = NULL; ++ int maxchips; + + /* We must avoid being called twice for the same device. */ + +@@ -538,14 +558,28 @@ + + + switch (this->ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ mtd->name = "DiskOnChip 2000 TSOP"; ++ this->ioreg = DoC_Mil_CDSN_IO; ++ /* Pretend it's a Millennium */ ++ this->ChipID = DOC_ChipID_DocMil; ++ maxchips = MAX_CHIPS; ++ break; + case DOC_ChipID_Doc2k: + mtd->name = "DiskOnChip 2000"; + this->ioreg = DoC_2k_CDSN_IO; ++ maxchips = MAX_CHIPS; + break; + case DOC_ChipID_DocMil: + mtd->name = "DiskOnChip Millennium"; + this->ioreg = DoC_Mil_CDSN_IO; ++ maxchips = MAX_CHIPS_MIL; + break; ++ default: ++ printk("Unknown ChipID 0x%02x\n", this->ChipID); ++ kfree(mtd); ++ iounmap((void *) this->virtadr); ++ return; + } + + printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, +@@ -553,11 +587,12 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + mtd->erasesize = 0; + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +@@ -565,6 +600,7 @@ + mtd->write = doc_write; + mtd->read_ecc = doc_read_ecc; + mtd->write_ecc = doc_write_ecc; ++ mtd->writev_ecc = doc_writev_ecc; + mtd->read_oob = doc_read_oob; + mtd->write_oob = doc_write_oob; + mtd->sync = NULL; +@@ -577,7 +613,7 @@ + init_MUTEX(&this->lock); + + /* Ident all the chips present. */ +- DoC_ScanChips(this); ++ DoC_ScanChips(this, maxchips); + + if (!this->totlen) { + kfree(mtd); +@@ -608,6 +644,7 @@ + unsigned char syndrome[6]; + volatile char dummy; + int i, len256 = 0, ret=0; ++ size_t left = len; + + docptr = this->virtadr; + +@@ -617,6 +654,10 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; +@@ -673,7 +714,7 @@ + DoC_ReadBuf(this, &buf[len256], len - len256); + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ +@@ -730,11 +771,16 @@ + + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ +- if(0 == ((from + *retlen) & 0x1ff)) ++ if(0 == ((from + len) & 0x1ff)) + { + DoC_WaitReady(this); + } + ++ from += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + + return ret; +@@ -757,6 +803,8 @@ + volatile char dummy; + int len256 = 0; + struct Nand *mychip; ++ size_t left = len; ++ int status; + + docptr = this->virtadr; + +@@ -766,15 +814,21 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ((to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + + /* The ECC will not be calculated correctly if less than 512 is written */ ++/* DBB- + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); ++ -DBB */ + + /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ + +@@ -853,6 +907,9 @@ + WriteDOC_(0, docptr, this->ioreg); + } + ++ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, ++ CDSNControl); ++ + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di = 0; di < 6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); +@@ -874,10 +931,16 @@ + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming flash\n"); + /* Error in programming */ + *retlen = 0; +@@ -886,7 +949,7 @@ + } + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + unsigned char x[8]; +@@ -901,13 +964,81 @@ + x[7]=0x55; + + ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); ++ if (ret) { + up(&this->lock); + return ret; + } ++ } ++ ++ to += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + return 0; + } + ++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel) ++{ ++ static char static_buf[512]; ++ static DECLARE_MUTEX(writev_buf_sem); ++ ++ size_t totretlen = 0; ++ size_t thisvecofs = 0; ++ int ret= 0; ++ ++ down(&writev_buf_sem); ++ ++ while(count) { ++ size_t thislen, thisretlen; ++ unsigned char *buf; ++ ++ buf = vecs->iov_base + thisvecofs; ++ thislen = vecs->iov_len - thisvecofs; ++ ++ ++ if (thislen >= 512) { ++ thislen = thislen & ~(512-1); ++ thisvecofs += thislen; ++ } else { ++ /* Not enough to fill a page. Copy into buf */ ++ memcpy(static_buf, buf, thislen); ++ buf = &static_buf[thislen]; ++ ++ while(count && thislen < 512) { ++ vecs++; ++ count--; ++ thisvecofs = min((512-thislen), vecs->iov_len); ++ memcpy(buf, vecs->iov_base, thisvecofs); ++ thislen += thisvecofs; ++ buf += thisvecofs; ++ } ++ buf = static_buf; ++ } ++ if (count && thisvecofs == vecs->iov_len) { ++ thisvecofs = 0; ++ vecs++; ++ count--; ++ } ++ ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); ++ ++ totretlen += thisretlen; ++ ++ if (ret || thisretlen != thislen) ++ break; ++ ++ to += thislen; ++ } ++ ++ up(&writev_buf_sem); ++ *retlen = totretlen; ++ return ret; ++} ++ ++ + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, u_char * buf) + { +@@ -977,6 +1108,7 @@ + unsigned long docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + volatile int dummy; ++ int status; + + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); +@@ -1025,10 +1157,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1044,10 +1182,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1080,6 +1224,7 @@ + volatile int dummy; + unsigned long docptr; + struct Nand *mychip; ++ int status; + + down(&this->lock); + +@@ -1111,10 +1256,16 @@ + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error erasing at 0x%x\n", ofs); + /* There was an error */ + instr->state = MTD_ERASE_FAILED; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001.c linux/drivers/mtd/devices/doc2001.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2001.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2001.c 2004-11-19 10:25:11.835209520 +0100 +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * +- * $Id: doc2001.c,v 1.38 2002/12/10 15:05:42 gleixner Exp $ ++ * $Id: doc2001.c,v 1.41 2003/06/11 09:45:19 dwmw2 Exp $ + */ + + #include +@@ -359,14 +359,15 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + +- /* FIXME: erase size is not always 8kB */ ++ /* FIXME: erase size is not always 8KiB */ + mtd->erasesize = 0x2000; + + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c linux/drivers/mtd/devices/doc2001plus.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2001plus.c 2004-11-19 10:25:11.837209216 +0100 +@@ -0,0 +1,1154 @@ ++/* ++ * Linux driver for Disk-On-Chip Millennium Plus ++ * ++ * (c) 2002-2003 Greg Ungerer ++ * (c) 2002-2003 SnapGear Inc ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * (c) 1999, 2000 David Woodhouse ++ * ++ * $Id: doc2001plus.c,v 1.7 2003/07/11 07:36:22 dwmw2 Exp $ ++ * ++ * Released under GPL ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* #define ECC_DEBUG */ ++ ++/* I have no idea why some DoC chips can not use memcop_form|to_io(). ++ * This may be due to the different revisions of the ASIC controller built-in or ++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment ++ * this:*/ ++#undef USE_MEMCPY ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); ++ ++static struct mtd_info *docmilpluslist = NULL; ++ ++ ++/* Perform the required delay cycles by writing to the NOP register */ ++static void DoC_Delay(unsigned long docptr, int cycles) ++{ ++ int i; ++ ++ for (i = 0; (i < cycles); i++) ++ WriteDOC(0, docptr, Mplus_NOP); ++} ++ ++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) ++ ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(unsigned long docptr) ++{ ++ unsigned int c = 0xffff; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "_DoC_WaitReady called for out-of-line wait\n"); ++ ++ /* Out-of-line routine to wait for chip response */ ++ while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) ++ ; ++ ++ if (c == 0) ++ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); ++ ++ return (c == 0); ++} ++ ++static inline int DoC_WaitReady(unsigned long docptr) ++{ ++ /* This is inline, to optimise the common case, where it's ready instantly */ ++ int ret = 0; ++ ++ /* read form NOP register should be issued prior to the read from CDSNControl ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(docptr, 4); ++ ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(docptr); ++ ++ return ret; ++} ++ ++/* For some reason the Millennium Plus seems to occassionally put itself ++ * into reset mode. For me this happens randomly, with no pattern that I ++ * can detect. M-systems suggest always check this on any block level ++ * operation and setting to normal mode if in reset mode. ++ */ ++static inline void DoC_CheckASIC(unsigned long docptr) ++{ ++ /* Make sure the DoC is in normal mode */ ++ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { ++ WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); ++ WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); ++ } ++} ++ ++/* DoC_Command: Send a flash command to the flash chip through the Flash ++ * command register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Command(unsigned long docptr, unsigned char command, ++ unsigned char xtraflags) ++{ ++ WriteDOC(command, docptr, Mplus_FlashCmd); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_Address: Set the current address for the flash chip through the Flash ++ * Address register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, ++ unsigned long ofs, unsigned char xtraflags1, ++ unsigned char xtraflags2) ++{ ++ unsigned long docptr = doc->virtadr; ++ ++ /* Allow for possible Mill Plus internal flash interleaving */ ++ ofs >>= doc->interleave; ++ ++ switch (numbytes) { ++ case 1: ++ /* Send single byte, bits 0-7. */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 2: ++ /* Send bits 9-16 followed by 17-23 */ ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 3: ++ /* Send 0-7, 9-16, then 17-23 */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ default: ++ return; ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_SelectChip: Select a given flash chip within the current floor */ ++static int DoC_SelectChip(unsigned long docptr, int chip) ++{ ++ /* No choice for flash chip on Millennium Plus */ ++ return 0; ++} ++ ++/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ ++static int DoC_SelectFloor(unsigned long docptr, int floor) ++{ ++ WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); ++ return 0; ++} ++ ++/* ++ * Translate the given offset into the appropriate command and offset. ++ * This does the mapping using the 16bit interleave layout defined by ++ * M-Systems, and looks like this for a sector pair: ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ */ ++/* FIXME: This lives in INFTL not here. Other users of flash devices ++ may not want it */ ++static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ ++ if (this->interleave) { ++ unsigned int ofs = *from & 0x3ff; ++ unsigned int cmd; ++ ++ if (ofs < 512) { ++ cmd = NAND_CMD_READ0; ++ ofs &= 0x1ff; ++ } else if (ofs < 1014) { ++ cmd = NAND_CMD_READ1; ++ ofs = (ofs & 0x1ff) + 10; ++ } else { ++ cmd = NAND_CMD_READOOB; ++ ofs = ofs - 1014; ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++ } else { ++ /* No interleave */ ++ if ((*from) & 0x100) ++ return NAND_CMD_READ1; ++ return NAND_CMD_READ0; ++ } ++} ++ ++static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ if (*from & 0x200) { ++ cmd = NAND_CMD_READOOB; ++ ofs = 10 + (*from & 0xf); ++ } else { ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0xf); ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0x200) ? 8 : 6; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READOOB; ++ ofs = (*from & 0x200) ? 24 : 16; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); ++#endif ++} ++ ++static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); ++#endif ++} ++ ++/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ ++static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ++{ ++ int mfr, id, i, j; ++ volatile char dummy; ++ unsigned long docptr = doc->virtadr; ++ ++ /* Page in the required floor/chip */ ++ DoC_SelectFloor(docptr, floor); ++ DoC_SelectChip(docptr, chip); ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Read the NAND chip ID: 1. Send ReadID command */ ++ DoC_Command(docptr, NAND_CMD_READID, 0); ++ ++ /* Read the NAND chip ID: 2. Send address byte zero */ ++ DoC_Address(doc, 1, 0x00, 0, 0x00); ++ ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* Read the manufacturer and device id codes of the flash device through ++ CDSN IO register see Software Requirement 11.4 item 5.*/ ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ mfr = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ id = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* No response - return failure */ ++ if (mfr == 0xff || mfr == 0) ++ return 0; ++ ++ for (i = 0; nand_flash_ids[i].name != NULL; i++) { ++ if (id == nand_flash_ids[i].id) { ++ /* Try to identify manufacturer */ ++ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { ++ if (nand_manuf_ids[j].id == mfr) ++ break; ++ } ++ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " ++ "Chip ID: %2.2X (%s:%s)\n", mfr, id, ++ nand_manuf_ids[j].name, nand_flash_ids[i].name); ++ doc->mfr = mfr; ++ doc->id = id; ++ doc->chipshift = nand_flash_ids[i].chipshift; ++ doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; ++ break; ++ } ++ } ++ ++ if (nand_flash_ids[i].name == NULL) ++ return 0; ++ return 1; ++} ++ ++/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ ++static void DoC_ScanChips(struct DiskOnChip *this) ++{ ++ int floor, chip; ++ int numchips[MAX_FLOORS_MPLUS]; ++ int ret; ++ ++ this->numchips = 0; ++ this->mfr = 0; ++ this->id = 0; ++ ++ /* Work out the intended interleave setting */ ++ this->interleave = 0; ++ if (this->ChipID == DOC_ChipID_DocMilPlus32) ++ this->interleave = 1; ++ ++ /* Check the ASIC agrees */ ++ if ( (this->interleave << 2) != ++ (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { ++ u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); ++ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", ++ this->interleave?"on (16-bit)":"off (8-bit)"); ++ conf ^= 4; ++ WriteDOC(this->virtadr, conf, Mplus_Configuration); ++ } ++ ++ /* For each floor, find the number of valid chips it contains */ ++ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { ++ numchips[floor] = 0; ++ for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { ++ ret = DoC_IdentChip(this, floor, chip); ++ if (ret) { ++ numchips[floor]++; ++ this->numchips++; ++ } ++ } ++ } ++ /* If there are none at all that we recognise, bail */ ++ if (!this->numchips) { ++ printk("No flash chips recognised.\n"); ++ return; ++ } ++ ++ /* Allocate an array to hold the information for each chip */ ++ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); ++ if (!this->chips){ ++ printk("MTD: No memory for allocating chip info structures\n"); ++ return; ++ } ++ ++ /* Fill out the chip array with {floor, chipno} for each ++ * detected chip in the device. */ ++ for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { ++ for (chip = 0 ; chip < numchips[floor] ; chip++) { ++ this->chips[ret].floor = floor; ++ this->chips[ret].chip = chip; ++ this->chips[ret].curadr = 0; ++ this->chips[ret].curmode = 0x50; ++ ret++; ++ } ++ } ++ ++ /* Calculate and print the total size of the device */ ++ this->totlen = this->numchips * (1 << this->chipshift); ++ printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", ++ this->numchips ,this->totlen >> 20); ++} ++ ++static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) ++{ ++ int tmp1, tmp2, retval; ++ ++ if (doc1->physadr == doc2->physadr) ++ return 1; ++ ++ /* Use the alias resolution register which was set aside for this ++ * purpose. If it's value is the same on both chips, they might ++ * be the same chip, and we write to one and check for a change in ++ * the other. It's unclear if this register is usuable in the ++ * DoC 2000 (it's in the Millennium docs), but it seems to work. */ ++ tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp1 != tmp2) ++ return 0; ++ ++ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp2 == (tmp1+1) % 0xff) ++ retval = 1; ++ else ++ retval = 0; ++ ++ /* Restore register contents. May not be necessary, but do it just to ++ * be safe. */ ++ WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); ++ ++ return retval; ++} ++ ++static const char im_name[] = "DoCMilPlus_init"; ++ ++/* This routine is made available to other mtd code via ++ * inter_module_register. It must only be accessed through ++ * inter_module_get which will bump the use count of this module. The ++ * addresses passed back in mtd are valid as long as the use count of ++ * this module is non-zero, i.e. between inter_module_get and ++ * inter_module_put. Keith Owens 29 Oct 2000. ++ */ ++static void DoCMilPlus_init(struct mtd_info *mtd) ++{ ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ struct DiskOnChip *old = NULL; ++ ++ /* We must avoid being called twice for the same device. */ ++ if (docmilpluslist) ++ old = (struct DiskOnChip *)docmilpluslist->priv; ++ ++ while (old) { ++ if (DoCMilPlus_is_alias(this, old)) { ++ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " ++ "Plus at 0x%lX - already configured\n", ++ this->physadr); ++ iounmap((void *)this->virtadr); ++ kfree(mtd); ++ return; ++ } ++ if (old->nextdoc) ++ old = (struct DiskOnChip *)old->nextdoc->priv; ++ else ++ old = NULL; ++ } ++ ++ mtd->name = "DiskOnChip Millennium Plus"; ++ printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " ++ "address 0x%lX\n", this->physadr); ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; ++ mtd->size = 0; ++ ++ mtd->erasesize = 0; ++ mtd->oobblock = 512; ++ mtd->oobsize = 16; ++ mtd->owner = THIS_MODULE; ++ mtd->erase = doc_erase; ++ mtd->point = NULL; ++ mtd->unpoint = NULL; ++ mtd->read = doc_read; ++ mtd->write = doc_write; ++ mtd->read_ecc = doc_read_ecc; ++ mtd->write_ecc = doc_write_ecc; ++ mtd->read_oob = doc_read_oob; ++ mtd->write_oob = doc_write_oob; ++ mtd->sync = NULL; ++ ++ this->totlen = 0; ++ this->numchips = 0; ++ this->curfloor = -1; ++ this->curchip = -1; ++ ++ /* Ident all the chips present. */ ++ DoC_ScanChips(this); ++ ++ if (!this->totlen) { ++ kfree(mtd); ++ iounmap((void *)this->virtadr); ++ } else { ++ this->nextdoc = docmilpluslist; ++ docmilpluslist = mtd; ++ mtd->size = this->totlen; ++ mtd->erasesize = this->erasesize; ++ add_mtd_device(mtd); ++ return; ++ } ++} ++ ++#if 0 ++static int doc_dumpblk(struct mtd_info *mtd, loff_t from) ++{ ++ int i; ++ loff_t fofs; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ unsigned char *bp, buf[1056]; ++ char c[32]; ++ ++ from &= ~0x3ff; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, 1054); ++ buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ memset(&c[0], 0, sizeof(c)); ++ printk("DUMP OFFSET=%x:\n", (int)from); ++ ++ for (i = 0, bp = &buf[0]; (i < 1056); i++) { ++ if ((i % 16) == 0) ++ printk("%08x: ", i); ++ printk(" %02x", *bp); ++ c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; ++ bp++; ++ if (((i + 1) % 16) == 0) ++ printk(" %s\n", c); ++ } ++ printk("\n"); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return 0; ++} ++#endif ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ /* Just a special case of doc_read_ecc */ ++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); ++} ++ ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int ret, i; ++ volatile char dummy; ++ loff_t fofs; ++ unsigned char syndrome[6]; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow a single read to cross a 512-byte block boundary */ ++ if (from + len > ((from | 0x1ff) + 1)) ++ len = ((from | 0x1ff) + 1) - from; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ if (eccbuf) { ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); ++ } else { ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ } ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ret = 0; ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ if (eccbuf) { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len); ++ ++ /* Read the ECC data following raw data */ ++ MemReadDOC(docptr, eccbuf, 4); ++ eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); ++ eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Flush the pipeline */ ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ ++ /* Check the ECC Status */ ++ if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { ++ int nb_errors; ++ /* There was an ECC error */ ++#ifdef ECC_DEBUG ++ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); ++#endif ++ /* Read the ECC syndrom through the DiskOnChip ECC logic. ++ These syndrome will be all ZERO when there is no error */ ++ for (i = 0; i < 6; i++) ++ syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ nb_errors = doc_decode_ecc(buf, syndrome); ++#ifdef ECC_DEBUG ++ printk("ECC Errors corrected: %x\n", nb_errors); ++#endif ++ if (nb_errors < 0) { ++ /* We return error, but have actually done the read. Not that ++ this can be told to user-space, via sys_read(), but at least ++ MTD-aware stuff can know about it by checking *retlen */ ++#ifdef ECC_DEBUG ++ printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", ++ __FILE__, __LINE__, (int)from); ++ printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ syndrome[0], syndrome[1], syndrome[2], ++ syndrome[3], syndrome[4], syndrome[5]); ++ printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ eccbuf[0], eccbuf[1], eccbuf[2], ++ eccbuf[3], eccbuf[4], eccbuf[5]); ++#endif ++ ret = -EIO; ++ } ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); ++ } else { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len-2); ++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return ret; ++} ++ ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ char eccbuf[6]; ++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); ++} ++ ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int i, before, ret = 0; ++ loff_t fto; ++ volatile char dummy; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[to >> (this->chipshift)]; ++ ++ /* Don't allow write past end of device */ ++ if (to >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow writes which aren't exactly one block (512 bytes) */ ++ if ((to & 0x1ff) || (len != 0x200)) ++ return -EINVAL; ++ ++ /* Determine position of OOB flags, before or after data */ ++ before = (this->interleave && (to & 0x200)); ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Set device to appropriate plane of flash */ ++ fto = to; ++ WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); ++ ++ /* On interleaved devices the flags for 2nd half 512 are before data */ ++ if (eccbuf && before) ++ fto -= 2; ++ ++ /* issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fto, 0x00, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ if (eccbuf) { ++ if (before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ } ++ ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); ++ } ++ ++ MemWriteDOC(docptr, (unsigned char *) buf, len); ++ ++ if (eccbuf) { ++ /* Write ECC data to flash, the ECC info is generated by ++ the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ ++ DoC_Delay(docptr, 3); ++ ++ /* Read the ECC data through the DiskOnChip ECC logic */ ++ for (i = 0; i < 6; i++) ++ eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ ++ /* Write the ECC data to flash */ ++ MemWriteDOC(docptr, eccbuf, 6); ++ ++ if (!before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+6); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+7); ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); ++ /* Error in programming ++ FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ++ return ret; ++} ++ ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ loff_t fofs, base; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ DoC_WaitReady(docptr); ++ ++ /* Maximum of 16 bytes in the OOB region, so limit read to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0xf; ++ if (!this->interleave) { ++ DoC_Command(docptr, NAND_CMD_READOOB, 0); ++ size = 16 - base; ++ } else if (base < 6) { ++ DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); ++ size = 6 - base; ++ } else if (base < 8) { ++ DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); ++ size = 8 - base; ++ } else { ++ DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue read command */ ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ MemReadDOC(docptr, &buf[got], size - 2); ++ buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return 0; ++} ++ ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ volatile char dummy; ++ loff_t fofs, base; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ int ret = 0; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ ++ /* Maximum of 16 bytes in the OOB region, so limit write to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0x0f; ++ if (!this->interleave) { ++ WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } else if (base < 6) { ++ WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 6 - base; ++ } else if (base < 8) { ++ WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 8 - base; ++ } else { ++ WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ /* Write the data via the internal pipeline through CDSN IO ++ register, see Pipelined Write Operations 11.2 */ ++ MemWriteDOC(docptr, (unsigned char *) &buf[got], size); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0x00); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming oob at 0x%x\n", ++ dummy, (int)ofs); ++ /* FIXME: implement Bad Block Replacement */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return ret; ++} ++ ++int doc_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ volatile char dummy; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ __u32 ofs = instr->addr; ++ __u32 len = instr->len; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ ++ DoC_CheckASIC(docptr); ++ ++ if (len != mtd->erasesize) ++ printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", ++ len, mtd->erasesize); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ instr->state = MTD_ERASE_PENDING; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ DoC_Command(docptr, NAND_CMD_RESET, 0x00); ++ DoC_WaitReady(docptr); ++ ++ DoC_Command(docptr, NAND_CMD_ERASE1, 0); ++ DoC_Address(this, 2, ofs, 0, 0x00); ++ DoC_Command(docptr, NAND_CMD_ERASE2, 0); ++ DoC_WaitReady(docptr); ++ instr->state = MTD_ERASING; ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5. */ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); ++ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ if (instr->callback) ++ instr->callback(instr); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ * Module stuff ++ * ++ ****************************************************************************/ ++ ++int __init init_doc2001plus(void) ++{ ++ inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); ++ return 0; ++} ++ ++static void __exit cleanup_doc2001plus(void) ++{ ++ struct mtd_info *mtd; ++ struct DiskOnChip *this; ++ ++ while ((mtd=docmilpluslist)) { ++ this = (struct DiskOnChip *)mtd->priv; ++ docmilpluslist = this->nextdoc; ++ ++ del_mtd_device(mtd); ++ ++ iounmap((void *)this->virtadr); ++ kfree(this->chips); ++ kfree(mtd); ++ } ++ inter_module_unregister(im_name); ++} ++ ++module_exit(cleanup_doc2001plus); ++module_init(init_doc2001plus); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer et al."); ++MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docecc.c linux/drivers/mtd/devices/docecc.c +--- linux-mips-2.4.27/drivers/mtd/devices/docecc.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/devices/docecc.c 2004-11-19 10:25:11.851207088 +0100 +@@ -7,7 +7,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: docecc.c,v 1.5 2003/05/21 15:15:06 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -519,6 +519,8 @@ + return nb_errors; + } + ++EXPORT_SYMBOL_GPL(doc_decode_ecc); ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Fabrice Bellard "); + MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docprobe.c linux/drivers/mtd/devices/docprobe.c +--- linux-mips-2.4.27/drivers/mtd/devices/docprobe.c 2003-06-16 01:42:21.000000000 +0200 ++++ linux/drivers/mtd/devices/docprobe.c 2004-11-19 10:25:11.853206784 +0100 +@@ -4,7 +4,7 @@ + /* (C) 1999 Machine Vision Holdings, Inc. */ + /* (C) 1999-2003 David Woodhouse */ + +-/* $Id: docprobe.c,v 1.33 2003/01/24 14:02:47 dwmw2 Exp $ */ ++/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */ + + + +@@ -31,14 +31,12 @@ + /* DOC_SINGLE_DRIVER: + Millennium driver has been merged into DOC2000 driver. + +- The newly-merged driver doesn't appear to work for writing. It's the +- same with the DiskOnChip 2000 and the Millennium. If you have a +- Millennium and you want write support to work, remove the definition +- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver. +- +- Otherwise, it's left on in the hope that it'll annoy someone with +- a Millennium enough that they go through and work out what the +- difference is :) ++ The old Millennium-only driver has been retained just in case there ++ are problems with the new code. If the combined driver doesn't work ++ for you, you can try the old one by undefining DOC_SINGLE_DRIVER ++ below and also enabling it in your configuration. If this fixes the ++ problems, please send a report to the MTD mailing list at ++ . + */ + #define DOC_SINGLE_DRIVER + +@@ -47,18 +45,15 @@ + #include + #include + #include +-#include +-#include +-#include + #include + #include +-#include + #include + #include + + #include + #include + #include ++#include + + /* Where to look for the devices? */ + #ifndef CONFIG_MTD_DOCPROBE_ADDRESS +@@ -92,17 +87,17 @@ + 0xff000000, + #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) + 0xff000000, +-#else ++##else + #warning Unknown architecture for DiskOnChip. No default probe locations defined + #endif +- 0 }; ++ 0xffffffff }; + + /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ + + static inline int __init doccheck(unsigned long potential, unsigned long physadr) + { + unsigned long window=potential; +- unsigned char tmp, ChipID; ++ unsigned char tmp, tmpb, tmpc, ChipID; + #ifndef DOC_PASSIVE_PROBE + unsigned char tmp2; + #endif +@@ -140,26 +135,80 @@ + window, DOCControl); + #endif /* !DOC_PASSIVE_PROBE */ + ++ /* We need to read the ChipID register four times. For some ++ newer DiskOnChip 2000 units, the first three reads will ++ return the DiskOnChip Millennium ident. Don't ask. */ + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; + break; + + case DOC_ChipID_DocMil: ++ /* Check for the new 2000 with Millennium ASIC */ ++ ReadDOC(window, ChipID); ++ ReadDOC(window, ChipID); ++ if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) ++ ChipID = DOC_ChipID_Doc2kTSOP; ++ + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) ++ return ChipID; ++ break; ++ ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ case 0: ++ /* Possible Millennium+, need to do more checks */ ++#ifndef DOC_PASSIVE_PROBE ++ /* Possibly release from power down mode */ ++ for (tmp = 0; (tmp < 4); tmp++) ++ ReadDOC(window, Mplus_Power); ++ ++ /* Reset the DiskOnChip ASIC */ ++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ ++ mdelay(1); ++ /* Enable the DiskOnChip ASIC */ ++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ mdelay(1); ++#endif /* !DOC_PASSIVE_PROBE */ ++ ++ ChipID = ReadDOC(window, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ /* Check the TOGGLE bit in the toggle register */ ++ tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; ++ default: + break; ++ } ++ /* FALL TRHU */ + + default: +-#ifndef CONFIG_MTD_DOCPROBE_55AA +- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ++ ++#ifdef CONFIG_MTD_DOCPROBE_55AA ++ printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + ChipID, physadr); + #endif + #ifndef DOC_PASSIVE_PROBE +@@ -200,6 +249,12 @@ + return; + + if ((ChipID = doccheck(docptr, physadr))) { ++ if (ChipID == DOC_ChipID_Doc2kTSOP) { ++ /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ ++ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); ++ iounmap((void *)docptr); ++ return; ++ } + docfound = 1; + mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); + +@@ -221,6 +276,12 @@ + sprintf(namebuf, "with ChipID %2.2X", ChipID); + + switch(ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ name="2000 TSOP"; ++ im_funcname = "DoC2k_init"; ++ im_modname = "doc2000"; ++ break; ++ + case DOC_ChipID_Doc2k: + name="2000"; + im_funcname = "DoC2k_init"; +@@ -237,6 +298,13 @@ + im_modname = "doc2001"; + #endif /* DOC_SINGLE_DRIVER */ + break; ++ ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ name="MillenniumPlus"; ++ im_funcname = "DoCMilPlus_init"; ++ im_modname = "doc2001plus"; ++ break; + } + + if (im_funcname) +@@ -248,6 +316,7 @@ + return; + } + printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); ++ kfree(mtd); + } + iounmap((void *)docptr); + } +@@ -267,7 +336,7 @@ + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + DoC_Probe(doc_config_location); + } else { +- for (i=0; doc_locations[i]; i++) { ++ for (i=0; (doc_locations[i] != 0xffffffff); i++) { + DoC_Probe(doc_locations[i]); + } + } +@@ -275,11 +344,7 @@ + found, so the user knows we at least tried. */ + if (!docfound) + printk(KERN_INFO "No recognised DiskOnChip devices found\n"); +- /* So it looks like we've been used and we get unloaded */ +- MOD_INC_USE_COUNT; +- MOD_DEC_USE_COUNT; +- return 0; +- ++ return -EAGAIN; + } + + module_init(init_doc); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/lart.c linux/drivers/mtd/devices/lart.c +--- linux-mips-2.4.27/drivers/mtd/devices/lart.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/devices/lart.c 2004-11-19 10:25:11.854206632 +0100 +@@ -2,7 +2,7 @@ + /* + * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. + * +- * $Id: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $ + * + * Author: Abraham vd Merwe + * +@@ -584,45 +584,40 @@ + + static struct mtd_info mtd; + +-static struct mtd_erase_region_info erase_regions[] = +-{ ++static struct mtd_erase_region_info erase_regions[] = { + /* parameter blocks */ + { +- offset: 0x00000000, +- erasesize: FLASH_BLOCKSIZE_PARAM, +- numblocks: FLASH_NUMBLOCKS_16m_PARAM ++ .offset = 0x00000000, ++ .erasesize = FLASH_BLOCKSIZE_PARAM, ++ .numblocks = FLASH_NUMBLOCKS_16m_PARAM, + }, + /* main blocks */ + { +- offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, +- erasesize: FLASH_BLOCKSIZE_MAIN, +- numblocks: FLASH_NUMBLOCKS_16m_MAIN ++ .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, ++ .erasesize = FLASH_BLOCKSIZE_MAIN, ++ .numblocks = FLASH_NUMBLOCKS_16m_MAIN, + } + }; + + #ifdef HAVE_PARTITIONS +-static struct mtd_partition lart_partitions[] = +-{ ++static struct mtd_partition lart_partitions[] = { + /* blob */ + { +- name: "blob", +- offset: BLOB_START, +- size: BLOB_LEN, +- mask_flags: 0 ++ .name = "blob", ++ .offset = BLOB_START, ++ .size = BLOB_LEN, + }, + /* kernel */ + { +- name: "kernel", +- offset: KERNEL_START, /* MTDPART_OFS_APPEND */ +- size: KERNEL_LEN, +- mask_flags: 0 ++ .name = "kernel", ++ .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ ++ .size = KERNEL_LEN, + }, + /* initial ramdisk / file system */ + { +- name: "file system", +- offset: INITRD_START, /* MTDPART_OFS_APPEND */ +- size: INITRD_LEN, /* MTDPART_SIZ_FULL */ +- mask_flags: 0 ++ .name = "file system", ++ .offset = INITRD_START, /* MTDPART_OFS_APPEND */ ++ .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ + } + }; + #endif +@@ -646,10 +641,10 @@ + mtd.erasesize = FLASH_BLOCKSIZE_MAIN; + mtd.numeraseregions = NB_OF (erase_regions); + mtd.eraseregions = erase_regions; +- mtd.module = THIS_MODULE; + mtd.erase = flash_erase; + mtd.read = flash_read; + mtd.write = flash_write; ++ mtd.owner = THIS_MODULE; + + #ifdef LART_DEBUG + printk (KERN_DEBUG +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c linux/drivers/mtd/devices/ms02-nv.c +--- linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c 2004-07-30 12:22:40.000000000 +0200 ++++ linux/drivers/mtd/devices/ms02-nv.c 2004-11-19 10:25:11.856206328 +0100 +@@ -6,7 +6,7 @@ + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * +- * $Id: ms02-nv.c,v 1.2 2003/01/24 14:05:17 dwmw2 Exp $ ++ * $Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $ + */ + + #include +@@ -31,7 +31,7 @@ + static char version[] __initdata = + "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; + +-MODULE_AUTHOR("Maciej W. Rozycki "); ++MODULE_AUTHOR("Maciej W. Rozycki "); + MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); + MODULE_LICENSE("GPL"); + +@@ -222,7 +222,7 @@ + mtd->flags = MTD_CAP_RAM | MTD_XIP; + mtd->size = fixsize; + mtd->name = (char *)ms02nv_name; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->read = ms02nv_read; + mtd->write = ms02nv_write; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c +--- linux-mips-2.4.27/drivers/mtd/devices/mtdram.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/mtdram.c 2004-11-19 10:25:11.859205872 +0100 +@@ -1,6 +1,6 @@ + /* + * mtdram - a test mtd device +- * $Id: mtdram.c,v 1.29 2002/10/21 13:40:06 jocke Exp $ ++ * $Id: mtdram.c,v 1.32 2003/05/21 15:15:07 dwmw2 Exp $ + * Author: Alexander Larsson + * + * Copyright (c) 1999 Alexander Larsson +@@ -13,6 +13,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -136,7 +138,7 @@ + mtd->erasesize = MTDRAM_ERASE_SIZE; + mtd->priv = mapped_address; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = ram_erase; + mtd->point = ram_point; + mtd->unpoint = ram_unpoint; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/phram.c linux/drivers/mtd/devices/phram.c +--- linux-mips-2.4.27/drivers/mtd/devices/phram.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/phram.c 2004-11-19 10:25:11.860205720 +0100 +@@ -0,0 +1,362 @@ ++/** ++ * ++ * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $ ++ * ++ * Copyright (c) Jochen Schaeuble ++ * 07/2003 rewritten by Joern Engel ++ * ++ * DISCLAIMER: This driver makes use of Rusty's excellent module code, ++ * so it will not work for 2.4 without changes and it wont work for 2.4 ++ * as a module without major changes. Oh well! ++ * ++ * Usage: ++ * ++ * one commend line parameter per device, each in the form: ++ * phram=,, ++ * may be up to 63 characters. ++ * and can be octal, decimal or hexadecimal. If followed ++ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or ++ * gigabytes. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) ++ ++struct phram_mtd_list { ++ struct list_head list; ++ struct mtd_info *mtdinfo; ++}; ++ ++static LIST_HEAD(phram_list); ++ ++ ++ ++int phram_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (instr->addr + instr->len > mtd->size) ++ return -EINVAL; ++ ++ memset(start + instr->addr, 0xff, instr->len); ++ ++ /* This'll catch a few races. Free the thing before returning :) ++ * I don't feel at all ashamed. This kind of thing is possible anyway ++ * with flash, but unlikely. ++ */ ++ ++ instr->state = MTD_ERASE_DONE; ++ ++ if (instr->callback) ++ (*(instr->callback))(instr); ++ else ++ kfree(instr); ++ ++ return 0; ++} ++ ++int phram_point(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char **mtdbuf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ *mtdbuf = start + from; ++ *retlen = len; ++ return 0; ++} ++ ++void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++{ ++} ++ ++int phram_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ memcpy(buf, start + from, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++int phram_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (to + len > mtd->size) ++ return -EINVAL; ++ ++ memcpy(start + to, buf, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++ ++ ++static void unregister_devices(void) ++{ ++ struct phram_mtd_list *this; ++ ++ list_for_each_entry(this, &phram_list, list) { ++ del_mtd_device(this->mtdinfo); ++ iounmap(this->mtdinfo->priv); ++ kfree(this->mtdinfo); ++ kfree(this); ++ } ++} ++ ++static int register_device(char *name, unsigned long start, unsigned long len) ++{ ++ struct phram_mtd_list *new; ++ int ret = -ENOMEM; ++ ++ new = kmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) ++ goto out0; ++ ++ new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!new->mtdinfo) ++ goto out1; ++ ++ memset(new->mtdinfo, 0, sizeof(struct mtd_info)); ++ ++ ret = -EIO; ++ new->mtdinfo->priv = ioremap(start, len); ++ if (!new->mtdinfo->priv) { ++ ERROR("ioremap failed\n"); ++ goto out2; ++ } ++ ++ ++ new->mtdinfo->name = name; ++ new->mtdinfo->size = len; ++ new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; ++ new->mtdinfo->erase = phram_erase; ++ new->mtdinfo->point = phram_point; ++ new->mtdinfo->unpoint = phram_unpoint; ++ new->mtdinfo->read = phram_read; ++ new->mtdinfo->write = phram_write; ++ new->mtdinfo->owner = THIS_MODULE; ++ new->mtdinfo->type = MTD_RAM; ++ new->mtdinfo->erasesize = 0x0; ++ ++ ret = -EAGAIN; ++ if (add_mtd_device(new->mtdinfo)) { ++ ERROR("Failed to register new device\n"); ++ goto out3; ++ } ++ ++ list_add_tail(&new->list, &phram_list); ++ return 0; ++ ++out3: ++ iounmap(new->mtdinfo->priv); ++out2: ++ kfree(new->mtdinfo); ++out1: ++ kfree(new); ++out0: ++ return ret; ++} ++ ++static int ustrtoul(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long result = simple_strtoul(cp, endp, base); ++ ++ switch (**endp) { ++ case 'G': ++ result *= 1024; ++ case 'M': ++ result *= 1024; ++ case 'k': ++ result *= 1024; ++ endp++; ++ } ++ return result; ++} ++ ++static int parse_num32(uint32_t *num32, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ ++ *num32 = n; ++ return 0; ++} ++ ++static int parse_name(char **pname, const char *token) ++{ ++ size_t len; ++ char *name; ++ ++ len = strlen(token) + 1; ++ if (len > 64) ++ return -ENOSPC; ++ ++ name = kmalloc(len, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ strcpy(name, token); ++ ++ *pname = name; ++ return 0; ++} ++ ++#define parse_err(fmt, args...) do { \ ++ ERROR(fmt , ## args); \ ++ return 0; \ ++} while (0) ++ ++static int phram_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[64+12+12], *str = buf; ++ char *token[3]; ++ char *name; ++ uint32_t start; ++ uint32_t len; ++ int i, ret; ++ ++ if (strnlen(val, sizeof(str)) >= sizeof(str)) ++ parse_err("parameter too long\n"); ++ ++ strcpy(str, val); ++ ++ for (i=0; i<3; i++) ++ token[i] = strsep(&str, ","); ++ ++ if (str) ++ parse_err("too many arguments\n"); ++ ++ if (!token[2]) ++ parse_err("not enough arguments\n"); ++ ++ ret = parse_name(&name, token[0]); ++ if (ret == -ENOMEM) ++ parse_err("out of memory\n"); ++ if (ret == -ENOSPC) ++ parse_err("name too long\n"); ++ if (ret) ++ return 0; ++ ++ ret = parse_num32(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address\n"); ++ ++ ret = parse_num32(&len, token[2]); ++ if (ret) ++ parse_err("illegal device length\n"); ++ ++ register_device(name, start, len); ++ ++ return 0; ++} ++ ++module_param_call(phram, phram_setup, NULL, NULL, 000); ++MODULE_PARM_DESC(phram, "Memory region to map. \"map=,\""); ++ ++/* ++ * Just for compatibility with slram, this is horrible and should go someday. ++ */ ++static int __init slram_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[256], *str = buf; ++ ++ if (!val || !val[0]) ++ parse_err("no arguments to \"slram=\"\n"); ++ ++ if (strnlen(val, sizeof(str)) >= sizeof(str)) ++ parse_err("parameter too long\n"); ++ ++ strcpy(str, val); ++ ++ while (str) { ++ char *token[3]; ++ char *name; ++ uint32_t start; ++ uint32_t len; ++ int i, ret; ++ ++ for (i=0; i<3; i++) { ++ token[i] = strsep(&str, ","); ++ if (token[i]) ++ continue; ++ parse_err("wrong number of arguments to \"slram=\"\n"); ++ } ++ ++ /* name */ ++ ret = parse_name(&name, token[0]); ++ if (ret == -ENOMEM) ++ parse_err("of memory\n"); ++ if (ret == -ENOSPC) ++ parse_err("too long\n"); ++ if (ret) ++ return 1; ++ ++ /* start */ ++ ret = parse_num32(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address\n"); ++ ++ /* len */ ++ if (token[2][0] == '+') ++ ret = parse_num32(&len, token[2] + 1); ++ else ++ ret = parse_num32(&len, token[2]); ++ ++ if (ret) ++ parse_err("illegal device length\n"); ++ ++ if (token[2][0] != '+') { ++ if (len < start) ++ parse_err("end < start\n"); ++ len -= start; ++ } ++ ++ register_device(name, start, len); ++ } ++ return 1; ++} ++ ++module_param_call(slram, slram_setup, NULL, NULL, 000); ++MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=,\""); ++ ++ ++int __init init_phram(void) ++{ ++ printk(KERN_ERR "phram loaded\n"); ++ return 0; ++} ++ ++static void __exit cleanup_phram(void) ++{ ++ unregister_devices(); ++} ++ ++module_init(init_phram); ++module_exit(cleanup_phram); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jörn Engel "); ++MODULE_DESCRIPTION("MTD driver for physical RAM"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c +--- linux-mips-2.4.27/drivers/mtd/devices/pmc551.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/pmc551.c 2004-11-19 10:25:11.862205416 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $ ++ * $Id: pmc551.c,v 1.25 2003/06/23 12:24:01 dwmw2 Exp $ + * + * PMC551 PCI Mezzanine Ram Device + * +@@ -681,11 +681,6 @@ + + printk(KERN_INFO PMC551_VERSION); + +- if(!pci_present()) { +- printk(KERN_NOTICE "pmc551: PCI not enabled.\n"); +- return -ENODEV; +- } +- + /* + * PCU-bus chipset probe. + */ +@@ -787,10 +782,10 @@ + mtd->write = pmc551_write; + mtd->point = pmc551_point; + mtd->unpoint = pmc551_unpoint; +- mtd->module = THIS_MODULE; + mtd->type = MTD_RAM; + mtd->name = "PMC551 RAM board"; + mtd->erasesize = 0x10000; ++ mtd->owner = THIS_MODULE; + + if (add_mtd_device(mtd)) { + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c +--- linux-mips-2.4.27/drivers/mtd/devices/slram.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/slram.c 2004-11-19 10:25:11.863205264 +0100 +@@ -1,6 +1,6 @@ + /*====================================================================== + +- $Id: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $ ++ $Id: slram.c,v 1.30 2003/05/20 21:03:08 dwmw2 Exp $ + + This driver provides a method to access memory not used by the kernel + itself (i.e. if the kernel commandline mem=xxx is used). To actually +@@ -199,7 +199,7 @@ + (*curmtd)->mtdinfo->unpoint = slram_unpoint; + (*curmtd)->mtdinfo->read = slram_read; + (*curmtd)->mtdinfo->write = slram_write; +- (*curmtd)->mtdinfo->module = THIS_MODULE; ++ (*curmtd)->mtdinfo->owner = THIS_MODULE; + (*curmtd)->mtdinfo->type = MTD_RAM; + (*curmtd)->mtdinfo->erasesize = 0x0; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/ftl.c linux/drivers/mtd/ftl.c +--- linux-mips-2.4.27/drivers/mtd/ftl.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/ftl.c 2004-11-19 10:25:11.630240680 +0100 +@@ -1,5 +1,5 @@ + /* This version ported to the Linux-MTD system by dwmw2@infradead.org +- * $Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $ ++ * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $ + * + * Fixes: Arnaldo Carvalho de Melo + * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups +@@ -55,8 +55,8 @@ + contact M-Systems (http://www.m-sys.com) directly. + + ======================================================================*/ ++#include + #include +-#include + #include + /*#define PSYCHO_DEBUG */ + +@@ -68,43 +68,13 @@ + #include + #include + #include +-#include ++#include + #include +- +-#if (LINUX_VERSION_CODE >= 0x20100) + #include +-#endif +-#if (LINUX_VERSION_CODE >= 0x20303) + #include +-#endif ++#include + + #include +-/*====================================================================*/ +-/* Stuff which really ought to be in compatmac.h */ +- +-#if (LINUX_VERSION_CODE < 0x20328) +-#define register_disk(dev, drive, minors, ops, size) \ +- do { (dev)->part[(drive)*(minors)].nr_sects = size; \ +- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ +- resetup_one_dev(dev, drive); } while (0) +-#endif +- +-#if (LINUX_VERSION_CODE < 0x20320) +-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +-#define blk_init_queue(q, req) q = (req) +-#define blk_cleanup_queue(q) q = NULL +-#define request_arg_t void +-#else +-#define request_arg_t request_queue_t *q +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif + + /*====================================================================*/ + +@@ -119,19 +89,6 @@ + #define FTL_MAJOR 44 + #endif + +-/* Funky stuff for setting up a block device */ +-#define MAJOR_NR FTL_MAJOR +-#define DEVICE_NAME "ftl" +-#define DEVICE_REQUEST do_ftl_request +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +- +-#define DEVICE_NR(minor) ((minor)>>5) +-#define REGION_NR(minor) (((minor)>>3)&3) +-#define PART_NR(minor) ((minor)&7) +-#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part)) +- +-#include + + /*====================================================================*/ + +@@ -142,8 +99,7 @@ + #define MAX_REGION 4 + + /* Maximum number of partitions in an FTL region */ +-#define PART_BITS 3 +-#define MAX_PART 8 ++#define PART_BITS 4 + + /* Maximum number of outstanding erase requests per socket */ + #define MAX_ERASE 8 +@@ -154,7 +110,7 @@ + + /* Each memory region corresponds to a minor device */ + typedef struct partition_t { +- struct mtd_info *mtd; ++ struct mtd_blktrans_dev mbd; + u_int32_t state; + u_int32_t *VirtualBlockMap; + u_int32_t *VirtualPageMap; +@@ -179,21 +135,10 @@ + region_info_t region; + memory_handle_t handle; + #endif +- atomic_t open; + } partition_t; + +-partition_t *myparts[MAX_MTD_DEVICES]; +- +-static void ftl_notify_add(struct mtd_info *mtd); +-static void ftl_notify_remove(struct mtd_info *mtd); +- + void ftl_freepart(partition_t *part); + +-static struct mtd_notifier ftl_notifier = { +- add: ftl_notify_add, +- remove: ftl_notify_remove, +-}; +- + /* Partition state flags */ + #define FTL_FORMATTED 0x01 + +@@ -204,51 +149,11 @@ + #define XFER_PREPARED 0x03 + #define XFER_FAILED 0x04 + +-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)]; +- +-static struct gendisk ftl_gendisk = { +- major: FTL_MAJOR, +- major_name: "ftl", +- minor_shift: PART_BITS, +- max_p: MAX_PART, +-#if (LINUX_VERSION_CODE < 0x20328) +- max_nr: MAX_DEV*MAX_PART, +-#endif +- part: ftl_hd, +- sizes: ftl_sizes, +-}; +- + /*====================================================================*/ + +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg); +-static int ftl_open(struct inode *inode, struct file *file); +-static release_t ftl_close(struct inode *inode, struct file *file); +-static int ftl_reread_partitions(int minor); + + static void ftl_erase_callback(struct erase_info *done); + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations ftl_blk_fops = { +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +- read: block_read, +- write: block_write, +- fsync: block_fsync +-}; +-#else +-static struct block_device_operations ftl_blk_fops = { +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +-}; +-#endif + + /*====================================================================== + +@@ -264,13 +169,13 @@ + loff_t offset, max_offset; + int ret; + part->header.FormattedSize = 0; +- max_offset = (0x100000mtd->size)?0x100000:part->mtd->size; ++ max_offset = (0x100000mbd.mtd->size)?0x100000:part->mbd.mtd->size; + /* Search first megabyte for a valid FTL header */ + for (offset = 0; + (offset + sizeof(header)) < max_offset; +- offset += part->mtd->erasesize ? : 0x2000) { ++ offset += part->mbd.mtd->erasesize ? : 0x2000) { + +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, + (unsigned char *)&header); + + if (ret) +@@ -283,15 +188,15 @@ + printk(KERN_NOTICE "ftl_cs: FTL header not found.\n"); + return -ENOENT; + } +- if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 || ++ if (header.BlockSize != 9 || + (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || + (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { + printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); + return -1; + } +- if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { ++ if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { + printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n", +- 1 << header.EraseUnitSize,part->mtd->erasesize); ++ 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); + return -1; + } + part->header = header; +@@ -326,7 +231,7 @@ + for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { + offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) + << part->header.EraseUnitSize); +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval, + (unsigned char *)&header); + + if (ret) +@@ -391,7 +296,7 @@ + part->EUNInfo[i].Deleted = 0; + offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), &retval, + (unsigned char *)part->bam_cache); + +@@ -456,7 +361,7 @@ + erase->len = 1 << part->header.EraseUnitSize; + erase->priv = (u_long)part; + +- ret = part->mtd->erase(part->mtd, erase); ++ ret = part->mbd.mtd->erase(part->mbd.mtd, erase); + + if (!ret) + xfer->EraseCount++; +@@ -523,7 +428,7 @@ + header.LogicalEUN = cpu_to_le16(0xffff); + header.EraseCount = cpu_to_le32(xfer->EraseCount); + +- ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header), + &retlen, (u_char *)&header); + + if (ret) { +@@ -539,7 +444,7 @@ + + for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&ctl); + + if (ret) +@@ -586,7 +491,7 @@ + + offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); + +@@ -604,7 +509,7 @@ + offset = xfer->Offset + 20; /* Bad! */ + unit = cpu_to_le16(0x7fff); + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), + &retlen, (u_char *) &unit); + + if (ret) { +@@ -624,7 +529,7 @@ + break; + case BLOCK_DATA: + case BLOCK_REPLACEMENT: +- ret = part->mtd->read(part->mtd, src, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n"); +@@ -632,7 +537,7 @@ + } + + +- ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE, ++ ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n"); +@@ -651,7 +556,7 @@ + } + + /* Write the BAM to the transfer unit */ +- ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(int32_t), &retlen, + (u_char *)part->bam_cache); + if (ret) { +@@ -661,7 +566,7 @@ + + + /* All clear? Then update the LogicalEUN again */ +- ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), + &retlen, (u_char *)&srcunitswap); + + if (ret) { +@@ -749,8 +654,8 @@ + if (queued) { + DEBUG(1, "ftl_cs: waiting for transfer " + "unit to be prepared...\n"); +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); ++ if (part->mbd.mtd->sync) ++ part->mbd.mtd->sync(part->mbd.mtd); + } else { + static int ne = 0; + if (++ne < 5) +@@ -848,7 +753,7 @@ + /* Invalidate cache */ + part->bam_index = 0xffff; + +- ret = part->mtd->read(part->mtd, ++ ret = part->mbd.mtd->read(part->mbd.mtd, + part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); +@@ -877,78 +782,6 @@ + + } /* find_free */ + +-/*====================================================================== +- +- This gets a memory handle for the region corresponding to the +- minor device number. +- +-======================================================================*/ +- +-static int ftl_open(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *partition; +- +- if (minor>>4 >= MAX_MTD_DEVICES) +- return -ENODEV; +- +- partition = myparts[minor>>4]; +- +- if (!partition) +- return -ENODEV; +- +- if (partition->state != FTL_FORMATTED) +- return -ENXIO; +- +- if (ftl_gendisk.part[minor].nr_sects == 0) +- return -ENXIO; +- +- BLK_INC_USE_COUNT; +- +- if (!get_mtd_device(partition->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { +- put_mtd_device(partition->mtd); +- BLK_DEC_USE_COUNT; +- return -EROFS; +- } +- +- DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); +- +- atomic_inc(&partition->open); +- +- return 0; +-} +- +-/*====================================================================*/ +- +-static release_t ftl_close(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *part = myparts[minor >> 4]; +- int i; +- +- DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); +- +- /* Wait for any pending erase operations to complete */ +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); +- +- for (i = 0; i < part->header.NumTransferUnits; i++) { +- if (part->XferInfo[i].state == XFER_ERASED) +- prepare_xfer(part, i); +- } +- +- atomic_dec(&part->open); +- +- put_mtd_device(part->mtd); +- BLK_DEC_USE_COUNT; +- release_return(0); +-} /* ftl_close */ +- + + /*====================================================================== + +@@ -983,7 +816,7 @@ + else { + offset = (part->EUNInfo[log_addr / bsize].Offset + + (log_addr % bsize)); +- ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE, + &retlen, (u_char *) buffer); + + if (ret) { +@@ -1022,7 +855,7 @@ + le32_to_cpu(part->header.BAMOffset)); + + #ifdef PSYCHO_DEBUG +- ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&old_addr); + if (ret) { + printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); +@@ -1059,7 +892,7 @@ + #endif + part->bam_cache[blk] = le_virt_addr; + } +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&le_virt_addr); + + if (ret) { +@@ -1119,7 +952,7 @@ + part->EUNInfo[part->bam_index].Deleted++; + offset = (part->EUNInfo[part->bam_index].Offset + + blk * SECTOR_SIZE); +- ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, + buffer); + + if (ret) { +@@ -1151,164 +984,32 @@ + return 0; + } /* ftl_write */ + +-/*====================================================================== +- +- IOCTL calls for getting device parameters. +- +-======================================================================*/ +- +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg) ++static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct hd_geometry *geo = (struct hd_geometry *)arg; +- int ret = 0, minor = MINOR(inode->i_rdev); +- partition_t *part= myparts[minor >> 4]; ++ partition_t *part = (void *)dev; + u_long sect; + +- if (!part) +- return -ENODEV; /* How? */ +- +- switch (cmd) { +- case HDIO_GETGEO: +- ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); +- if (ret) return ret; +- /* Sort of arbitrary: round size down to 4K boundary */ ++ /* Sort of arbitrary: round size down to 4KiB boundary */ + sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; +- put_user(1, (char *)&geo->heads); +- put_user(8, (char *)&geo->sectors); +- put_user((sect>>3), (short *)&geo->cylinders); +- put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); +- break; +- case BLKGETSIZE: +- ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); +- break; +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); +- break; +-#endif +- case BLKRRPART: +- ret = ftl_reread_partitions(minor); +- break; +-#if (LINUX_VERSION_CODE < 0x20303) +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- break; +- RO_IOCTLS(inode->i_rdev, arg); +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKFLSBUF: +- ret = blk_ioctl(inode->i_rdev, cmd, arg); +- break; +-#endif +- default: +- ret = -EINVAL; +- } +- +- return ret; +-} /* ftl_ioctl */ +- +-/*====================================================================== + +- Handler for block device requests ++ geo->heads = 1; ++ geo->sectors = 8; ++ geo->cylinders = sect >> 3; + +-======================================================================*/ +- +-static int ftl_reread_partitions(int minor) +-{ +- partition_t *part = myparts[minor >> 4]; +- int i, whole; +- +- DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); +- if ((atomic_read(&part->open) > 1)) { +- return -EBUSY; +- } +- whole = minor & ~(MAX_PART-1); +- +- i = MAX_PART - 1; +- while (i-- > 0) { +- if (ftl_hd[whole+i].nr_sects > 0) { +- kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); +- +- invalidate_device(rdev, 1); +- } +- ftl_hd[whole+i].start_sect = 0; +- ftl_hd[whole+i].nr_sects = 0; +- } +- +- scan_header(part); +- +- register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, +- &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); +- +-#ifdef PCMCIA_DEBUG +- for (i = 0; i < MAX_PART; i++) { +- if (ftl_hd[whole+i].nr_sects > 0) +- printk(KERN_INFO " %d: start %ld size %ld\n", i, +- ftl_hd[whole+i].start_sect, +- ftl_hd[whole+i].nr_sects); +- } +-#endif + return 0; + } + +-/*====================================================================== +- +- Handler for block device requests +- +-======================================================================*/ +- +-static void do_ftl_request(request_arg_t) ++static int ftl_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int ret, minor; +- partition_t *part; +- +- do { +- // sti(); +- INIT_REQUEST; +- +- minor = MINOR(CURRENT->rq_dev); +- +- part = myparts[minor >> 4]; +- if (part) { +- ret = 0; +- +- switch (CURRENT->cmd) { +- case READ: +- ret = ftl_read(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_read returned %d\n", ret); +- break; +- +- case WRITE: +- ret = ftl_write(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_write returned %d\n", ret); +- break; +- +- default: +- panic("ftl_cs: unknown block command!\n"); +- +- } +- } else { +- ret = 1; +- printk("NULL part in ftl_request\n"); +- } +- +- if (!ret) { +- CURRENT->sector += CURRENT->current_nr_sectors; +- } ++ return ftl_read((void *)dev, buf, block, 1); ++} + +- end_request((ret == 0) ? 1 : 0); +- } while (1); +-} /* do_ftl_request */ ++static int ftl_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ return ftl_write((void *)dev, buf, block, 1); ++} + + /*====================================================================*/ + +@@ -1337,19 +1038,9 @@ + + } /* ftl_freepart */ + +-static void ftl_notify_add(struct mtd_info *mtd) ++static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { + partition_t *partition; +- int device; +- +- for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) +- ; +- +- if (device == MAX_MTD_DEVICES) { +- printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" +- "Not scanning <%s>\n", mtd->name); +- return; +- } + + partition = kmalloc(sizeof(partition_t), GFP_KERNEL); + +@@ -1361,92 +1052,55 @@ + + memset(partition, 0, sizeof(partition_t)); + +- partition->mtd = mtd; ++ partition->mbd.mtd = mtd; + + if ((scan_header(partition) == 0) && + (build_maps(partition) == 0)) { + + partition->state = FTL_FORMATTED; +- atomic_set(&partition->open, 0); +- myparts[device] = partition; +- ftl_reread_partitions(device << 4); + #ifdef PCMCIA_DEBUG +- printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", ++ printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n", + le32_to_cpu(partition->header.FormattedSize) >> 10); + #endif ++ partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; ++ partition->mbd.blksize = SECTOR_SIZE; ++ partition->mbd.tr = tr; ++ partition->mbd.devnum = -1; ++ if (add_mtd_blktrans_dev((void *)partition)) ++ kfree(partition); ++ + } else + kfree(partition); + } + +-static void ftl_notify_remove(struct mtd_info *mtd) ++static void ftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i,j; +- +- /* Q: What happens if you try to remove a device which has +- * a currently-open FTL partition on it? +- * +- * A: You don't. The ftl_open routine is responsible for +- * increasing the use count of the driver module which +- * it uses. +- */ +- +- /* That's the theory, anyway :) */ +- +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (myparts[i] && myparts[i]->mtd == mtd) { +- +- if (myparts[i]->state == FTL_FORMATTED) +- ftl_freepart(myparts[i]); +- +- myparts[i]->state = 0; +- for (j=0; j<16; j++) { +- ftl_gendisk.part[j].nr_sects=0; +- ftl_gendisk.part[j].start_sect=0; +- } +- kfree(myparts[i]); +- myparts[i] = NULL; +- } ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); + } + ++struct mtd_blktrans_ops ftl_tr = { ++ .name = "ftl", ++ .major = FTL_MAJOR, ++ .part_bits = PART_BITS, ++ .readsect = ftl_readsect, ++ .writesect = ftl_writesect, ++ .getgeo = ftl_getgeo, ++ .add_mtd = ftl_add_mtd, ++ .remove_dev = ftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ + int init_ftl(void) + { +- int i; +- +- memset(myparts, 0, sizeof(myparts)); ++ DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n"); + +- DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n"); +- +- if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { +- printk(KERN_NOTICE "ftl_cs: unable to grab major " +- "device number!\n"); +- return -EAGAIN; +- } +- +- for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) +- ftl_blocksizes[i] = 1024; +- for (i = 0; i < MAX_DEV*MAX_PART; i++) { +- ftl_hd[i].nr_sects = 0; +- ftl_hd[i].start_sect = 0; +- } +- blksize_size[FTL_MAJOR] = ftl_blocksizes; +- ftl_gendisk.major = FTL_MAJOR; +- blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); +- add_gendisk(&ftl_gendisk); +- +- register_mtd_user(&ftl_notifier); +- +- return 0; ++ return register_mtd_blktrans(&ftl_tr); + } + + static void __exit cleanup_ftl(void) + { +- unregister_mtd_user(&ftl_notifier); +- +- unregister_blkdev(FTL_MAJOR, "ftl"); +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); +- blksize_size[FTL_MAJOR] = NULL; +- +- del_gendisk(&ftl_gendisk); ++ deregister_mtd_blktrans(&ftl_tr); + } + + module_init(init_ftl); +diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlcore.c linux/drivers/mtd/inftlcore.c +--- linux-mips-2.4.27/drivers/mtd/inftlcore.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/inftlcore.c 2004-11-19 10:25:11.632240376 +0100 +@@ -0,0 +1,900 @@ ++/* ++ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) ++ * ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlcore.c code which is: ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * Author: David Woodhouse ++ * ++ * $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Maximum number of loops while examining next block, to have a ++ * chance to detect consistency problems (they should never happen ++ * because of the checks done in the mounting. ++ */ ++#define MAX_LOOPS 10000 ++ ++extern void INFTL_dumptables(struct INFTLrecord *inftl); ++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); ++ ++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ++{ ++ struct INFTLrecord *inftl; ++ unsigned long temp; ++ ++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) ++ return; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); ++ ++ inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); ++ ++ if (!inftl) { ++ printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); ++ return; ++ } ++ memset(inftl, 0, sizeof(*inftl)); ++ ++ inftl->mbd.mtd = mtd; ++ inftl->mbd.devnum = -1; ++ inftl->mbd.blksize = 512; ++ inftl->mbd.tr = tr; ++ ++ if (INFTL_mount(inftl) < 0) { ++ printk(KERN_WARNING "INFTL: could not mount device\n"); ++ kfree(inftl); ++ return; ++ } ++ ++ /* OK, it's a new one. Set up all the data structures. */ ++ ++ /* Calculate geometry */ ++ inftl->cylinders = 1024; ++ inftl->heads = 16; ++ ++ temp = inftl->cylinders * inftl->heads; ++ inftl->sectors = inftl->mbd.size / temp; ++ if (inftl->mbd.size % temp) { ++ inftl->sectors++; ++ temp = inftl->cylinders * inftl->sectors; ++ inftl->heads = inftl->mbd.size / temp; ++ ++ if (inftl->mbd.size % temp) { ++ inftl->heads++; ++ temp = inftl->heads * inftl->sectors; ++ inftl->cylinders = inftl->mbd.size / temp; ++ } ++ } ++ ++ if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "INFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", inftl->mbd.size); ++ printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", ++ inftl->cylinders, inftl->heads , inftl->sectors, ++ (long)inftl->cylinders * (long)inftl->heads * ++ (long)inftl->sectors ); ++ } ++ ++ if (add_mtd_blktrans_dev(&inftl->mbd)) { ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++ return; ++ } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif ++ return; ++} ++ ++static void inftl_remove_dev(struct mtd_blktrans_dev *dev) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++} ++ ++/* ++ * Actual INFTL access routines. ++ */ ++ ++/* ++ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. ++ * This function is used when the give Virtual Unit Chain. ++ */ ++static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate) ++{ ++ u16 pot = inftl->LastFreeEUN; ++ int silly = inftl->nb_blocks; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=0x%x," ++ "desperate=%d)\n", (int)inftl, desperate); ++ ++ /* ++ * Normally, we force a fold to happen before we run out of free ++ * blocks completely. ++ */ ++ if (!desperate && inftl->numfreeEUNs < 2) { ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free " ++ "EUNs (%d)\n", inftl->numfreeEUNs); ++ return 0xffff; ++ } ++ ++ /* Scan for a free block */ ++ do { ++ if (inftl->PUtable[pot] == BLOCK_FREE) { ++ inftl->LastFreeEUN = pot; ++ return pot; ++ } ++ ++ if (++pot > inftl->lastEUN) ++ pot = 0; ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: no free blocks found! " ++ "EUN range = %d - %d\n", 0, inftl->LastFreeEUN); ++ return BLOCK_NIL; ++ } ++ } while (pot != inftl->LastFreeEUN); ++ ++ return BLOCK_NIL; ++} ++ ++static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) ++{ ++ u16 BlockMap[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, prevEUN, status; ++ int block, silly; ++ unsigned int targetEUN; ++ struct inftl_oob oob; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d," ++ "pending=%d)\n", (int)inftl, thisVUC, pendingblock); ++ ++ memset(BlockMap, 0xff, sizeof(BlockMap)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = targetEUN = inftl->VUtable[thisVUC]; ++ ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to fold non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ /* ++ * Scan to find the Erase Unit which holds the actual data for each ++ * 512-byte block within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { ++ if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 16 , &retlen, ++ (char *)&oob) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = oob.b.Status | oob.b.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockMap[block] = thisEUN; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: %x\n", ++ block, thisEUN, status); ++ break; ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* ++ * OK. We now know the location of every block in the Virtual Unit ++ * Chain, and the Erase Unit into which we are supposed to be copying. ++ * Go for it. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n", ++ thisVUC, targetEUN); ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { ++ unsigned char movebuf[SECTORSIZE]; ++ int ret; ++ ++ /* ++ * If it's in the target EUN already, or if it's pending write, ++ * do nothing. ++ */ ++ if (BlockMap[block] == targetEUN || (pendingblock == ++ (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { ++ continue; ++ } ++ ++ /* ++ * Copy only in non free block (free blocks can only ++ * happen in case of media errors or deleted blocks). ++ */ ++ if (BlockMap[block] == BLOCK_NIL) ++ continue; ++ ++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, ++ &retlen, movebuf, (char *)&oob, NULL); ++ if (ret < 0) { ++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), ++ SECTORSIZE, &retlen, movebuf, (char *)&oob, ++ NULL); ++ if (ret != -EIO) ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " ++ "away on retry?\n"); ++ } ++ MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + ++ (block * SECTORSIZE), SECTORSIZE, &retlen, ++ movebuf, (char *)&oob, NULL); ++ } ++ ++ /* ++ * Newest unit in chain now contains data from _all_ older units. ++ * So go through and erase each unit in chain, oldest first. (This ++ * is important, by doing oldest first if we crash/reboot then it ++ * it is relatively simple to clean up the mess). ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n", ++ thisVUC); ++ ++ for (;;) { ++ /* Find oldest unit in chain. */ ++ thisEUN = inftl->VUtable[thisVUC]; ++ prevEUN = BLOCK_NIL; ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ prevEUN = thisEUN; ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* Check if we are all done */ ++ if (thisEUN == targetEUN) ++ break; ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ * FixMe: Update Bad Unit Table on disk. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->PUtable[prevEUN] = BLOCK_NIL; ++ inftl->numfreeEUNs++; ++ } ++ } ++ ++ return targetEUN; ++} ++ ++u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock) ++{ ++ /* ++ * This is the part that needs some cleverness applied. ++ * For now, I'm doing the minimum applicable to actually ++ * get the thing to work. ++ * Wear-levelling and other clever stuff needs to be implemented ++ * and we also need to do some assessment of the results when ++ * the system loses power half-way through the routine. ++ */ ++ u16 LongestChain = 0; ++ u16 ChainLength = 0, thislen; ++ u16 chain, EUN; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=0x%x," ++ "pending=%d)\n", (int)inftl, pendingblock); ++ ++ for (chain = 0; chain < inftl->nb_blocks; chain++) { ++ EUN = inftl->VUtable[chain]; ++ thislen = 0; ++ ++ while (EUN <= inftl->lastEUN) { ++ thislen++; ++ EUN = inftl->PUtable[EUN]; ++ if (thislen > 0xff00) { ++ printk(KERN_WARNING "INFTL: endless loop in " ++ "Virtual Chain %d: Unit %x\n", ++ chain, EUN); ++ /* ++ * Actually, don't return failure. ++ * Just ignore this chain and get on with it. ++ */ ++ thislen = 0; ++ break; ++ } ++ } ++ ++ if (thislen > ChainLength) { ++ ChainLength = thislen; ++ LongestChain = chain; ++ } ++ } ++ ++ if (ChainLength < 2) { ++ printk(KERN_WARNING "INFTL: no Virtual Unit Chains available " ++ "for folding. Failing request\n"); ++ return BLOCK_NIL; ++ } ++ ++ return INFTL_foldchain(inftl, LongestChain, pendingblock); ++} ++ ++static int nrbits(unsigned int val, int bitcount) ++{ ++ int i, total = 0; ++ ++ for (i = 0; (i < bitcount); i++) ++ total += (((0x1 << i) & val) ? 1 : 0); ++ return total; ++} ++ ++/* ++ * INFTL_findwriteunit: Return the unit number into which we can write ++ * for this block. Make it available if it isn't already. ++ */ ++static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); ++ unsigned int thisEUN, writeEUN, prev_block, status; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); ++ struct inftl_oob oob; ++ struct inftl_bci bci; ++ unsigned char anac, nacs, parity; ++ size_t retlen; ++ int silly, silly2 = 3; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ do { ++ /* ++ * Scan the media to find a unit in the VUC which has ++ * a free space for the block in question. ++ */ ++ writeEUN = BLOCK_NIL; ++ thisEUN = inftl->VUtable[thisVUC]; ++ silly = MAX_LOOPS; ++ ++ while (thisEUN <= inftl->lastEUN) { ++ MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci); ++ ++ status = bci.Status | bci.Status1; ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " ++ "EUN %d is %x\n", block , writeEUN, status); ++ ++ switch(status) { ++ case SECTOR_FREE: ++ writeEUN = thisEUN; ++ break; ++ case SECTOR_DELETED: ++ case SECTOR_USED: ++ /* Can't go any further */ ++ goto hitused; ++ case SECTOR_IGNORE: ++ break; ++ default: ++ /* ++ * Invalid block. Don't use it any more. ++ * Must implement. ++ */ ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++ } ++ ++ /* Skip to next block in chain */ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++hitused: ++ if (writeEUN != BLOCK_NIL) ++ return writeEUN; ++ ++ ++ /* ++ * OK. We didn't find one in the existing chain, or there ++ * is no existing chain. Allocate a new one. ++ */ ++ writeEUN = INFTL_findfreeblock(inftl, 0); ++ ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * That didn't work - there were no free blocks just ++ * waiting to be picked up. We're going to have to fold ++ * a chain to make room. ++ */ ++ thisEUN = INFTL_makefreeblock(inftl, 0xffff); ++ ++ /* ++ * Hopefully we free something, lets try again. ++ * This time we are desperate... ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 " ++ "to find free EUN to accommodate write to " ++ "VUC %d\n", thisVUC); ++ writeEUN = INFTL_findfreeblock(inftl, 1); ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * Ouch. This should never happen - we should ++ * always be able to make some room somehow. ++ * If we get here, we've allocated more storage ++ * space than actual media, or our makefreeblock ++ * routine is missing something. ++ */ ++ printk(KERN_WARNING "INFTL: cannot make free " ++ "space.\n"); ++#ifdef DEBUG ++ INFTL_dumptables(inftl); ++ INFTL_dumpVUchains(inftl); ++#endif ++ return BLOCK_NIL; ++ } ++ } ++ ++ /* ++ * Insert new block into virtual chain. Firstly update the ++ * block headers in flash... ++ */ ++ anac = 0; ++ nacs = 0; ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN != BLOCK_NIL) { ++ MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize ++ + 8, 8, &retlen, (char *)&oob.u); ++ anac = oob.u.a.ANAC + 1; ++ nacs = oob.u.a.NACs + 1; ++ } ++ ++ prev_block = inftl->VUtable[thisVUC]; ++ if (prev_block < inftl->nb_blocks) ++ prev_block -= inftl->firstEUN; ++ ++ parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; ++ parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; ++ parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; ++ parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; ++ ++ oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.a.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.a.ANAC = anac; ++ oob.u.a.NACs = nacs; ++ oob.u.a.parityPerField = parity; ++ oob.u.a.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, ++ &retlen, (char *)&oob.u); ++ ++ /* Also back up header... */ ++ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.b.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.b.ANAC = anac; ++ oob.u.b.NACs = nacs; ++ oob.u.b.parityPerField = parity; ++ oob.u.b.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + ++ SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); ++ ++ inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; ++ inftl->VUtable[thisVUC] = writeEUN; ++ ++ inftl->numfreeEUNs--; ++ return writeEUN; ++ ++ } while (silly2--); ++ ++ printk(KERN_WARNING "INFTL: error folding to make room for Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++} ++ ++/* ++ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it. ++ */ ++static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) ++{ ++ unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, status; ++ int block, silly; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=0x%x," ++ "thisVUC=%d)\n", (int)inftl, thisVUC); ++ ++ memset(BlockUsed, 0, sizeof(BlockUsed)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to delete non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return; ++ } ++ ++ /* ++ * Scan through the Erase Units to determine whether any data is in ++ * each of the 512-byte blocks within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { ++ if (BlockUsed[block] || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 8 , &retlen, ++ (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockUsed[block] = 1; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) ++ if (BlockUsed[block]) ++ return; ++ ++ /* ++ * For each block in the chain free it and make it available ++ * for future use. Erase from the oldest unit first. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC); ++ ++ for (;;) { ++ u16 *prevEUN = &inftl->VUtable[thisVUC]; ++ thisEUN = *prevEUN; ++ ++ /* If the chain is all gone already, we're done */ ++ if (thisEUN == BLOCK_NIL) { ++ DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); ++ return; ++ } ++ ++ /* Find oldest unit in chain. */ ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ BUG_ON(thisEUN >= inftl->nb_blocks); ++ ++ prevEUN = &inftl->PUtable[thisEUN]; ++ thisEUN = *prevEUN; ++ } ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", ++ thisEUN, thisVUC); ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ * FixMe: Update Bad Unit Table on medium. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->numfreeEUNs++; ++ } ++ ++ /* Now sort out whatever was pointing to it... */ ++ *prevEUN = BLOCK_NIL; ++ ++ /* Ideally we'd actually be responsive to new ++ requests while we're doing this -- if there's ++ free space why should others be made to wait? */ ++ cond_resched(); ++ } ++ ++ inftl->VUtable[thisVUC] = BLOCK_NIL; ++} ++ ++static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ size_t retlen; ++ struct inftl_bci bci; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN != BLOCK_NIL) { ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ bci.Status = bci.Status1 = SECTOR_DELETED; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); ++ } ++ return 0; ++} ++ ++static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int writeEUN; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ size_t retlen; ++ u8 eccbuf[6]; ++ char *p, *pend; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d," ++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer); ++ ++ /* Is block all zero? */ ++ pend = buffer + SECTORSIZE; ++ for (p = buffer; p < pend && !*p; p++) ++ ; ++ ++ if (p < pend) { ++ writeEUN = INFTL_findwriteunit(inftl, block); ++ ++ if (writeEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "inftl_writeblock(): cannot find " ++ "block to write to\n"); ++ /* ++ * If we _still_ haven't got a block to use, ++ * we're screwed. ++ */ ++ return 1; ++ } ++ ++ MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + ++ blockofs, SECTORSIZE, &retlen, (char *)buffer, ++ (char *)eccbuf, NULL); ++ /* ++ * No need to write SECTOR_USED flags since they are written ++ * in mtd_writeecc ++ */ ++ } else { ++ INFTL_deleteblock(inftl, block); ++ } ++ ++ return 0; ++} ++ ++static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d," ++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %ld in EUN %d: 0x%04x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%lx\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN == BLOCK_NIL) { ++ /* The requested block is not on the media, return all 0x00 */ ++ memset(buffer, 0, SECTORSIZE); ++ } else { ++ size_t retlen; ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ u_char eccbuf[6]; ++ if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, ++ buffer, eccbuf, NULL)) ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ geo->heads = inftl->heads; ++ geo->sectors = inftl->sectors; ++ geo->cylinders = inftl->cylinders; ++ ++ return 0; ++} ++ ++struct mtd_blktrans_ops inftl_tr = { ++ .name = "inftl", ++ .major = INFTL_MAJOR, ++ .part_bits = INFTL_PARTN_BITS, ++ .getgeo = inftl_getgeo, ++ .readsect = inftl_readblock, ++ .writesect = inftl_writeblock, ++ .add_mtd = inftl_add_mtd, ++ .remove_dev = inftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ ++extern char inftlmountrev[]; ++ ++int __init init_inftl(void) ++{ ++ printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, " ++ "inftlmount.c %s\n", inftlmountrev); ++ ++ return register_mtd_blktrans(&inftl_tr); ++} ++ ++static void __exit cleanup_inftl(void) ++{ ++ deregister_mtd_blktrans(&inftl_tr); ++} ++ ++module_init(init_inftl); ++module_exit(cleanup_inftl); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer , David Woodhouse , Fabrice Bellard et al."); ++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlmount.c linux/drivers/mtd/inftlmount.c +--- linux-mips-2.4.27/drivers/mtd/inftlmount.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/inftlmount.c 2004-11-19 10:25:11.633240224 +0100 +@@ -0,0 +1,817 @@ ++/* ++ * inftlmount.c -- INFTL mount code with extensive checks. ++ * ++ * Author: Greg Ungerer (gerg@snapgear.com) ++ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlmount.c code which is: ++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) ++ * Copyright (C) 2000 Netgem S.A. ++ * ++ * $Id: inftlmount.c,v 1.12 2003/06/26 07:31:36 dwmw2 Exp $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char inftlmountrev[]="$Revision: 1.12 $"; ++ ++/* ++ * find_boot_record: Find the INFTL Media Header and its Spare copy which ++ * contains the various device information of the INFTL partition and ++ * Bad Unit Table. Update the PUtable[] table according to the Bad ++ * Unit Table. PUtable[] is used for management of Erase Unit in ++ * other routines in inftlcore.c and inftlmount.c. ++ */ ++static int find_boot_record(struct INFTLrecord *inftl) ++{ ++ struct inftl_unittail h1; ++ //struct inftl_oob oob; ++ unsigned int i, block, boot_record_count = 0; ++ u8 buf[SECTORSIZE]; ++ struct INFTLMediaHeader *mh = &inftl->MediaHdr; ++ struct INFTLPartition *ip; ++ int retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n", ++ (int)inftl); ++ ++ /* ++ * Assume logical EraseSize == physical erasesize for starting the ++ * scan. We'll sort it out later if we find a MediaHeader which says ++ * otherwise. ++ */ ++ inftl->EraseSize = inftl->mbd.mtd->erasesize; ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ ++ inftl->MediaUnit = BLOCK_NIL; ++ inftl->SpareMediaUnit = BLOCK_NIL; ++ ++ /* Search for a valid boot record */ ++ for (block = 0; block < inftl->nb_blocks; block++) { ++ int ret; ++ ++ /* ++ * Check for BNAND header first. Then whinge if it's found ++ * but later checks fail. ++ */ ++ if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, ++ SECTORSIZE, &retlen, buf))) { ++ static int warncount = 5; ++ ++ if (warncount) { ++ printk(KERN_WARNING "INFTL: block read at 0x%x " ++ "of mtd%d failed: %d\n", ++ block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ if (!--warncount) ++ printk(KERN_WARNING "INFTL: further " ++ "failures for this block will " ++ "not be printed\n"); ++ } ++ continue; ++ } ++ ++ if (retlen < 6 || memcmp(buf, "BNAND", 6)) { ++ /* BNAND\0 not found. Continue */ ++ continue; ++ } ++ ++ /* To be safer with BIOS, also use erase mark as discriminant */ ++ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + ++ SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { ++ printk(KERN_WARNING "INFTL: ANAND header found at " ++ "0x%x in mtd%d, but OOB data read failed " ++ "(err %d)\n", block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ continue; ++ } ++ ++ if (boot_record_count) { ++ /* ++ * We've already processed one. So we just check if ++ * this one is the same as the first one we found. ++ */ ++ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { ++ printk(KERN_WARNING "INFTL: Media Headers at " ++ "0x%x and 0x%x disagree.\n", ++ inftl->MediaUnit * inftl->EraseSize, ++ block * inftl->EraseSize); ++ return -1; ++ } ++ if (boot_record_count == 1) ++ inftl->SpareMediaUnit = block; ++ ++ /* ++ * Mark this boot record (INFTL MediaHeader) block as ++ * reserved. ++ */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ ++ boot_record_count++; ++ continue; ++ } ++ ++ /* ++ * This is the first we've seen. ++ * Copy the media header structure into place. ++ */ ++ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); ++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); ++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); ++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); ++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); ++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); ++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk("INFTL: Media Header ->\n" ++ " bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplerBits = %d\n" ++ " FormatFlgs = %d\n" ++ " OsakVersion = 0x%x\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, mh->NoOfBootImageBlocks, ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->BlockMultiplierBits, mh->FormatFlags, ++ mh->OsakVersion, mh->PercentUsed); ++ } ++#endif ++ ++ if (mh->NoOfBDTLPartitions == 0) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: NoOfBDTLPartitions (%d) == 0, " ++ "must be at least 1\n", mh->NoOfBDTLPartitions); ++ return -1; ++ } ++ ++ if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: Total Partitions (%d) > 4, " ++ "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->NoOfBinaryPartitions); ++ return -1; ++ } ++ ++ if (mh->BlockMultiplierBits > 1) { ++ printk(KERN_WARNING "INFTL: sorry, we don't support " ++ "UnitSizeFactor 0x%02x\n", ++ mh->BlockMultiplierBits); ++ return -1; ++ } else if (mh->BlockMultiplierBits == 1) { ++ printk(KERN_WARNING "INFTL: support for INFTL with " ++ "UnitSizeFactor 0x%02x is experimental\n", ++ mh->BlockMultiplierBits); ++ inftl->EraseSize = inftl->mbd.mtd->erasesize << ++ (0xff - mh->BlockMultiplierBits); ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ } ++ ++ /* Scan the partitions */ ++ for (i = 0; (i < 4); i++) { ++ ip = &mh->Partitions[i]; ++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits); ++ ip->firstUnit = le32_to_cpu(ip->firstUnit); ++ ip->lastUnit = le32_to_cpu(ip->lastUnit); ++ ip->flags = le32_to_cpu(ip->flags); ++ ip->spareUnits = le32_to_cpu(ip->spareUnits); ++ ip->Reserved0 = le32_to_cpu(ip->Reserved0); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk(" PARTITION[%d] ->\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ i, ip->virtualUnits, ip->firstUnit, ++ ip->lastUnit, ip->flags, ++ ip->spareUnits); ++ } ++#endif ++ ++ if (ip->Reserved0 != ip->firstUnit) { ++ struct erase_info *instr = &inftl->instr; ++ ++ /* ++ * Most likely this is using the ++ * undocumented qiuck mount feature. ++ * We don't support that, we will need ++ * to erase the hidden block for full ++ * compatibility. ++ */ ++ instr->addr = ip->Reserved0 * inftl->EraseSize; ++ instr->len = inftl->EraseSize; ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ } ++ if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed\n" ++ " firstUnit %d : lastUnit %d > " ++ "virtualUnits %d\n", i, ip->lastUnit, ++ ip->firstUnit, ip->Reserved0); ++ return -1; ++ } ++ if (ip->Reserved1 != 0) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed: " ++ "Reserved1 %d != 0\n", ++ i, ip->Reserved1); ++ return -1; ++ } ++ ++ if (ip->flags & INFTL_BDTL) ++ break; ++ } ++ ++ if (i >= 4) { ++ printk(KERN_WARNING "INFTL: Media Header Partition " ++ "sanity check failed:\n No partition " ++ "marked as Disk Partition\n"); ++ return -1; ++ } ++ ++ inftl->nb_boot_blocks = ip->firstUnit; ++ inftl->numvunits = ip->virtualUnits; ++ if (inftl->numvunits > (inftl->nb_blocks - ++ inftl->nb_boot_blocks - 2)) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed:\n numvunits (%d) > nb_blocks " ++ "(%d) - nb_boot_blocks(%d) - 2\n", ++ inftl->numvunits, inftl->nb_blocks, ++ inftl->nb_boot_blocks); ++ return -1; ++ } ++ ++ inftl->mbd.size = inftl->numvunits * ++ (inftl->EraseSize / SECTORSIZE); ++ ++ /* ++ * Block count is set to last used EUN (we won't need to keep ++ * any meta-data past that point). ++ */ ++ inftl->firstEUN = ip->firstUnit; ++ inftl->lastEUN = ip->lastUnit; ++ inftl->nb_blocks = ip->lastUnit + 1; ++ ++ /* Memory alloc */ ++ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->PUtable) { ++ printk(KERN_WARNING "INFTL: allocation of PUtable " ++ "failed (%d bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->VUtable) { ++ kfree(inftl->PUtable); ++ printk(KERN_WARNING "INFTL: allocation of VUtable " ++ "failed (%d bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ /* Mark the blocks before INFTL MediaHeader as reserved */ ++ for (i = 0; i < inftl->nb_boot_blocks; i++) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ /* Mark all remaining blocks as potentially containing data */ ++ for (; i < inftl->nb_blocks; i++) ++ inftl->PUtable[i] = BLOCK_NOTEXPLORED; ++ ++ /* Mark this boot record (NFTL MediaHeader) block as reserved */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ ++#if 0 ++ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ ++ for (i = 0; i < inftl->nb_blocks; i++) { ++ if ((i & (SECTORSIZE - 1)) == 0) { ++ /* read one sector for every SECTORSIZE of blocks */ ++ if ((ret = MTD_READECC(inftl->mbd.mtd, ++ block * inftl->EraseSize + i + SECTORSIZE, ++ SECTORSIZE, &retlen, buf, ++ (char *)&oob, NULL)) < 0) { ++ printk(KERN_WARNING "INFTL: read of " ++ "bad sector table failed " ++ "(err %d)\n", ret); ++ kfree(inftl->VUtable); ++ kfree(inftl->PUtable); ++ return -1; ++ } ++ } ++ /* Mark the Bad Erase Unit as RESERVED in PUtable */ ++ if (buf[i & (SECTORSIZE - 1)] != 0xff) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ } ++#endif ++ ++ inftl->MediaUnit = block; ++ boot_record_count++; ++ } ++ ++ return boot_record_count ? 0 : -1; ++} ++ ++static int memcmpb(void *a, int c, int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) { ++ if (c != ((unsigned char *)a)[i]) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * check_free_sector: check if a free sector is actually FREE, ++ * i.e. All 0xff in data and oob area. ++ */ ++static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, ++ int len, int check_oob) ++{ ++ int i, retlen; ++ u8 buf[SECTORSIZE]; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," ++ "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, ++ address, len, check_oob); ++ ++ for (i = 0; i < len; i += SECTORSIZE) { ++ /* ++ * We want to read the sector without ECC check here since a ++ * free sector does not have ECC syndrome on it yet. ++ */ ++ if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) ++ return -1; ++ if (memcmpb(buf, 0xff, SECTORSIZE) != 0) ++ return -1; ++ ++ if (check_oob) { ++ if (MTD_READOOB(inftl->mbd.mtd, address, ++ inftl->mbd.mtd->oobsize, &retlen, buf) < 0) ++ return -1; ++ if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0) ++ return -1; ++ } ++ address += SECTORSIZE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase ++ * Unit and Update INFTL metadata. Each erase operation is ++ * checked with check_free_sectors. ++ * ++ * Return: 0 when succeed, -1 on error. ++ * ++ * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? ++ * 2. UnitSizeFactor != 0xFF ++ */ ++int INFTL_formatblock(struct INFTLrecord *inftl, int block) ++{ ++ int retlen; ++ struct inftl_unittail uci; ++ struct erase_info *instr = &inftl->instr; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ memset(instr, 0, sizeof(struct erase_info)); ++ ++ /* FIXME: Shouldn't we be setting the 'discarded' flag to zero ++ _first_? */ ++ ++ /* Use async erase interface, test return code */ ++ instr->addr = block * inftl->EraseSize; ++ instr->len = inftl->EraseSize; ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ ++ if (instr->state == MTD_ERASE_FAILED) { ++ /* ++ * Could not format, FixMe: We should update the BadUnitTable ++ * both in memory and on disk. ++ */ ++ printk(KERN_WARNING "INFTL: error while formatting block %d\n", ++ block); ++ return -1; ++ } ++ ++ /* ++ * Check the "freeness" of Erase Unit before updating metadata. ++ * FixMe: is this check really necessary? Since we have check the ++ * return code after the erase operation. ++ */ ++ if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) ++ return -1; ++ ++ uci.EraseMark = cpu_to_le16(ERASE_MARK); ++ uci.EraseMark1 = cpu_to_le16(ERASE_MARK); ++ uci.Reserved[0] = 0; ++ uci.Reserved[1] = 0; ++ uci.Reserved[2] = 0; ++ uci.Reserved[3] = 0; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + ++ 8, 8, &retlen, (char *)&uci) < 0) ++ return -1; ++ return 0; ++} ++ ++/* ++ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase ++ * Units in a Virtual Unit Chain, i.e. all the units are disconnected. ++ * ++ * Since the chain is invalid then we will have to erase it from its ++ * head (normally for INFTL we go from the oldest). But if it has a ++ * loop then there is no oldest... ++ */ ++static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) ++{ ++ unsigned int block = first_block, block1; ++ ++ printk(KERN_WARNING "INFTL: formatting chain at block %d\n", ++ first_block); ++ ++ for (;;) { ++ block1 = inftl->PUtable[block]; ++ ++ printk(KERN_WARNING "INFTL: formatting block %d\n", block); ++ if (INFTL_formatblock(inftl, block) < 0) { ++ /* ++ * Cannot format !!!! Mark it as Bad Unit, ++ * FixMe: update the BadUnitTable on disk. ++ */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ } else { ++ inftl->PUtable[block] = BLOCK_FREE; ++ } ++ ++ /* Goto next block on the chain */ ++ block = block1; ++ ++ if (block == BLOCK_NIL || block >= inftl->lastEUN) ++ break; ++ } ++} ++ ++void INFTL_dumptables(struct INFTLrecord *s) ++{ ++ int i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("VUtable[%d] ->", s->nb_blocks); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->VUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks); ++ for (i = 0; i <= s->lastEUN; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->PUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL ->\n" ++ " EraseSize = %d\n" ++ " h/s/c = %d/%d/%d\n" ++ " numvunits = %d\n" ++ " firstEUN = %d\n" ++ " lastEUN = %d\n" ++ " numfreeEUNs = %d\n" ++ " LastFreeEUN = %d\n" ++ " nb_blocks = %d\n" ++ " nb_boot_blocks = %d", ++ s->EraseSize, s->heads, s->sectors, s->cylinders, ++ s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs, ++ s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks); ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++void INFTL_dumpVUchains(struct INFTLrecord *s) ++{ ++ int logical, block, i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL Virtual Unit Chains:\n"); ++ for (logical = 0; logical < s->nb_blocks; logical++) { ++ block = s->VUtable[logical]; ++ if (block > s->nb_blocks) ++ continue; ++ printk(" LOGICAL %d --> %d ", logical, block); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ block = s->PUtable[block]; ++ printk("%d ", block); ++ } ++ printk("\n"); ++ } ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++int INFTL_mount(struct INFTLrecord *s) ++{ ++ unsigned int block, first_block, prev_block, last_block; ++ unsigned int first_logical_block, logical_block, erase_mark; ++ int chain_length, do_format_chain; ++ struct inftl_unithead1 h0; ++ struct inftl_unittail h1; ++ int i, retlen; ++ u8 *ANACtable, ANAC; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s); ++ ++ /* Search for INFTL MediaHeader and Spare INFTL Media Header */ ++ if (find_boot_record(s) < 0) { ++ printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); ++ return -1; ++ } ++ ++ /* Init the logical to physical table */ ++ for (i = 0; i < s->nb_blocks; i++) ++ s->VUtable[i] = BLOCK_NIL; ++ ++ logical_block = block = BLOCK_NIL; ++ ++ /* Temporary buffer to store ANAC numbers. */ ++ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); ++ memset(ANACtable, 0, s->nb_blocks); ++ ++ /* ++ * First pass is to explore each physical unit, and construct the ++ * virtual chains that exist (newest physical unit goes into VUtable). ++ * Any block that is in any way invalid will be left in the ++ * NOTEXPLORED state. Then at the end we will try to format it and ++ * mark it as free. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n"); ++ for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) { ++ if (s->PUtable[first_block] != BLOCK_NOTEXPLORED) ++ continue; ++ ++ do_format_chain = 0; ++ first_logical_block = BLOCK_NIL; ++ last_block = BLOCK_NIL; ++ block = first_block; ++ ++ for (chain_length = 0; ; chain_length++) { ++ ++ if ((chain_length == 0) && ++ (s->PUtable[block] != BLOCK_NOTEXPLORED)) { ++ /* Nothing to do here, onto next block */ ++ break; ++ } ++ ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, ++ 8, &retlen, (char *)&h0) < 0 || ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + ++ 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { ++ /* Should never happen? */ ++ do_format_chain++; ++ break; ++ } ++ ++ logical_block = le16_to_cpu(h0.virtualUnitNo); ++ prev_block = le16_to_cpu(h0.prevUnitNo); ++ erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1)); ++ ANACtable[block] = h0.ANAC; ++ ++ /* Previous block is relative to start of Partition */ ++ if (prev_block < s->nb_blocks) ++ prev_block += s->firstEUN; ++ ++ /* Already explored partial chain? */ ++ if (s->PUtable[block] != BLOCK_NOTEXPLORED) { ++ /* Check if chain for this logical */ ++ if (logical_block == first_logical_block) { ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ } ++ break; ++ } ++ ++ /* Check for invalid block */ ++ if (erase_mark != ERASE_MARK) { ++ printk(KERN_WARNING "INFTL: corrupt block %d " ++ "in chain %d, chain length %d, erase " ++ "mark 0x%x?\n", block, first_block, ++ chain_length, erase_mark); ++ /* ++ * Assume end of chain, probably incomplete ++ * fold/erase... ++ */ ++ if (chain_length == 0) ++ do_format_chain++; ++ break; ++ } ++ ++ /* Check for it being free already then... */ ++ if ((logical_block == BLOCK_FREE) || ++ (logical_block == BLOCK_NIL)) { ++ s->PUtable[block] = BLOCK_FREE; ++ break; ++ } ++ ++ /* Sanity checks on block numbers */ ++ if ((logical_block >= s->nb_blocks) || ++ ((prev_block >= s->nb_blocks) && ++ (prev_block != BLOCK_NIL))) { ++ if (chain_length > 0) { ++ printk(KERN_WARNING "INFTL: corrupt " ++ "block %d in chain %d?\n", ++ block, first_block); ++ do_format_chain++; ++ } ++ break; ++ } ++ ++ if (first_logical_block == BLOCK_NIL) { ++ first_logical_block = logical_block; ++ } else { ++ if (first_logical_block != logical_block) { ++ /* Normal for folded chain... */ ++ break; ++ } ++ } ++ ++ /* ++ * Current block is valid, so if we followed a virtual ++ * chain to get here then we can set the previous ++ * block pointer in our PUtable now. Then move onto ++ * the previous block in the chain. ++ */ ++ s->PUtable[block] = BLOCK_NIL; ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ last_block = block; ++ block = prev_block; ++ ++ /* Check for end of chain */ ++ if (block == BLOCK_NIL) ++ break; ++ ++ /* Validate next block before following it... */ ++ if (block > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid previous " ++ "block %d in chain %d?\n", block, ++ first_block); ++ do_format_chain++; ++ break; ++ } ++ } ++ ++ if (do_format_chain) { ++ format_chain(s, first_block); ++ continue; ++ } ++ ++ /* ++ * Looks like a valid chain then. It may not really be the ++ * newest block in the chain, but it is the newest we have ++ * found so far. We might update it in later iterations of ++ * this loop if we find something newer. ++ */ ++ s->VUtable[first_logical_block] = first_block; ++ logical_block = BLOCK_NIL; ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++#endif ++ ++ /* ++ * Second pass, check for infinite loops in chains. These are ++ * possible because we don't update the previous pointers when ++ * we fold chains. No big deal, just fix them up in PUtable. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n"); ++ for (logical_block = 0; logical_block < s->numvunits; logical_block++) { ++ block = s->VUtable[logical_block]; ++ last_block = BLOCK_NIL; ++ ++ /* Check for free/reserved/nil */ ++ if (block >= BLOCK_RESERVED) ++ continue; ++ ++ ANAC = ANACtable[block]; ++ for (i = 0; i < s->numvunits; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ if (s->PUtable[block] > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid prev %d, " ++ "in virtual chain %d\n", ++ s->PUtable[block], logical_block); ++ s->PUtable[block] = BLOCK_NIL; ++ ++ } ++ if (ANACtable[block] != ANAC) { ++ /* ++ * Chain must point back to itself. This is ok, ++ * but we will need adjust the tables with this ++ * newest block and oldest block. ++ */ ++ s->VUtable[logical_block] = block; ++ s->PUtable[last_block] = BLOCK_NIL; ++ break; ++ } ++ ++ ANAC--; ++ last_block = block; ++ block = s->PUtable[block]; ++ } ++ ++ if (i >= s->nb_blocks) { ++ /* ++ * Uhoo, infinite chain with valid ANACS! ++ * Format whole chain... ++ */ ++ format_chain(s, first_block); ++ } ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumpVUchains(s); ++#endif ++ ++ /* ++ * Third pass, format unreferenced blocks and init free block count. ++ */ ++ s->numfreeEUNs = 0; ++ s->LastFreeEUN = BLOCK_NIL; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n"); ++ for (block = s->firstEUN; block <= s->lastEUN; block++) { ++ if (s->PUtable[block] == BLOCK_NOTEXPLORED) { ++ printk("INFTL: unreferenced block %d, formatting it\n", ++ block); ++ if (INFTL_formatblock(s, block) < 0) ++ s->PUtable[block] = BLOCK_RESERVED; ++ else ++ s->PUtable[block] = BLOCK_FREE; ++ } ++ if (s->PUtable[block] == BLOCK_FREE) { ++ s->numfreeEUNs++; ++ if (s->LastFreeEUN == BLOCK_NIL) ++ s->LastFreeEUN = block; ++ } ++ } ++ ++ kfree(ANACtable); ++ return 0; ++} +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in +--- linux-mips-2.4.27/drivers/mtd/maps/Config.in 2004-02-26 01:46:35.000000000 +0100 ++++ linux/drivers/mtd/maps/Config.in 2004-11-19 10:25:11.888201464 +0100 +@@ -1,17 +1,14 @@ + # drivers/mtd/maps/Config.in + +-# $Id: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $ ++# $Id: Config.in,v 1.63 2003/11/26 21:26:09 dsaxena Exp $ + + mainmenu_option next_comment + + comment 'Mapping drivers for chip access' + +-dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE +-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then +- hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 +- hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 +- int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 +-fi ++bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS ++ ++bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE + + if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then + dep_tristate ' Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI +@@ -21,56 +18,68 @@ + dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS ++ dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT + if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then + hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000 + fi +- dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC ++ dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI + dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE + dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE +- dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE ++ dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS + dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE + fi + +-if [ "$CONFIG_PPC" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL ++if [ "$CONFIG_PPC32" = "y" ]; then ++ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then ++ dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE ++ fi ++ if [ "$CONFIG_8xx" = "y" ]; then ++ if [ "$CONFIG_TQM8xxL" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then + dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_MBX" = "y" ]; then + dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_DBOX2" = "y" ]; then + dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI ++ fi + dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI +- dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_4xx" = "y" ]; then ++ if [ "$CONFIG_40x" = "y" ]; then ++ if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH ++ dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2 ++ fi ++ if [ "$CONFIG_440" = "y" ]; then ++ dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY ++ fi ++ fi + fi + +-if [ "$CONFIG_MIPS" = "y" ]; then +- dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000 +- dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500 +- dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100 +- dep_tristate ' Bosporus MTD support' CONFIG_MTD_BOSPORUS $CONFIG_MIPS_BOSPORUS +- dep_tristate ' XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500 +- dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1 +- if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \ +- -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then +- bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT +- bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER ++if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then ++ if [ "$CONFIG_MIPS_PB1000" = "y" -o "$CONFIG_MIPS_PB1100" = "y" -o "$CONFIG_MIPS_PB1500" = "y" ]; then ++ tristate ' Pb1x00 MTD support' CONFIG_MTD_PB1XXX ++ if [ "$CONFIG_MIPS_PB1500" = "y" -o "$CONFIG_MIPS_PB1100" = "m" ]; then ++ bool ' Pb1x00 boot flash device' CONFIG_MTD_PB1500_BOOT ++ bool ' Pb1x00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER ++ fi + fi + tristate ' Db1x00 MTD support' CONFIG_MTD_DB1X00 + if [ "$CONFIG_MTD_DB1X00" = "y" -o "$CONFIG_MTD_DB1X00" = "m" ]; then + bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT + bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER + fi +- tristate ' Pb1550 MTD support' CONFIG_MTD_PB1550 +- if [ "$CONFIG_MTD_PB1550" = "y" -o "$CONFIG_MTD_PB1550" = "m" ]; then +- bool ' Pb1550 Boot Flash' CONFIG_MTD_PB1550_BOOT +- bool ' Pb1550 User Parameter Flash' CONFIG_MTD_PB1550_USER +- fi +- dep_tristate ' Hydrogen 3 MTD support' CONFIG_MTD_HYDROGEN3 $CONFIG_MIPS_HYDROGEN3 +- dep_tristate ' Mirage MTD support' CONFIG_MTD_MIRAGE $CONFIG_MIPS_MIRAGE + dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS + if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000 +@@ -78,7 +87,7 @@ + int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2 + fi + dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT +- dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT ++ dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT + fi + + if [ "$CONFIG_SUPERH" = "y" ]; then +@@ -90,21 +99,24 @@ + fi + + if [ "$CONFIG_ARM" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI + dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 + dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE ++ dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 ++ dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK ++ dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI ++ dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA ++ dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO + fi + if [ "$CONFIG_ALPHA" = "y" ]; then +- dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE ++ dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS + fi + + if [ "$CONFIG_UCLINUX" = "y" ]; then +@@ -112,7 +124,7 @@ + fi + + # This needs CFI or JEDEC, depending on the cards found. +-dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI +-dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA ++dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS ++dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS + + endmenu +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile +--- linux-mips-2.4.27/drivers/mtd/maps/Makefile 2004-02-26 01:46:35.000000000 +0100 ++++ linux/drivers/mtd/maps/Makefile 2004-11-19 10:25:11.890201160 +0100 +@@ -1,12 +1,16 @@ + # + # linux/drivers/maps/Makefile + # +-# $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $ ++# $Id: Makefile.common,v 1.8 2003/11/26 21:26:09 dsaxena Exp $ + +-BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) +- +-ifeq ($(BELOW25),y) ++ifeq ($(PATCHLEVEL),4) + O_TARGET := mapslink.o ++export-objs := map_funcs.o ++endif ++ ++ ++ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) ++obj-$(CONFIG_MTD) += map_funcs.o + endif + + # Chip mappings +@@ -21,19 +25,13 @@ + obj-$(CONFIG_MTD_IQ80310) += iq80310.o + obj-$(CONFIG_MTD_L440GX) += l440gx.o + obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o ++obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o + obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o ++obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o + obj-$(CONFIG_MTD_MBX860) += mbx860.o +-obj-$(CONFIG_MTD_NORA) += nora.o + obj-$(CONFIG_MTD_CEIVA) += ceiva.o + obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o +-ifneq ($(CONFIG_MTD_PHYSMAP),n) +- ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) +- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o +- else +- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +- endif +-endif ++obj-$(CONFIG_MTD_PHYSMAP) += physmap.o + obj-$(CONFIG_MTD_PNC2000) += pnc2000.o + obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o + obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o +@@ -49,16 +47,9 @@ + obj-$(CONFIG_MTD_OCELOT) += ocelot.o + obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o + obj-$(CONFIG_MTD_PCI) += pci.o +-obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_XXS1500) += xxs1500.o +-obj-$(CONFIG_MTD_MTX1) += mtx-1.o +-obj-$(CONFIG_MTD_LASAT) += lasat.o ++obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o + obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o +-obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o +-obj-$(CONFIG_MTD_HYDROGEN3) += hydrogen3-flash.o +-obj-$(CONFIG_MTD_BOSPORUS) += pb1xxx-flash.o ++obj-$(CONFIG_MTD_LASAT) += lasat.o + obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o + obj-$(CONFIG_MTD_EDB7312) += edb7312.o + obj-$(CONFIG_MTD_IMPA7) += impa7.o +@@ -67,6 +58,13 @@ + obj-$(CONFIG_MTD_UCLINUX) += uclinux.o + obj-$(CONFIG_MTD_NETtel) += nettel.o + obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o +-obj-$(CONFIG_MTD_MIRAGE) += mirage-flash.o ++obj-$(CONFIG_MTD_EBONY) += ebony.o ++obj-$(CONFIG_MTD_BEECH) += beech-mtd.o ++obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o ++obj-$(CONFIG_MTD_H720X) += h720x-flash.o ++obj-$(CONFIG_MTD_SBC8240) += sbc8240.o ++obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o ++obj-$(CONFIG_MTD_MPC1211) += mpc1211.o ++obj-$(CONFIG_MTD_IXP425) += ixp425.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c linux/drivers/mtd/maps/amd76xrom.c +--- linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/amd76xrom.c 2004-11-19 10:25:11.891201008 +0100 +@@ -2,12 +2,13 @@ + * amd76xrom.c + * + * Normal mappings of chips in physical memory +- * $Id: amd76xrom.c,v 1.1 2002/10/18 22:45:48 eric Exp $ ++ * $Id: amd76xrom.c,v 1.9 2003/10/23 23:10:59 thayne Exp $ + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -16,77 +17,59 @@ + #include + + ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define MTD_DEV_NAME_LENGTH 16 ++ + struct amd76xrom_map_info { + struct map_info map; + struct mtd_info *mtd; + unsigned long window_addr; + u32 window_start, window_size; + struct pci_dev *pdev; ++ struct resource window_rsrc; ++ struct resource rom_rsrc; ++ char mtd_name[MTD_DEV_NAME_LENGTH]; + }; + +-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} + +-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} ++static struct amd76xrom_map_info amd76xrom_map = { ++ .map = { ++ .name = MOD_NAME, ++ .size = 0, ++ .buswidth = 1, ++ } ++ /* remaining fields of structure are initialized to 0 */ ++}; + +-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} + +-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr) ++static void amd76xrom_cleanup(struct amd76xrom_map_info *info) + { +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} ++ u8 byte; + +-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++ /* Disable writes through the rom window */ ++ pci_read_config_byte(info->pdev, 0x40, &byte); ++ pci_write_config_byte(info->pdev, 0x40, byte & ~1); + +-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++ if (info->mtd) { ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = NULL; ++ info->map.virt = 0; ++ } ++ if (info->rom_rsrc.parent) ++ release_resource(&info->rom_rsrc); ++ if (info->window_rsrc.parent) ++ release_resource(&info->window_rsrc); + +-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (info->window_addr) { ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ } + } + +-static struct amd76xrom_map_info amd76xrom_map = { +- map: { +- name: "AMD76X rom", +- size: 0, +- buswidth: 1, +- read8: amd76xrom_read8, +- read16: amd76xrom_read16, +- read32: amd76xrom_read32, +- copy_from: amd76xrom_copy_from, +- write8: amd76xrom_write8, +- write16: amd76xrom_write16, +- write32: amd76xrom_write32, +- copy_to: amd76xrom_copy_to, +- /* The standard rom socket is for single power supply chips +- * that don't have an extra vpp. +- */ +- }, +- mtd: 0, +- window_addr: 0, +-}; + + static int __devinit amd76xrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +@@ -97,6 +80,10 @@ + u8 segen_bits; + }; + static struct rom_window rom_window[] = { ++ /* ++ * Need the 5MiB window for chips that have block lock/unlock ++ * registers located below 4MiB window. ++ */ + { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, + { 0xffc00000, 4*1024*1024, (1<<7), }, + { 0xffff0000, 64*1024, 0 }, +@@ -112,19 +99,29 @@ + int i; + u32 rom_size; + ++ info->pdev = pdev; + window = &rom_window[0]; +-#if 0 +- while(window->size) { +- if (request_mem_region(window->start, window->size, "amd76xrom")) { +- break; +- } +- window++; +- } +- if (!window->size) { +- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n"); +- goto err_out_none; ++ ++ while (window->size) { ++ /* ++ * Try to reserve the window mem region. If this fails then ++ * it is likely due to a fragment of the window being ++ * "reseved" by the BIOS. In the case that the ++ * request_mem_region() fails then once the rom size is ++ * discovered we will try to reserve the unreserved fragment. ++ */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = window->start; ++ info->window_rsrc.end = window->start + window->size - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ info->window_rsrc.parent = NULL; ++ printk(KERN_ERR MOD_NAME ++ " %s(): Unable to register resource" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ info->window_rsrc.start, info->window_rsrc.end); + } +-#endif + + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); +@@ -136,49 +133,94 @@ + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + +- printk(KERN_NOTICE "amd76xrom window : %x at %x\n", ++ printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", + window->size, window->start); + /* For write accesses caches are useless */ +- info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size); ++ info->window_addr = ++ (unsigned long)ioremap_nocache(window->start, ++ window->size); + + if (!info->window_addr) { + printk(KERN_ERR "Failed to ioremap\n"); +- goto err_out_free_mmio_region; ++ continue; + } +- info->mtd = 0; ++ ++ info->mtd = NULL; ++ + for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { + char **chip_type; + if (rom_size > window->size) { + continue; + } +- info->map.map_priv_1 = ++ info->map.phys = window->start + window->size - rom_size; ++ info->map.virt = + info->window_addr + window->size - rom_size; + info->map.size = rom_size; ++ simple_map_init(&info->map); + chip_type = rom_probe_types; + for(; !info->mtd && *chip_type; chip_type++) { + info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); + } +- if (info->mtd) { +- break; +- } ++ if (info->mtd) goto found_mtd; + } +- if (!info->mtd) { +- goto err_out_iounmap; ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_byte(pdev, 0x40, &byte); ++ pci_write_config_byte(pdev, 0x40, byte & ~1); ++ ++ window++; + } +- printk(KERN_NOTICE "amd76xrom chip at offset: %x\n", ++ goto failed; ++ ++ found_mtd: ++ printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n", + window->size - rom_size); + +- info->mtd->module = THIS_MODULE; ++ info->mtd->owner = THIS_MODULE; ++ ++ if (!info->window_rsrc.parent) { ++ /* failed to reserve entire window - try fragments */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = window->start; ++ info->window_rsrc.end = window->start + window->size - rom_size - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve window resource fragment\n"); ++ goto failed; ++ } ++ } ++ + add_mtd_device(info->mtd); + info->window_start = window->start; + info->window_size = window->size; ++ ++ if (info->window_rsrc.parent) { ++ /* ++ * Registering the MTD device in iomem may not be possible ++ * if there is a BIOS "reserved" and BUSY range. If this ++ * fails then continue anyway. ++ */ ++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, ++ "mtd%d", info->mtd->index); ++ ++ info->rom_rsrc.name = info->mtd_name; ++ info->rom_rsrc.start = window->start + window->size - rom_size; ++ info->rom_rsrc.end = window->start + window->size - 1; ++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve MTD resource\n"); ++ info->rom_rsrc.parent = NULL; ++ } ++ } ++ + return 0; + +-err_out_iounmap: +- iounmap((void *)(info->window_addr)); +-err_out_free_mmio_region: +- release_mem_region(window->start, window->size); +-err_out_none: ++ failed: ++ amd76xrom_cleanup(info); + return -ENODEV; + } + +@@ -186,21 +228,8 @@ + static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) + { + struct amd76xrom_map_info *info = &amd76xrom_map; +- u8 byte; +- +- del_mtd_device(info->mtd); +- map_destroy(info->mtd); +- info->mtd = 0; +- info->map.map_priv_1 = 0; +- +- iounmap((void *)(info->window_addr)); +- info->window_addr = 0; +- +- /* Disable writes through the rom window */ +- pci_read_config_byte(pdev, 0x40, &byte); +- pci_write_config_byte(pdev, 0x40, byte & ~1); + +- release_mem_region(info->window_start, info->window_size); ++ amd76xrom_cleanup(info); + } + + static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = { +@@ -208,6 +237,7 @@ + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, + PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */ + { 0, } + }; + +@@ -215,10 +245,10 @@ + + #if 0 + static struct pci_driver amd76xrom_driver = { +- name: "amd76xrom", +- id_table: amd76xrom_pci_tbl, +- probe: amd76xrom_init_one, +- remove: amd76xrom_remove_one, ++ .name = MOD_NAME, ++ .id_table = amd76xrom_pci_tbl, ++ .probe = amd76xrom_init_one, ++ .remove = amd76xrom_remove_one, + }; + #endif + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c linux/drivers/mtd/maps/arctic-mtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/arctic-mtd.c 2004-11-19 10:25:11.893200704 +0100 +@@ -0,0 +1,135 @@ ++/* ++ * $Id: arctic-mtd.c,v 1.10 2003/06/02 16:37:59 trini Exp $ ++ * ++ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Arctic boards. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ * modified for Arctic by, ++ * David Gibson ++ * IBM OzLabs, Canberra, Australia ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) ++ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) ++ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) ++ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) ++ */ ++ ++#define FFS1_SIZE 0x01000000 /* 16MiB */ ++#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ ++#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ ++#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ ++ ++ ++#define NAME "Arctic Linux Flash" ++#define PADDR SUBZERO_BOOTFLASH_PADDR ++#define BUSWIDTH 2 ++#define SIZE SUBZERO_BOOTFLASH_SIZE ++#define PARTITIONS 4 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++{ ++ /* do nothing for now */ ++} ++ ++static struct map_info arctic_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR, ++}; ++ ++static struct mtd_info *arctic_mtd; ++ ++static struct mtd_partition arctic_partitions[PARTITIONS] = { ++ { .name = "Filesystem", ++ .size = FFS1_SIZE, ++ .offset = 0,}, ++ { .name = "Kernel", ++ .size = KERNEL_SIZE, ++ .offset = FFS1_SIZE,}, ++ { .name = "Filesystem", ++ .size = FFS2_SIZE, ++ .offset = FFS1_SIZE + KERNEL_SIZE,}, ++ { .name = "Firmware", ++ .size = FIRMWARE_SIZE, ++ .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, ++}; ++ ++static int __init ++init_arctic_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); ++ ++ if (!arctic_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ simple_map_init(&arctic_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); ++ ++ if (!arctic_mtd) ++ return -ENXIO; ++ ++ arctic_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); ++} ++ ++static void __exit ++cleanup_arctic_mtd(void) ++{ ++ if (arctic_mtd) { ++ del_mtd_partitions(arctic_mtd); ++ map_destroy(arctic_mtd); ++ iounmap((void *) arctic_mtd_map.virt); ++ } ++} ++ ++module_init(init_arctic_mtd); ++module_exit(cleanup_arctic_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Gibson "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c linux/drivers/mtd/maps/autcpu12-nvram.c +--- linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/autcpu12-nvram.c 2004-11-19 10:25:11.894200552 +0100 +@@ -2,7 +2,7 @@ + * NV-RAM memory access on autcpu12 + * (C) 2002 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ ++ * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -32,80 +33,27 @@ + #include + #include + +-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} + + static struct mtd_info *sram_mtd; + + struct map_info autcpu12_sram_map = { +- name: "SRAM", +- size: 32768, +- buswidth: 8, +- read8: autcpu12_read8, +- read16: autcpu12_read16, +- read32: autcpu12_read32, +- copy_from: autcpu12_copy_from, +- write8: autcpu12_write8, +- write16: autcpu12_write16, +- write32: autcpu12_write32, +- copy_to: autcpu12_copy_to ++ .name = "SRAM", ++ .size = 32768, ++ .buswidth = 4, ++ .phys = 0x12000000, + }; + + static int __init init_autcpu12_sram (void) + { + int err, save0, save1; + +- autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); +- if (!autcpu12_sram_map.map_priv_1) { ++ autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K); ++ if (!autcpu12_sram_map.virt) { + printk("Failed to ioremap autcpu12 NV-RAM space\n"); + err = -EIO; + goto out; + } ++ simple_map_init(&autcpu_sram_map); + + /* + * Check for 32K/128K +@@ -115,20 +63,20 @@ + * Read and check result on ofs 0x0 + * Restore contents + */ +- save0 = autcpu12_read32(&autcpu12_sram_map,0); +- save1 = autcpu12_read32(&autcpu12_sram_map,0x10000); +- autcpu12_write32(&autcpu12_sram_map,~save0,0x10000); ++ save0 = map_read32(&autcpu12_sram_map,0); ++ save1 = map_read32(&autcpu12_sram_map,0x10000); ++ map_write32(&autcpu12_sram_map,~save0,0x10000); + /* if we find this pattern on 0x0, we have 32K size + * restore contents and exit + */ +- if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) { +- autcpu12_write32(&autcpu12_sram_map,save0,0x0); ++ if ( map_read32(&autcpu12_sram_map,0) != save0) { ++ map_write32(&autcpu12_sram_map,save0,0x0); + goto map; + } + /* We have a 128K found, restore 0x10000 and set size + * to 128K + */ +- autcpu12_write32(&autcpu12_sram_map,save1,0x10000); ++ ma[_write32(&autcpu12_sram_map,save1,0x10000); + autcpu12_sram_map.size = SZ_128K; + + map: +@@ -139,7 +87,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -148,7 +96,7 @@ + goto out_probe; + } + +- printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); ++ printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + + return 0; + +@@ -157,7 +105,7 @@ + sram_mtd = 0; + + out_ioremap: +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + out: + return err; + } +@@ -167,7 +115,7 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c linux/drivers/mtd/maps/beech-mtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/beech-mtd.c 2004-11-19 10:25:11.895200400 +0100 +@@ -0,0 +1,112 @@ ++/* ++ * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ ++ * ++ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Beech boards. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define NAME "Beech Linux Flash" ++#define PADDR BEECH_BIGFLASH_PADDR ++#define SIZE BEECH_BIGFLASH_SIZE ++#define BUSWIDTH 1 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++ ++static struct map_info beech_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR ++}; ++ ++static struct mtd_info *beech_mtd; ++ ++static struct mtd_partition beech_partitions[2] = { ++ { ++ .name = "Linux Kernel", ++ .size = BEECH_KERNEL_SIZE, ++ .offset = BEECH_KERNEL_OFFSET ++ }, { ++ .name = "Free Area", ++ .size = BEECH_FREE_AREA_SIZE, ++ .offset = BEECH_FREE_AREA_OFFSET ++ } ++}; ++ ++static int __init ++init_beech_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); ++ ++ if (!beech_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ ++ simple_map_init(&beech_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); ++ ++ if (!beech_mtd) ++ return -ENXIO; ++ ++ beech_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(beech_mtd, beech_partitions, 2); ++} ++ ++static void __exit ++cleanup_beech_mtd(void) ++{ ++ if (beech_mtd) { ++ del_mtd_partitions(beech_mtd); ++ map_destroy(beech_mtd); ++ iounmap((void *) beech_mtd_map.virt); ++ } ++} ++ ++module_init(init_beech_mtd); ++module_exit(cleanup_beech_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Bishop Brock "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c linux/drivers/mtd/maps/cdb89712.c +--- linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cdb89712.c 2004-11-19 10:25:11.897200096 +0100 +@@ -1,13 +1,14 @@ + /* + * Flash on Cirrus CDB89712 + * +- * $Id: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $ ++ * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -16,77 +17,21 @@ + + + +-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} +- + + static struct mtd_info *flash_mtd; + + struct map_info cdb89712_flash_map = { +- name: "flash", +- size: FLASH_SIZE, +- buswidth: FLASH_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "flash", ++ .size = FLASH_SIZE, ++ .buswidth = FLASH_WIDTH, ++ .phys = FLASH_START, + }; + + struct resource cdb89712_flash_resource = { +- name: "Flash", +- start: FLASH_START, +- end: FLASH_START + FLASH_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "Flash", ++ .start = FLASH_START, ++ .end = FLASH_START + FLASH_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_flash (void) +@@ -99,13 +44,13 @@ + goto out; + } + +- cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!cdb89712_flash_map.map_priv_1) { ++ cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); ++ if (!cdb89712_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_flash_map); + flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); + if (!flash_mtd) { + flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); +@@ -118,7 +63,7 @@ + goto out_ioremap; + } + +- flash_mtd->module = THIS_MODULE; ++ flash_mtd->owner = THIS_MODULE; + + if (add_mtd_device(flash_mtd)) { + printk("FLASH device addition failed\n"); +@@ -132,7 +77,7 @@ + map_destroy(flash_mtd); + flash_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + out_resource: + release_resource (&cdb89712_flash_resource); + out: +@@ -146,24 +91,17 @@ + static struct mtd_info *sram_mtd; + + struct map_info cdb89712_sram_map = { +- name: "SRAM", +- size: SRAM_SIZE, +- buswidth: SRAM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "SRAM", ++ .size = SRAM_SIZE, ++ .buswidth = SRAM_WIDTH, ++ .phys = SRAM_START, + }; + + struct resource cdb89712_sram_resource = { +- name: "SRAM", +- start: SRAM_START, +- end: SRAM_START + SRAM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "SRAM", ++ .start = SRAM_START, ++ .end = SRAM_START + SRAM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_sram (void) +@@ -176,13 +114,13 @@ + goto out; + } + +- cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); +- if (!cdb89712_sram_map.map_priv_1) { ++ cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); ++ if (!cdb89712_sram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_sram_map); + sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); + if (!sram_mtd) { + printk("SRAM probe failed\n"); +@@ -190,7 +128,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -205,7 +143,7 @@ + map_destroy(sram_mtd); + sram_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + out_resource: + release_resource (&cdb89712_sram_resource); + out: +@@ -221,20 +159,17 @@ + static struct mtd_info *bootrom_mtd; + + struct map_info cdb89712_bootrom_map = { +- name: "BootROM", +- size: BOOTROM_SIZE, +- buswidth: BOOTROM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, ++ .name = "BootROM", ++ .size = BOOTROM_SIZE, ++ .buswidth = BOOTROM_WIDTH, ++ .phys = BOOTROM_START, + }; + + struct resource cdb89712_bootrom_resource = { +- name: "BootROM", +- start: BOOTROM_START, +- end: BOOTROM_START + BOOTROM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "BootROM", ++ .start = BOOTROM_START, ++ .end = BOOTROM_START + BOOTROM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_bootrom (void) +@@ -247,13 +182,13 @@ + goto out; + } + +- cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); +- if (!cdb89712_bootrom_map.map_priv_1) { ++ cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); ++ if (!cdb89712_bootrom_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_bootrom_map); + bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); + if (!bootrom_mtd) { + printk("BootROM probe failed\n"); +@@ -261,7 +196,7 @@ + goto out_ioremap; + } + +- bootrom_mtd->module = THIS_MODULE; ++ bootrom_mtd->owner = THIS_MODULE; + bootrom_mtd->erasesize = 0x10000; + + if (add_mtd_device(bootrom_mtd)) { +@@ -276,7 +211,7 @@ + map_destroy(bootrom_mtd); + bootrom_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + out_resource: + release_resource (&cdb89712_bootrom_resource); + out: +@@ -306,21 +241,21 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + release_resource (&cdb89712_sram_resource); + } + + if (flash_mtd) { + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + release_resource (&cdb89712_flash_resource); + } + + if (bootrom_mtd) { + del_mtd_device(bootrom_mtd); + map_destroy(bootrom_mtd); +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + release_resource (&cdb89712_bootrom_resource); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ceiva.c linux/drivers/mtd/maps/ceiva.c +--- linux-mips-2.4.27/drivers/mtd/maps/ceiva.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/ceiva.c 2004-11-19 10:25:11.898199944 +0100 +@@ -11,7 +11,7 @@ + * + * (C) 2000 Nicolas Pitre + * +- * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ ++ * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -31,62 +32,10 @@ + #include + + /* +- * This isnt complete yet, so... ++ * This isn't complete yet, so... + */ + #define CONFIG_MTD_CEIVA_STATICMAP + +-static __u8 clps_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 clps_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 clps_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info clps_map __initdata = { +- name: "clps flash", +- read8: clps_read8, +- read16: clps_read16, +- read32: clps_read32, +- copy_from: clps_copy_from, +- write8: clps_write8, +- write16: clps_write16, +- write32: clps_write32, +- copy_to: clps_copy_to, +-}; +- + #ifdef CONFIG_MTD_CEIVA_STATICMAP + /* + * See include/linux/mtd/partitions.h for definition of the mtd_partition +@@ -176,7 +125,7 @@ + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; +- ++ memset(maps, 0, sizeof(struct map_info) * nr); + /* + * Claim and then map the memory regions. + */ +@@ -191,7 +140,9 @@ + } + + clps[i].map = maps + i; +- memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); ++ ++ clps[i].map->name = "clps flash"; ++ clps[i].map->phys = clps[i].base; + + clps[i].vbase = ioremap(clps[i].base, clps[i].size); + if (!clps[i].vbase) { +@@ -199,16 +150,18 @@ + break; + } + +- clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; ++ clps[i].map->virt = (unsigned long)clps[i].vbase; + clps[i].map->buswidth = clps[i].width; + clps[i].map->size = clps[i].size; + ++ simple_map_init(&clps[i].map); ++ + clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); + if (clps[i].mtd == NULL) { + ret = -ENXIO; + break; + } +- clps[i].mtd->module = THIS_MODULE; ++ clps[i].mtd->owner = THIS_MODULE; + subdev[i] = clps[i].mtd; + + printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " +@@ -318,10 +271,8 @@ + return nr; + } + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); +- + static struct mtd_partition *parsed_parts; ++static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; + + static void __init clps_locate_partitions(struct mtd_info *mtd) + { +@@ -331,20 +282,11 @@ + /* + * Partition selection stuff. + */ +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps"); ++ nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); + if (nr_parts > 0) { + part_type = "command line"; + break; + } +-#endif +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mtd, &parsed_parts); +- if (nr_parts > 0) { +- part_type = "RedBoot"; +- break; +- } +-#endif + #ifdef CONFIG_MTD_CEIVA_STATICMAP + nr_parts = clps_static_partitions(&parsed_parts); + if (nr_parts > 0) { +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c linux/drivers/mtd/maps/cfi_flagadm.c +--- linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cfi_flagadm.c 2004-11-19 10:25:11.900199640 +0100 +@@ -1,7 +1,7 @@ + /* + * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson + * +- * $Id: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -55,83 +56,33 @@ + #define FLASH_PARTITION3_ADDR 0x00240000 + #define FLASH_PARTITION3_SIZE 0x001C0000 + +-__u8 flagadm_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 flagadm_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 flagadm_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info flagadm_map = { +- name: "FlagaDM flash device", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: flagadm_read8, +- read16: flagadm_read16, +- read32: flagadm_read32, +- copy_from: flagadm_copy_from, +- write8: flagadm_write8, +- write16: flagadm_write16, +- write32: flagadm_write32, +- copy_to: flagadm_copy_to ++ .name = "FlagaDM flash device", ++ .size = FLASH_SIZE, ++ .buswidth = 2, + }; + + struct mtd_partition flagadm_parts[] = { + { +- name : "Bootloader", +- offset : FLASH_PARTITION0_ADDR, +- size : FLASH_PARTITION0_SIZE ++ .name = "Bootloader", ++ .offset = FLASH_PARTITION0_ADDR, ++ .size = FLASH_PARTITION0_SIZE + }, + { +- name : "Kernel image", +- offset : FLASH_PARTITION1_ADDR, +- size : FLASH_PARTITION1_SIZE ++ .name = "Kernel image", ++ .offset = FLASH_PARTITION1_ADDR, ++ .size = FLASH_PARTITION1_SIZE + }, + { +- name : "Initial ramdisk image", +- offset : FLASH_PARTITION2_ADDR, +- size : FLASH_PARTITION2_SIZE ++ .name = "Initial ramdisk image", ++ .offset = FLASH_PARTITION2_ADDR, ++ .size = FLASH_PARTITION2_SIZE + }, + { +- name : "Persistant storage", +- offset : FLASH_PARTITION3_ADDR, +- size : FLASH_PARTITION3_SIZE ++ .name = "Persistant storage", ++ .offset = FLASH_PARTITION3_ADDR, ++ .size = FLASH_PARTITION3_SIZE + } + }; + +@@ -144,22 +95,26 @@ + printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + +- flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR, ++ flagadm_map.phys = FLASH_PHYS_ADDR; ++ flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + +- if (!flagadm_map.map_priv_1) { ++ if (!flagadm_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ ++ simple_map_init(&flagadm_map); ++ + mymtd = do_map_probe("cfi_probe", &flagadm_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT); + printk(KERN_NOTICE "FlagaDM flash device initialized\n"); + return 0; + } + +- iounmap((void *)flagadm_map.map_priv_1); ++ iounmap((void *)flagadm_map.virt); + return -ENXIO; + } + +@@ -169,9 +124,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (flagadm_map.map_priv_1) { +- iounmap((void *)flagadm_map.map_priv_1); +- flagadm_map.map_priv_1 = 0; ++ if (flagadm_map.virt) { ++ iounmap((void *)flagadm_map.virt); ++ flagadm_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c linux/drivers/mtd/maps/cstm_mips_ixx.c +--- linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cstm_mips_ixx.c 2004-11-19 10:25:11.901199488 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: cstm_mips_ixx.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: cstm_mips_ixx.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. +@@ -33,55 +33,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include +- +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #include +-#endif +- +-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #define CC_GCR 0xB4013818 +@@ -97,10 +55,17 @@ + #define CC_GPAICR 0xB4013804 + #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + ++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) + { ++ static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED; ++ static int vpp_count = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpp_lock, flags); ++ + if (vpp) { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ if (!vpp_count++) { + __u16 data; + __u8 data1; + static u8 first = 1; +@@ -116,10 +81,9 @@ + enabling vpp after powerup */ + udelay(40); + } +-#endif /* CONFIG_MIPS_ITE8172 */ + } +- else { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ } else { ++ if (!--vpp_count) { + __u16 data; + + // Set GPIO port B pin3 to high +@@ -127,26 +91,11 @@ + data = (data & 0xff3f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; +-#endif /* CONFIG_MIPS_ITE8172 */ + } ++ } ++ spin_unlock_irqrestore(&vpp_lock, flags); + } +- +-const struct map_info basic_cstm_mips_ixx_map = { +- NULL, +- 0, +- 0, +- cstm_mips_ixx_read8, +- cstm_mips_ixx_read16, +- cstm_mips_ixx_read32, +- cstm_mips_ixx_copy_from, +- cstm_mips_ixx_write8, +- cstm_mips_ixx_write16, +- cstm_mips_ixx_write32, +- cstm_mips_ixx_copy_to, +- cstm_mips_ixx_set_vpp, +- 0, +- 0 +-}; ++#endif + + /* board and partition description */ + +@@ -175,9 +124,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { // 28F128J3A in 2x16 configuration + { +- name: "main partition ", +- size: 0x02000000, // 128 x 2 x 128k byte sectors +- offset: 0, ++ .name = "main partition ", ++ .size = 0x02000000, // 128 x 2 x 128k byte sectors ++ .offset = 0, + }, + }, + }; +@@ -197,9 +146,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { + { +- name: "main partition", +- size: CONFIG_MTD_CSTM_MIPS_IXX_LEN, +- offset: 0, ++ .name = "main partition", ++ .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, ++ .offset = 0, + }, + }, + }; +@@ -216,17 +165,24 @@ + + /* Initialize mapping */ + for (i=0;imodule = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; + add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); +@@ -266,9 +222,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (cstm_mips_ixx_map[i].map_priv_1) { +- iounmap((void *)cstm_mips_ixx_map[i].map_priv_1); +- cstm_mips_ixx_map[i].map_priv_1 = 0; ++ if (cstm_mips_ixx_map[i].virt) { ++ iounmap((void *)cstm_mips_ixx_map[i].virt); ++ cstm_mips_ixx_map[i].virt = 0; + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c linux/drivers/mtd/maps/db1x00-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c 2003-02-16 07:25:24.000000000 +0100 ++++ linux/drivers/mtd/maps/db1x00-flash.c 2004-11-19 10:25:11.903199184 +0100 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -29,76 +30,6 @@ + static unsigned long flash_size; + + static BCSR * const bcsr = (BCSR *)0xAE000000; +- +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- ret = __raw_readb(map->map_priv_1 + ofs); +- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- ret = __raw_readw(map->map_priv_1 + ofs); +- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- __u32 ret; +- ret = __raw_readl(map->map_priv_1 + ofs); +- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from); +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- +-static struct map_info db1x00_map = { +- name: "Db1x00 flash", +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to, +-}; +- + static unsigned char flash_buswidth = 4; + + /* +@@ -115,58 +46,62 @@ + */ + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x1c00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x1c00000, ++ .offset = 0x0000000 + },{ +- name: "yamon", +- size: 0x0100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + },{ +- name: "raw kernel", +- size: (0x300000-0x40000), /* last 256KB is yamon env */ +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = (0x300000-0x40000), /* last 256KB is env */ ++ .offset = MTDPART_OFS_APPEND, + } + }; + #elif defined(DB1X00_BOOT_ONLY) + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x00c00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x00c00000, ++ .offset = 0x0000000 + },{ +- name: "yamon", +- size: 0x0100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + },{ +- name: "raw kernel", +- size: (0x300000-0x40000), /* last 256KB is yamon env */ +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = (0x300000-0x40000), /* last 256KB is env */ ++ .offset = MTDPART_OFS_APPEND, + } + }; + #elif defined(DB1X00_USER_ONLY) + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x0e00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x0e00000, ++ .offset = 0x0000000 + },{ +- name: "raw kernel", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #else + #error MTD_DB1X00 define combo error /* should never happen */ + #endif ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + ++#define NAME "Db1x00 Linux Flash" + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++static struct map_info db1xxx_mtd_map = { ++ .name = NAME, ++}; + + static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; ++static struct mtd_info *db1xxx_mtd; + + /* + * Probe the flash density and setup window address and size +@@ -174,7 +109,7 @@ + * want the MTD driver to be probing the boot or user flash, + * so having the option to enable only one bank is important. + */ +-int setup_flash_params() ++int setup_flash_params(void) + { + switch ((bcsr->status >> 14) & 0x3) { + case 0: /* 64Mbit devices */ +@@ -228,6 +163,10 @@ + default: + return 1; + } ++ db1xxx_mtd_map.size = window_size; ++ db1xxx_mtd_map.buswidth = flash_buswidth; ++ db1xxx_mtd_map.phys = window_addr; ++ db1xxx_mtd_map.buswidth = flash_buswidth; + return 0; + } + +@@ -235,10 +174,6 @@ + { + struct mtd_partition *parts; + int nb_parts = 0; +- char *part_type; +- +- /* Default flash buswidth */ +- db1x00_map.buswidth = flash_buswidth; + + if (setup_flash_params()) + return -ENXIO; +@@ -246,32 +181,29 @@ + /* + * Static partition definition selection + */ +- part_type = "static"; + parts = db1x00_partitions; + nb_parts = NB_OF(db1x00_partitions); +- db1x00_map.size = window_size; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n", +- db1x00_map.buswidth*8); +- db1x00_map.map_priv_1 = +- (unsigned long)ioremap(window_addr, window_size); +- mymtd = do_map_probe("cfi_probe", &db1x00_map); +- if (!mymtd) return -ENXIO; +- mymtd->module = THIS_MODULE; ++ db1xxx_mtd_map.buswidth*8); ++ db1xxx_mtd_map.virt = (unsigned long)ioremap(window_addr, window_size); ++ db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map); ++ if (!db1xxx_mtd) return -ENXIO; ++ db1xxx_mtd->owner = THIS_MODULE; + +- add_mtd_partitions(mymtd, parts, nb_parts); ++ add_mtd_partitions(db1xxx_mtd, parts, nb_parts); + return 0; + } + + static void __exit db1x00_mtd_cleanup(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); ++ if (db1xxx_mtd) { ++ del_mtd_partitions(db1xxx_mtd); ++ map_destroy(db1xxx_mtd); + if (parsed_parts) + kfree(parsed_parts); + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c linux/drivers/mtd/maps/dbox2-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/dbox2-flash.c 2004-11-19 10:25:11.904199032 +0100 +@@ -1,12 +1,13 @@ + /* +- * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * +- * Nokia / Sagem D-Box 2 flash driver ++ * D-Box 2 flash driver + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -16,22 +17,44 @@ + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +-static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw +- size: 128 * 1024, +- offset: 0, +- mask_flags: MTD_WRITEABLE}, +- {name: "PPC bootloader", // flfs +- size: 128 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "Kernel", // idxfs +- size: 768 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "System", // jffs +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}}; ++static struct mtd_partition partition_info[]= { ++ { ++ .name = "BR bootloader", ++ .size = 128 * 1024, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ }, ++ { ++ .name = "flfs (ppcboot)", ++ .size = 128 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "root (cramfs)", ++ .size = 7040 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "var (jffs2)", ++ .size = 896 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "flash without bootloader", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 128 * 1024, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "complete flash", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ } ++}; + + #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) + +@@ -40,72 +63,24 @@ + + static struct mtd_info *mymtd; + +-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info dbox2_flash_map = { +- name: "D-Box 2 flash memory", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: dbox2_flash_read8, +- read16: dbox2_flash_read16, +- read32: dbox2_flash_read32, +- copy_from: dbox2_flash_copy_from, +- write8: dbox2_flash_write8, +- write16: dbox2_flash_write16, +- write32: dbox2_flash_write32, +- copy_to: dbox2_flash_copy_to ++ .name = "D-Box 2 flash memory", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_dbox2_flash(void) + { + printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); +- dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!dbox2_flash_map.map_priv_1) { ++ if (!dbox2_flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&dbox2_flash_map); + + // Probe for dual Intel 28F320 or dual AMD + mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); +@@ -117,7 +92,7 @@ + } + + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); +@@ -125,7 +100,7 @@ + return 0; + } + +- iounmap((void *)dbox2_flash_map.map_priv_1); ++ iounmap((void *)dbox2_flash_map.virt); + return -ENXIO; + } + +@@ -135,9 +110,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dbox2_flash_map.map_priv_1) { +- iounmap((void *)dbox2_flash_map.map_priv_1); +- dbox2_flash_map.map_priv_1 = 0; ++ if (dbox2_flash_map.virt) { ++ iounmap((void *)dbox2_flash_map.virt); ++ dbox2_flash_map.virt = 0; + } + } + +@@ -146,5 +121,5 @@ + + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Kári Davíðsson "); +-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); ++MODULE_AUTHOR("Kári Davíðsson , Bastian Blank , Alexander Wild "); ++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dc21285.c linux/drivers/mtd/maps/dc21285.c +--- linux-mips-2.4.27/drivers/mtd/maps/dc21285.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/dc21285.c 2004-11-19 10:25:11.906198728 +0100 +@@ -5,12 +5,13 @@ + * + * This code is GPL + * +- * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ ++ * $Id: dc21285.c,v 1.15 2003/05/21 12:45:18 dwmw2 Exp $ + */ + #include + #include + #include + #include ++#include + + #include + #include +@@ -92,26 +93,42 @@ + } + + struct map_info dc21285_map = { +- name: "DC21285 flash", +- size: 16*1024*1024, +- read8: dc21285_read8, +- read16: dc21285_read16, +- read32: dc21285_read32, +- copy_from: dc21285_copy_from, +- write8: dc21285_write8, +- write16: dc21285_write16, +- write32: dc21285_write32, +- copy_to: dc21285_copy_to ++ .name = "DC21285 flash", ++ .phys = NO_XIP, ++ .size = 16*1024*1024, ++ .read8 = dc21285_read8, ++ .read16 = dc21285_read16, ++ .read32 = dc21285_read32, ++ .copy_from = dc21285_copy_from, ++ .write8 = dc21285_write8, ++ .write16 = dc21285_write16, ++ .write32 = dc21285_write32, ++ .copy_to = dc21285_copy_to + }; + + + /* Partition stuff */ + static struct mtd_partition *dc21285_parts; +- +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++#endif + + int __init init_dc21285(void) + { ++ ++ /* ++ * Flash timing is determined with bits 19-16 of the ++ * CSR_SA110_CNTL. The value is the number of wait cycles, or ++ * 0 for 16 cycles (the default). Cycles are 20 ns. ++ * Here we use 7 for 140 ns flash chips. ++ */ ++ /* access time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); ++ /* burst time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); ++ /* tristate time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); ++ + /* Determine buswidth */ + switch (*CSR_SA110_CNTL & (3<<14)) { + case SA110_CNTL_ROMWIDTH_8: +@@ -141,33 +158,18 @@ + if (mymtd) { + int nrparts = 0; + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* partition fixup */ + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); +-#endif ++#ifdef CONFIG_MTD_PARTITIONS ++ nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0); + if (nrparts > 0) { + add_mtd_partitions(mymtd, dc21285_parts, nrparts); +- } else if (nrparts == 0) { +- printk(KERN_NOTICE "RedBoot partition table failed\n"); +- add_mtd_device(mymtd); ++ return 0; + } +- +- /* +- * Flash timing is determined with bits 19-16 of the +- * CSR_SA110_CNTL. The value is the number of wait cycles, or +- * 0 for 16 cycles (the default). Cycles are 20 ns. +- * Here we use 7 for 140 ns flash chips. +- */ +- /* access time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); +- /* burst time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); +- /* tristate time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); +- ++#endif ++ add_mtd_device(mymtd); + return 0; + } + +@@ -177,17 +179,16 @@ + + static void __exit cleanup_dc21285(void) + { +- if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (dc21285_parts) { ++ del_mtd_partitions(mymtd); ++ kfree(dc21285_parts); ++ } else ++#endif + del_mtd_device(mymtd); ++ + map_destroy(mymtd); +- mymtd = NULL; +- } +- if (dc21285_map.map_priv_1) { + iounmap((void *)dc21285_map.map_priv_1); +- dc21285_map.map_priv_1 = 0; +- } +- if(dc21285_parts) +- kfree(dc21285_parts); + } + + module_init(init_dc21285); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c linux/drivers/mtd/maps/dilnetpc.c +--- linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/dilnetpc.c 2004-11-19 10:25:11.907198576 +0100 +@@ -14,7 +14,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $ ++ * $Id: dilnetpc.c,v 1.12 2003/05/21 12:45:18 dwmw2 Exp $ + * + * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems + * featuring the AMD Elan SC410 processor. There are two variants of this +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,7 +37,7 @@ + #include + + /* +-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks. ++** The DIL/NetPC keeps its BIOS in two distinct flash blocks. + ** Destroying any of these blocks transforms the DNPC into + ** a paperweight (albeit not a very useful one, considering + ** it only weighs a few grams). +@@ -189,45 +190,6 @@ + } + + +-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + /* + ************************************************************ +@@ -288,19 +250,11 @@ + #define WINDOW_ADDR FLASH_BASE + + static struct map_info dnpc_map = { +- name: "ADNP Flash Bank", +- size: ADNP_WINDOW_SIZE, +- buswidth: 1, +- read8: dnpc_read8, +- read16: dnpc_read16, +- read32: dnpc_read32, +- copy_from: dnpc_copy_from, +- write8: dnpc_write8, +- write16: dnpc_write16, +- write32: dnpc_write32, +- copy_to: dnpc_copy_to, +- set_vpp: adnp_set_vpp, +- map_priv_2: WINDOW_ADDR ++ .name = "ADNP Flash Bank", ++ .size = ADNP_WINDOW_SIZE, ++ .buswidth = 1, ++ .set_vpp = adnp_set_vpp, ++ .phys = WINDOW_ADDR + }; + + /* +@@ -316,29 +270,29 @@ + static struct mtd_partition partition_info[]= + { + { +- name: "ADNP boot", +- offset: 0, +- size: 0xf0000, ++ .name = "ADNP boot", ++ .offset = 0, ++ .size = 0xf0000, + }, + { +- name: "ADNP system BIOS", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x10000, ++ .name = "ADNP system BIOS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x10000, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + { +- name: "ADNP file system", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x2f0000, ++ .name = "ADNP file system", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x2f0000, + }, + { +- name: "ADNP system BIOS entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -369,21 +323,21 @@ + static struct mtd_partition higlvl_partition_info[]= + { + { +- name: "ADNP boot block", +- offset: 0, +- size: CONFIG_MTD_DILNETPC_BOOTSIZE, ++ .name = "ADNP boot block", ++ .offset = 0, ++ .size = CONFIG_MTD_DILNETPC_BOOTSIZE, + }, + { +- name: "ADNP file system space", +- offset: MTDPART_OFS_NXTBLK, +- size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, ++ .name = "ADNP file system space", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, + }, + { +- name: "ADNP system BIOS + BIOS Entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS + BIOS Entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -447,18 +401,19 @@ + } + + printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", +- is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2); ++ is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); + +- dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size); + +- dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map_flash(dnpc_map.phys, dnpc_map.size); + +- if (!dnpc_map.map_priv_1) { ++ if (!dnpc_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ simple_map_init(&dnpc_map); + +- printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1); ++ printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt); + + mymtd = do_map_probe("jedec_probe", &dnpc_map); + +@@ -475,11 +430,11 @@ + mymtd->erasesize = 0x10000; + + if (!mymtd) { +- iounmap((void *)dnpc_map.map_priv_1); ++ iounmap((void *)dnpc_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* + ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions() +@@ -525,10 +480,10 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dnpc_map.map_priv_1) { +- iounmap((void *)dnpc_map.map_priv_1); ++ if (dnpc_map.virt) { ++ iounmap((void *)dnpc_map.virt); + dnpc_unmap_flash(); +- dnpc_map.map_priv_1 = 0; ++ dnpc_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ebony.c linux/drivers/mtd/maps/ebony.c +--- linux-mips-2.4.27/drivers/mtd/maps/ebony.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ebony.c 2004-11-19 10:25:11.909198272 +0100 +@@ -0,0 +1,164 @@ ++/* ++ * $Id: ebony.c,v 1.8 2003/06/23 11:48:18 dwmw2 Exp $ ++ * ++ * Mapping for Ebony user flash ++ * ++ * Matt Porter ++ * ++ * Copyright 2002 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *flash; ++ ++static struct map_info ebony_small_map = { ++ .name = "Ebony small flash", ++ .size = EBONY_SMALL_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct map_info ebony_large_map = { ++ .name = "Ebony large flash", ++ .size = EBONY_LARGE_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct mtd_partition ebony_small_partitions[] = { ++ { ++ .name = "OpenBIOS", ++ .offset = 0x0, ++ .size = 0x80000, ++ } ++}; ++ ++static struct mtd_partition ebony_large_partitions[] = { ++ { ++ .name = "fs", ++ .offset = 0, ++ .size = 0x380000, ++ }, ++ { ++ .name = "firmware", ++ .offset = 0x380000, ++ .size = 0x80000, ++ } ++}; ++ ++int __init init_ebony(void) ++{ ++ u8 fpga0_reg; ++ unsigned long fpga0_adr; ++ unsigned long long small_flash_base, large_flash_base; ++ ++ fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); ++ if (!fpga0_adr) ++ return -ENOMEM; ++ ++ fpga0_reg = readb(fpga0_adr); ++ iounmap64(fpga0_adr); ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH2; ++ else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH1; ++ else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_LOW2; ++ else ++ small_flash_base = EBONY_SMALL_FLASH_LOW1; ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_ONBRD_FLASH_EN(fpga0_reg)) ++ large_flash_base = EBONY_LARGE_FLASH_LOW; ++ else ++ large_flash_base = EBONY_LARGE_FLASH_HIGH; ++ ++ ebony_small_map.phys = small_flash_base; ++ ebony_small_map.virt = ++ (unsigned long)ioremap64(small_flash_base, ++ ebony_small_map.size); ++ ++ if (!ebony_small_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_small_map); ++ ++ flash = do_map_probe("map_rom", &ebony_small_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_small_partitions, ++ ARRAY_SIZE(ebony_small_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ ebony_large_map.phys = large_flash_base; ++ ebony_large_map.virt = ++ (unsigned long)ioremap64(large_flash_base, ++ ebony_large_map.size); ++ ++ if (!ebony_large_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_large_map); ++ ++ flash = do_map_probe("cfi_probe", &ebony_large_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_large_partitions, ++ ARRAY_SIZE(ebony_large_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit cleanup_ebony(void) ++{ ++ if (flash) { ++ del_mtd_partitions(flash); ++ map_destroy(flash); ++ } ++ ++ if (ebony_small_map.virt) { ++ iounmap((void *)ebony_small_map.virt); ++ ebony_small_map.virt = 0; ++ } ++ ++ if (ebony_large_map.virt) { ++ iounmap((void *)ebony_large_map.virt); ++ ebony_large_map.virt = 0; ++ } ++} ++ ++module_init(init_ebony); ++module_exit(cleanup_ebony); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matt Porter "); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/edb7312.c linux/drivers/mtd/maps/edb7312.c +--- linux-mips-2.4.27/drivers/mtd/maps/edb7312.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/edb7312.c 2004-11-19 10:25:11.910198120 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ ++ * $Id: edb7312.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * + * Handle mapping of the NOR flash on Cogent EDB7312 boards + * +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -35,61 +36,11 @@ + + static struct mtd_info *mymtd; + +-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + struct map_info edb7312nor_map = { +- name: "NOR flash on EDB7312", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: edb7312nor_read8, +- read16: edb7312nor_read16, +- read32: edb7312nor_read32, +- copy_from: edb7312nor_copy_from, +- write8: edb7312nor_write8, +- write16: edb7312nor_write16, +- write32: edb7312nor_write32, +- copy_to: edb7312nor_copy_to ++ .name = "NOR flash on EDB7312", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR, + }; + + #ifdef CONFIG_MTD_PARTITIONS +@@ -100,29 +51,23 @@ + static struct mtd_partition static_partitions[3] = + { + { +- name: "ARMboot", +- size: 0x40000, +- offset: 0 ++ .name = "ARMboot", ++ .size = 0x40000, ++ .offset = 0 + }, + { +- name: "Kernel", +- size: 0x200000, +- offset: 0x40000 ++ .name = "Kernel", ++ .size = 0x200000, ++ .offset = 0x40000 + }, + { +- name: "RootFS", +- size: 0xDC0000, +- offset: 0x240000 ++ .name = "RootFS", ++ .size = 0xDC0000, ++ .offset = 0x240000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); +-#endif ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + #endif + +@@ -137,32 +82,33 @@ + + printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", + WINDOW_SIZE, WINDOW_ADDR); +- edb7312nor_map.map_priv_1 = (unsigned long) ++ edb7312nor_map.virt = (unsigned long) + ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!edb7312nor_map.map_priv_1) { ++ if (!edb7312nor_map.virt) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + ++ simple_map_init(&edb7312nor_map); ++ + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &edb7312nor_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); + if (mtd_parts_nb > 0) +- part_type = "command line"; +-#endif ++ part_type = "detected"; ++ + if (mtd_parts_nb == 0) + { + mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ mtd_parts_nb = ARRAY_SIZE(static_partitions); + part_type = "static"; + } + #endif +@@ -178,7 +124,7 @@ + return 0; + } + +- iounmap((void *)edb7312nor_map.map_priv_1); ++ iounmap((void *)edb7312nor_map.virt); + return -ENXIO; + } + +@@ -188,9 +134,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (edb7312nor_map.map_priv_1) { +- iounmap((void *)edb7312nor_map.map_priv_1); +- edb7312nor_map.map_priv_1 = 0; ++ if (edb7312nor_map.virt) { ++ iounmap((void *)edb7312nor_map.virt); ++ edb7312nor_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c +--- linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/elan-104nc.c 2004-11-19 10:25:11.912197816 +0100 +@@ -16,7 +16,7 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +- $Id: elan-104nc.c,v 1.13 2002/02/13 15:30:20 dwmw2 Exp $ ++ $Id: elan-104nc.c,v 1.18 2003/06/23 07:37:02 dwmw2 Exp $ + + The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 + mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. +@@ -40,6 +40,7 @@ + #include + + #include ++#include + #include + + #define WINDOW_START 0xb0000 +@@ -59,14 +60,14 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "ELAN-104NC flash boot partition", +- offset: 0, +- size: 640*1024 }, +- { name: "ELAN-104NC flash partition 1", +- offset: 640*1024, +- size: 896*1024 }, +- { name: "ELAN-104NC flash partition 2", +- offset: (640+896)*1024 } ++ { .name = "ELAN-104NC flash boot partition", ++ .offset = 0, ++ .size = 640*1024 }, ++ { .name = "ELAN-104NC flash partition 1", ++ .offset = 640*1024, ++ .size = 896*1024 }, ++ { .name = "ELAN-104NC flash partition 2", ++ .offset = (640+896)*1024 } + }; + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +@@ -195,19 +196,20 @@ + } + + static struct map_info elan_104nc_map = { +- name: "ELAN-104NC flash", +- size: 8*1024*1024, /* this must be set to a maximum possible amount ++ .name = "ELAN-104NC flash", ++ .phys = NO_XIP, ++ .size = 8*1024*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ +- buswidth: 2, +- read8: elan_104nc_read8, +- read16: elan_104nc_read16, +- read32: elan_104nc_read32, +- copy_from: elan_104nc_copy_from, +- write8: elan_104nc_write8, +- write16: elan_104nc_write16, +- write32: elan_104nc_write32, +- copy_to: elan_104nc_copy_to ++ .buswidth = 2, ++ .read8 = elan_104nc_read8, ++ .read16 = elan_104nc_read16, ++ .read32 = elan_104nc_read32, ++ .copy_from = elan_104nc_copy_from, ++ .write8 = elan_104nc_write8, ++ .write16 = elan_104nc_write16, ++ .write32 = elan_104nc_write32, ++ .copy_to = elan_104nc_copy_to + }; + + /* MTD device for all of the flash. */ +@@ -221,20 +223,13 @@ + } + + iounmap((void *)iomapadr); +- release_region(PAGE_IO,PAGE_IO_SIZE); + } + + int __init init_elan_104nc(void) + { +- /* Urg! We use I/O port 0x22 without request_region()ing it */ +- /* +- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { +- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", +- elan_104nc_map.name, +- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); +- return -EAGAIN; +- } +- */ ++ /* Urg! We use I/O port 0x22 without request_region()ing it, ++ because it's already allocated to the PIC. */ ++ + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", +@@ -242,10 +237,6 @@ + return -EIO; + } + +- /* +- request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" ); +- */ +- + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + elan_104nc_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, +@@ -260,7 +251,7 @@ + return -ENXIO; + } + +- all_mtd->module=THIS_MODULE; ++ all_mtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS ); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c linux/drivers/mtd/maps/epxa10db-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/epxa10db-flash.c 2004-11-19 10:25:11.913197664 +0100 +@@ -5,7 +5,7 @@ + * Copyright (C) 2001 Altera Corporation + * Copyright (C) 2001 Red Hat, Inc. + * +- * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ ++ * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -43,87 +44,38 @@ + + static struct mtd_info *mymtd; + +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); + static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +-static __u8 epxa_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 epxa_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 epxa_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +- + + static struct map_info epxa_map = { +- name: "EPXA flash", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: epxa_read8, +- read16: epxa_read16, +- read32: epxa_read32, +- copy_from: epxa_copy_from, +- write8: epxa_write8, +- write16: epxa_write16, +- write32: epxa_write32, +- copy_to: epxa_copy_to ++ .name = "EPXA flash", ++ .size = FLASH_SIZE, ++ .buswidth = 2, ++ .phys = FLASH_START, + }; + ++static const char *probes[] = { "RedBoot", "afs", NULL }; + + static int __init epxa_mtd_init(void) + { + int i; + +- printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); +- epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!epxa_map.map_priv_1) { ++ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); ++ ++ epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); ++ if (!epxa_map.virt) { + printk("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } ++ simple_map_init(&epxa_map); + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { +- iounmap((void *)epxa_map.map_priv_1); ++ iounmap((void *)epxa_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ +@@ -135,23 +87,14 @@ + } + } + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mymtd, &parts); +- +- if (nr_parts > 0) { +- add_mtd_partitions(mymtd, parts, nr_parts); +- return 0; +- } +-#endif +-#ifdef CONFIG_MTD_AFS_PARTS +- nr_parts = parse_afs_partitions(mymtd, &parts); ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + #endif +- + /* No recognised partitioning schemes found - use defaults */ + nr_parts = epxa_default_partitions(mymtd, &parts); + if (nr_parts > 0) { +@@ -173,9 +116,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (epxa_map.map_priv_1) { +- iounmap((void *)epxa_map.map_priv_1); +- epxa_map.map_priv_1 = 0; ++ if (epxa_map.virt) { ++ iounmap((void *)epxa_map.virt); ++ epxa_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/fortunet.c linux/drivers/mtd/maps/fortunet.c +--- linux-mips-2.4.27/drivers/mtd/maps/fortunet.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/fortunet.c 2004-11-19 10:25:11.915197360 +0100 +@@ -1,11 +1,12 @@ + /* fortunet.c memory map + * +- * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ ++ * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -23,7 +24,7 @@ + + struct map_region + { +- int window_addr_phyical; ++ int window_addr_physical; + int altbuswidth; + struct map_info map_info; + struct mtd_info *mymtd; +@@ -37,57 +38,10 @@ + static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; + + +-__u8 fortunet_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 fortunet_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 fortunet_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} + + struct map_info default_map = { +- size: DEF_WINDOW_SIZE, +- buswidth: 4, +- read8: fortunet_read8, +- read16: fortunet_read16, +- read32: fortunet_read32, +- copy_from: fortunet_copy_from, +- write8: fortunet_write8, +- write16: fortunet_write16, +- write32: fortunet_write32, +- copy_to: fortunet_copy_to ++ .size = DEF_WINDOW_SIZE, ++ .buswidth = 4, + }; + + static char * __init get_string_option(char *dest,int dest_size,char *sor) +@@ -147,7 +101,7 @@ + get_options (get_string_option(string,sizeof(string),line),6,params); + if(params[0]<1) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " + " name,region-number[,base,size,buswidth,altbuswidth]\n"); + return 1; + } +@@ -161,14 +115,14 @@ + memcpy(&map_regions[params[1]].map_info, + &default_map,sizeof(map_regions[params[1]].map_info)); + map_regions_set[params[1]] = 1; +- map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY; ++ map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[params[1]].altbuswidth = 2; + map_regions[params[1]].mymtd = NULL; + map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; + strcpy(map_regions[params[1]].map_info.name,string); + if(params[0]>1) + { +- map_regions[params[1]].window_addr_phyical = params[2]; ++ map_regions[params[1]].window_addr_physical = params[2]; + } + if(params[0]>2) + { +@@ -185,14 +139,14 @@ + return 1; + } + +-static int __init MTD_New_Partion(char *line) ++static int __init MTD_New_Partition(char *line) + { + char string[MAX_NAME_SIZE]; + int params[4]; + get_options (get_string_option(string,sizeof(string),line),4,params); + if(params[0]<3) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion " ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " + " name,region-number,size,offset\n"); + return 1; + } +@@ -204,7 +158,7 @@ + } + if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) + { +- printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); ++ printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); + return 1; + } + map_regions[params[1]].parts[map_regions_parts[params[1]]].name = +@@ -220,7 +174,10 @@ + } + + __setup("MTD_Region=", MTD_New_Region); +-__setup("MTD_Partion=", MTD_New_Partion); ++__setup("MTD_Partition=", MTD_New_Partition); ++ ++/* Backwards-spelling-compatibility */ ++__setup("MTD_Partion=", MTD_New_Partition); + + int __init init_fortunet(void) + { +@@ -229,13 +186,13 @@ + { + if(map_regions_parts[ix]&&(!map_regions_set[ix])) + { +- printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n", ++ printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", + ix); + memset(&map_regions[ix],0,sizeof(map_regions[ix])); + memcpy(&map_regions[ix].map_info,&default_map, + sizeof(map_regions[ix].map_info)); + map_regions_set[ix] = 1; +- map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY; ++ map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[ix].altbuswidth = 2; + map_regions[ix].mymtd = NULL; + map_regions[ix].map_info.name = map_regions[ix].map_name; +@@ -244,30 +201,35 @@ + if(map_regions_set[ix]) + { + iy++; +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " + " address %x size %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].window_addr_phyical, ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- map_regions[ix].map_info.map_priv_1 = ++ ++ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, ++ ++ map_regions[ix].map_info.virt = + (int)ioremap_nocache( +- map_regions[ix].window_addr_phyical, ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- if(!map_regions[ix].map_info.map_priv_1) ++ if(!map_regions[ix].map_info.virt) + { + printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", + map_regions[ix].map_info.name); + return -ENXIO; + } +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n", ++ simple_map_init(&map_regions[ix].map_info); ++ ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].map_info.map_priv_1); ++ map_regions[ix].map_info.virt); + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + if((!map_regions[ix].mymtd)&&( + map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth)) + { +- printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth " + "for %s flash.\n", + map_regions[ix].map_info.name); + map_regions[ix].map_info.buswidth = +@@ -275,7 +237,7 @@ + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + } +- map_regions[ix].mymtd->module = THIS_MODULE; ++ map_regions[ix].mymtd->owner = THIS_MODULE; + add_mtd_partitions(map_regions[ix].mymtd, + map_regions[ix].parts,map_regions_parts[ix]); + } +@@ -297,7 +259,7 @@ + del_mtd_partitions( map_regions[ix].mymtd ); + map_destroy( map_regions[ix].mymtd ); + } +- iounmap((void *)map_regions[ix].map_info.map_priv_1); ++ iounmap((void *)map_regions[ix].map_info.virt); + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c linux/drivers/mtd/maps/h720x-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/h720x-flash.c 2004-11-19 10:25:11.917197056 +0100 +@@ -0,0 +1,142 @@ ++/* ++ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based ++ * evaluation boards ++ * ++ * (C) 2002 Jungjun Kim ++ * 2003 Thomas Gleixner ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *mymtd; ++ ++static struct map_info h720x_map = { ++ .name = "H720X", ++ .buswidth = 4, ++ .size = FLASH_SIZE, ++ .phys = FLASH_PHYS, ++}; ++ ++static struct mtd_partition h720x_partitions[] = { ++ { ++ .name = "ArMon", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Env", ++ .size = 0x00040000, ++ .offset = 0x00080000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Kernel", ++ .size = 0x00180000, ++ .offset = 0x000c0000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Ramdisk", ++ .size = 0x00400000, ++ .offset = 0x00240000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND ++ } ++}; ++ ++#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) ++ ++static int nr_mtd_parts; ++static struct mtd_partition *mtd_parts; ++static const char *probes[] = { "cmdlinepart", NULL }; ++ ++/* ++ * Initialize FLASH support ++ */ ++int __init h720x_mtd_init(void) ++{ ++ ++ char *part_type = NULL; ++ ++ h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE); ++ ++ if (!h720x_map.virt) { ++ printk(KERN_ERR "H720x-MTD: ioremap failed\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&h720x_map); ++ ++ // Probe for flash buswidth 4 ++ printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ if (!mymtd) { ++ printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); ++ // Probe for buswidth 2 ++ h720x_map.buswidth = 2; ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ } ++ ++ if (mymtd) { ++ mymtd->owner = THIS_MODULE; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); ++ if (nr_mtd_parts > 0) ++ part_type = "command line"; ++#endif ++ if (nr_mtd_parts <= 0) { ++ mtd_parts = h720x_partitions; ++ nr_mtd_parts = NUM_PARTITIONS; ++ part_type = "builtin"; ++ } ++ printk(KERN_INFO "Using %s partition table\n", part_type); ++ add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); ++ return 0; ++ } ++ ++ iounmap((void *)h720x_map.virt); ++ return -ENXIO; ++} ++ ++/* ++ * Cleanup ++ */ ++static void __exit h720x_mtd_cleanup(void) ++{ ++ ++ if (mymtd) { ++ del_mtd_partitions(mymtd); ++ map_destroy(mymtd); ++ } ++ ++ /* Free partition info, if commandline partition was used */ ++ if (mtd_parts && (mtd_parts != h720x_partitions)) ++ kfree (mtd_parts); ++ ++ if (h720x_map.virt) { ++ iounmap((void *)h720x_map.virt); ++ h720x_map.virt = 0; ++ } ++} ++ ++ ++module_init(h720x_mtd_init); ++module_exit(h720x_mtd_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Thomas Gleixner "); ++MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c linux/drivers/mtd/maps/ichxrom.c +--- linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ichxrom.c 2004-11-19 10:25:11.918196904 +0100 +@@ -0,0 +1,380 @@ ++/* ++ * ichxrom.c ++ * ++ * Normal mappings of chips in physical memory ++ * $Id: ichxrom.c,v 1.1 2003/10/27 19:49:23 thayne Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define MTD_DEV_NAME_LENGTH 16 ++ ++#define RESERVE_MEM_REGION 0 ++ ++#define ICHX_FWH_REGION_START 0xFF000000UL ++#define ICHX_FWH_REGION_SIZE 0x01000000UL ++#define BIOS_CNTL 0x4e ++#define FWH_DEC_EN1 0xE3 ++#define FWH_DEC_EN2 0xF0 ++#define FWH_SEL1 0xE8 ++#define FWH_SEL2 0xEE ++ ++struct ichxrom_map_info { ++ struct map_info map; ++ struct mtd_info *mtd; ++ unsigned long window_addr; ++ struct pci_dev *pdev; ++ struct resource window_rsrc; ++ struct resource rom_rsrc; ++ char mtd_name[MTD_DEV_NAME_LENGTH]; ++}; ++ ++static inline unsigned long addr(struct map_info *map, unsigned long ofs) ++{ ++ unsigned long offset; ++ offset = ((8*1024*1024) - map->size) + ofs; ++ if (offset >= (4*1024*1024)) { ++ offset += 0x400000; ++ } ++ return map->map_priv_1 + 0x400000 + offset; ++} ++ ++static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr) ++{ ++ return addr - map->map_priv_1 + ICHX_FWH_REGION_START; ++} ++ ++static __u8 ichxrom_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(addr(map, ofs)); ++} ++ ++static __u16 ichxrom_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(addr(map, ofs)); ++} ++ ++static __u32 ichxrom_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(addr(map, ofs)); ++} ++ ++static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, addr(map, from), len); ++} ++ ++static void ichxrom_write8(struct map_info *map, __u8 d, unsigned long ofs) ++{ ++ __raw_writeb(d, addr(map,ofs)); ++ mb(); ++} ++ ++static void ichxrom_write16(struct map_info *map, __u16 d, unsigned long ofs) ++{ ++ __raw_writew(d, addr(map, ofs)); ++ mb(); ++} ++ ++static void ichxrom_write32(struct map_info *map, __u32 d, unsigned long ofs) ++{ ++ __raw_writel(d, addr(map, ofs)); ++ mb(); ++} ++ ++static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(addr(map, to), from, len); ++} ++ ++static struct ichxrom_map_info ichxrom_map = { ++ .map = { ++ .name = MOD_NAME, ++ .phys = NO_XIP, ++ .size = 0, ++ .buswidth = 1, ++ .read8 = ichxrom_read8, ++ .read16 = ichxrom_read16, ++ .read32 = ichxrom_read32, ++ .copy_from = ichxrom_copy_from, ++ .write8 = ichxrom_write8, ++ .write16 = ichxrom_write16, ++ .write32 = ichxrom_write32, ++ .copy_to = ichxrom_copy_to, ++ /* Firmware hubs only use vpp when being programmed ++ * in a factory setting. So in-place programming ++ * needs to use a different method. ++ */ ++ }, ++ /* remaining fields of structure are initialized to 0 */ ++}; ++ ++enum fwh_lock_state { ++ FWH_DENY_WRITE = 1, ++ FWH_IMMUTABLE = 2, ++ FWH_DENY_READ = 4, ++}; ++ ++static void ichxrom_cleanup(struct ichxrom_map_info *info) ++{ ++ u16 word; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_word(info->pdev, BIOS_CNTL, &word); ++ pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1); ++ ++ if (info->mtd) { ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = NULL; ++ info->map.virt = 0; ++ } ++ if (info->rom_rsrc.parent) ++ release_resource(&info->rom_rsrc); ++ if (info->window_rsrc.parent) ++ release_resource(&info->window_rsrc); ++ ++ if (info->window_addr) { ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ } ++} ++ ++ ++static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len, ++ enum fwh_lock_state state) ++{ ++ struct map_info *map = mtd->priv; ++ unsigned long start = ofs; ++ unsigned long end = start + len -1; ++ ++ /* FIXME do I need to guard against concurrency here? */ ++ /* round down to 64K boundaries */ ++ start = start & ~0xFFFF; ++ end = end & ~0xFFFF; ++ while (start <= end) { ++ unsigned long ctrl_addr; ++ ctrl_addr = addr(map, start) - 0x400000 + 2; ++ writeb(state, ctrl_addr); ++ start = start + 0x10000; ++ } ++ return 0; ++} ++ ++static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE); ++} ++ ++static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ return ichxrom_set_lock_state(mtd, ofs, len, 0); ++} ++ ++static int __devinit ichxrom_init_one (struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ u16 word; ++ struct ichxrom_map_info *info = &ichxrom_map; ++ unsigned long map_size; ++ ++ /* For now I just handle the ichx and I assume there ++ * are not a lot of resources up at the top of the address ++ * space. It is possible to handle other devices in the ++ * top 16MB but it is very painful. Also since ++ * you can only really attach a FWH to an ICHX there ++ * a number of simplifications you can make. ++ * ++ * Also you can page firmware hubs if an 8MB window isn't enough ++ * but don't currently handle that case either. ++ */ ++ ++ info->pdev = pdev; ++ ++ /* ++ * Try to reserve the window mem region. If this fails then ++ * it is likely due to the window being "reseved" by the BIOS. ++ */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = ICHX_FWH_REGION_START; ++ info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ info->window_rsrc.parent = NULL; ++ printk(KERN_ERR MOD_NAME ++ " %s(): Unable to register resource" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ info->window_rsrc.start, info->window_rsrc.end); ++ } ++ ++ /* Enable writes through the rom window */ ++ pci_read_config_word(pdev, BIOS_CNTL, &word); ++ if (!(word & 1) && (word & (1<<1))) { ++ /* The BIOS will generate an error if I enable ++ * this device, so don't even try. ++ */ ++ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); ++ goto failed; ++ } ++ pci_write_config_word(pdev, BIOS_CNTL, word | 1); ++ ++ ++ /* Map the firmware hub into my address space. */ ++ /* Does this use too much virtual address space? */ ++ info->window_addr = (unsigned long)ioremap( ++ ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); ++ if (!info->window_addr) { ++ printk(KERN_ERR "Failed to ioremap\n"); ++ goto failed; ++ } ++ ++ /* For now assume the firmware has setup all relevant firmware ++ * windows. We don't have enough information to handle this case ++ * intelligently. ++ */ ++ ++ /* FIXME select the firmware hub and enable a window to it. */ ++ ++ info->mtd = 0; ++ info->map.map_priv_1 = info->window_addr; ++ ++ map_size = ICHX_FWH_REGION_SIZE; ++ while(!info->mtd && (map_size > 0)) { ++ info->map.size = map_size; ++ info->mtd = do_map_probe("jedec_probe", &ichxrom_map.map); ++ map_size -= 512*1024; ++ } ++ if (!info->mtd) { ++ goto failed; ++ } ++ /* I know I can only be a firmware hub here so put ++ * in the special lock and unlock routines. ++ */ ++ info->mtd->lock = ichxrom_lock; ++ info->mtd->unlock = ichxrom_unlock; ++ ++ info->mtd->owner = THIS_MODULE; ++ add_mtd_device(info->mtd); ++ ++ if (info->window_rsrc.parent) { ++ /* ++ * Registering the MTD device in iomem may not be possible ++ * if there is a BIOS "reserved" and BUSY range. If this ++ * fails then continue anyway. ++ */ ++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, ++ "mtd%d", info->mtd->index); ++ ++ info->rom_rsrc.name = info->mtd_name; ++ info->rom_rsrc.start = ICHX_FWH_REGION_START ++ + ICHX_FWH_REGION_SIZE - map_size; ++ info->rom_rsrc.end = ICHX_FWH_REGION_START ++ + ICHX_FWH_REGION_SIZE; ++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve MTD resource\n"); ++ info->rom_rsrc.parent = NULL; ++ } ++ } ++ ++ return 0; ++ ++ failed: ++ ichxrom_cleanup(info); ++ return -ENODEV; ++} ++ ++ ++static void __devexit ichxrom_remove_one (struct pci_dev *pdev) ++{ ++ struct ichxrom_map_info *info = &ichxrom_map; ++ u16 word; ++ ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = 0; ++ info->map.map_priv_1 = 0; ++ ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_word(pdev, BIOS_CNTL, &word); ++ pci_write_config_word(pdev, BIOS_CNTL, word & ~1); ++ ++#if RESERVE_MEM_REGION ++ release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); ++#endif ++} ++ ++static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { 0, }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl); ++ ++#if 0 ++static struct pci_driver ichxrom_driver = { ++ .name = MOD_NAME, ++ .id_table = ichxrom_pci_tbl, ++ .probe = ichxrom_init_one, ++ .remove = ichxrom_remove_one, ++}; ++#endif ++ ++static struct pci_dev *mydev; ++int __init init_ichxrom(void) ++{ ++ struct pci_dev *pdev; ++ struct pci_device_id *id; ++ pdev = 0; ++ for(id = ichxrom_pci_tbl; id->vendor; id++) { ++ pdev = pci_find_device(id->vendor, id->device, 0); ++ if (pdev) { ++ break; ++ } ++ } ++ if (pdev) { ++ mydev = pdev; ++ return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]); ++ } ++ return -ENXIO; ++#if 0 ++ return pci_module_init(&ichxrom_driver); ++#endif ++} ++ ++static void __exit cleanup_ichxrom(void) ++{ ++ ichxrom_remove_one(mydev); ++} ++ ++module_init(init_ichxrom); ++module_exit(cleanup_ichxrom); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Biederman "); ++MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/impa7.c linux/drivers/mtd/maps/impa7.c +--- linux-mips-2.4.27/drivers/mtd/maps/impa7.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/impa7.c 2004-11-19 10:25:11.920196600 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ ++ * $Id: impa7.c,v 1.9 2003/06/23 11:47:43 dwmw2 Exp $ + * + * Handle mapping of the NOR flash on implementa A7 boards + * +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -37,75 +38,17 @@ + + static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; + +-__u8 impa7_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 impa7_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 impa7_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info impa7_map[NUM_FLASHBANKS] = { + { +- name: "impA7 NOR Flash Bank #0", +- size: WINDOW_SIZE0, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .buswidth = BUSWIDTH, + }, + { +- name: "impA7 NOR Flash Bank #1", +- size: WINDOW_SIZE1, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .buswidth = BUSWIDTH, + }, + }; + +@@ -117,24 +60,18 @@ + static struct mtd_partition static_partitions[] = + { + { +- name: "FileSystem", +- size: 0x800000, +- offset: 0x00000000 ++ .name = "FileSystem", ++ .size = 0x800000, ++ .offset = 0x00000000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++static int mtd_parts_nb[NUM_FLASHBANKS]; ++static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; + +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); + #endif + +-#endif +- +-static int mtd_parts_nb = 0; +-static struct mtd_partition *mtd_parts = 0; ++static const char *probes[] = { "cmdlinepart", NULL }; + + int __init init_impa7(void) + { +@@ -146,20 +83,21 @@ + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, + }; +- char mtdid[10]; + int devicesfound = 0; + + for(i=0; imodule = THIS_MODULE; +- add_mtd_device(impa7_mtd[i]); ++ if (impa7_mtd[i]) { ++ impa7_mtd[i]->owner = THIS_MODULE; + devicesfound++; + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- sprintf(mtdid, MTDID, i); +- mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], +- &mtd_parts, +- mtdid); +- if (mtd_parts_nb > 0) ++ mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], ++ probes, ++ &mtd_parts[i], ++ 0); ++ if (mtd_parts_nb[i] > 0) { + part_type = "command line"; +-#endif +- if (mtd_parts_nb <= 0) +- { +- mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ } else { ++ mtd_parts[i] = static_partitions; ++ mtd_parts_nb[i] = ARRAY_SIZE(static_partitions); + part_type = "static"; + } +- if (mtd_parts_nb <= 0) +- { +- printk(KERN_NOTICE MSG_PREFIX +- "no partition info available\n"); +- } +- else +- { ++ + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", + part_type); + add_mtd_partitions(impa7_mtd[i], +- mtd_parts, mtd_parts_nb); +- } ++ mtd_parts[i], mtd_parts_nb[i]); ++#else ++ add_mtd_device(impa7_mtd[i]); ++ + #endif + } + else +- iounmap((void *)impa7_map[i].map_priv_1); ++ iounmap((void *)impa7_map[i].virt); + } + return devicesfound == 0 ? -ENXIO : 0; + } +@@ -211,17 +140,16 @@ + static void __exit cleanup_impa7(void) + { + int i; +- for (i=0; i ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++// board specific stuff - sorry, it should be in arch/arm/mach-*. ++#ifdef CONFIG_ARCH_INTEGRATOR ++ ++#define FLASH_BASE INTEGRATOR_FLASH_BASE ++#define FLASH_SIZE INTEGRATOR_FLASH_SIZE ++ ++#define FLASH_PART_SIZE 0x400000 ++ ++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) ++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) ++#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) ++#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) ++ ++/* ++ * Initialise the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Set write enable bit in EBI reg ++ */ ++static void armflash_flash_init(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++/* ++ * Shutdown the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Clear write enable bit in EBI reg ++ */ ++static void armflash_flash_exit(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ /* ++ * Clear the write enable bit in system controller EBI register. ++ */ ++ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++static void armflash_flash_wp(int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLC; ++ else ++ reg = SC_CTRLS; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLS; ++ else ++ reg = SC_CTRLC; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); ++} ++#endif ++ ++#ifdef CONFIG_ARCH_P720T ++ ++#define FLASH_BASE (0x04000000) ++#define FLASH_SIZE (64*1024*1024) ++ ++#define FLASH_PART_SIZE (4*1024*1024) ++#define FLASH_BLOCK_SIZE (128*1024) ++ ++static void armflash_flash_init(void) ++{ ++} ++ ++static void armflash_flash_exit(void) ++{ ++} ++ ++static void armflash_flash_wp(int on) ++{ ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++} ++#endif ++ ++ ++static struct map_info armflash_map = ++{ ++ .name = "AFS", ++ .set_vpp = armflash_set_vpp, ++ .phys = FLASH_BASE, ++}; ++ ++static struct mtd_info *mtd; ++static struct mtd_partition *parts; ++static const char *probes[] = { "RedBoot", "afs", NULL }; ++ ++static int __init armflash_cfi_init(void *base, u_int size) ++{ ++ int ret; ++ ++ armflash_flash_init(); ++ armflash_flash_wp(1); ++ ++ /* ++ * look for CFI based flash parts fitted to this board ++ */ ++ armflash_map.size = size; ++ armflash_map.buswidth = 4; ++ armflash_map.virt = (unsigned long) base; ++ ++ simple_map_init(&armflash_map); ++ ++ /* ++ * Also, the CFI layer automatically works out what size ++ * of chips we have, and does the necessary identification ++ * for us automatically. ++ */ ++ mtd = do_map_probe("cfi_probe", &armflash_map); ++ if (!mtd) ++ return -ENXIO; ++ ++ mtd->owner = THIS_MODULE; ++ ++ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0); ++ if (ret > 0) { ++ ret = add_mtd_partitions(mtd, parts, ret); ++ if (ret) ++ printk(KERN_ERR "mtd partition registration " ++ "failed: %d\n", ret); ++ } ++ ++ /* ++ * If we got an error, free all resources. ++ */ ++ if (ret < 0) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ ++ return ret; ++} ++ ++static void armflash_cfi_exit(void) ++{ ++ if (mtd) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ if (parts) ++ kfree(parts); ++} ++ ++static int __init armflash_init(void) ++{ ++ int err = -EBUSY; ++ void *base; ++ ++ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) ++ goto out; ++ ++ base = ioremap(FLASH_BASE, FLASH_SIZE); ++ err = -ENOMEM; ++ if (base == NULL) ++ goto release; ++ ++ err = armflash_cfi_init(base, FLASH_SIZE); ++ if (err) { ++ iounmap(base); ++release: ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ } ++out: ++ return err; ++} ++ ++static void __exit armflash_exit(void) ++{ ++ armflash_cfi_exit(); ++ iounmap((void *)armflash_map.virt); ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ armflash_flash_exit(); ++} ++ ++module_init(armflash_init); ++module_exit(armflash_exit); ++ ++MODULE_AUTHOR("ARM Ltd"); ++MODULE_DESCRIPTION("ARM Integrator CFI map driver"); ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c linux/drivers/mtd/maps/integrator-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/integrator-flash.c 2004-11-19 10:25:11.923196144 +0100 +@@ -1,8 +1,9 @@ + /*====================================================================== + +- drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning ++ drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver + + Copyright (C) 2000 ARM Limited ++ Copyright (C) 2003 Deep Blue Solutions Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -21,7 +22,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id: integrator-flash.c,v 1.7 2001/11/01 20:55:47 rmk Exp $ ++ $Id: integrator-flash.c,v 1.14 2003/10/11 10:00:31 rmk Exp $ + + ======================================================================*/ + +@@ -31,268 +32,181 @@ + #include + #include + #include ++#include + #include + + #include + #include + #include + ++#include + #include + #include + #include + +-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); +- +-// board specific stuff - sorry, it should be in arch/arm/mach-*. +-#ifdef CONFIG_ARCH_INTEGRATOR +- +-#define FLASH_BASE INTEGRATOR_FLASH_BASE +-#define FLASH_SIZE INTEGRATOR_FLASH_SIZE +- +-#define FLASH_PART_SIZE 0x400000 +- +-#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +-#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) +-#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) +-#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) +- +-/* +- * Initialise the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Set write enable bit in EBI reg +- */ +-static void armflash_flash_init(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-/* +- * Shutdown the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Clear write enable bit in EBI reg +- */ +-static void armflash_flash_exit(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- /* +- * Clear the write enable bit in system controller EBI register. +- */ +- tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-static void armflash_flash_wp(int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLC; +- else +- reg = SC_CTRLS; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLS; +- else +- reg = SC_CTRLC; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); +-} +-#endif +- + #ifdef CONFIG_ARCH_P720T +- + #define FLASH_BASE (0x04000000) + #define FLASH_SIZE (64*1024*1024) +- +-#define FLASH_PART_SIZE (4*1024*1024) +-#define FLASH_BLOCK_SIZE (128*1024) +- +-static void armflash_flash_init(void) +-{ +-} +- +-static void armflash_flash_exit(void) +-{ +-} +- +-static void armflash_flash_wp(int on) +-{ +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +-} + #endif + +-static __u8 armflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(ofs + map->map_priv_2); +-} +- +-static __u16 armflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(ofs + map->map_priv_2); +-} ++struct armflash_info { ++ struct flash_platform_data *plat; ++ struct resource *res; ++ struct mtd_partition *parts; ++ struct mtd_info *mtd; ++ struct map_info map; ++}; + +-static __u32 armflash_read32(struct map_info *map, unsigned long ofs) ++static void armflash_set_vpp(struct map_info *map, int on) + { +- return readl(ofs + map->map_priv_2); +-} ++ struct armflash_info *info = container_of(map, struct armflash_info, map); + +-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *) (from + map->map_priv_2), len); ++ if (info->plat && info->plat->set_vpp) ++ info->plat->set_vpp(on); + } + +-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, adr + map->map_priv_2); +-} +- +-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, adr + map->map_priv_2); +-} ++static const char *probes[] = { "RedBoot", "afs", NULL }; + +-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr) ++static int armflash_probe(struct device *_dev) + { +- writel(d, adr + map->map_priv_2); +-} ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct resource *res = dev->resource; ++ unsigned int size = res->end - res->start + 1; ++ struct armflash_info *info; ++ int err; ++ void *base; + +-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (to + map->map_priv_2), from, len); +-} ++ info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); ++ if (!info) { ++ err = -ENOMEM; ++ goto out; ++ } + +-static struct map_info armflash_map = +-{ +- name: "AFS", +- read8: armflash_read8, +- read16: armflash_read16, +- read32: armflash_read32, +- copy_from: armflash_copy_from, +- write8: armflash_write8, +- write16: armflash_write16, +- write32: armflash_write32, +- copy_to: armflash_copy_to, +- set_vpp: armflash_set_vpp, +-}; ++ memset(info, 0, sizeof(struct armflash_info)); + +-static struct mtd_info *mtd; +-static struct mtd_partition *parts; ++ info->plat = plat; ++ if (plat && plat->init) { ++ err = plat->init(); ++ if (err) ++ goto no_resource; ++ } + +-static int __init armflash_cfi_init(void *base, u_int size) +-{ +- int ret; ++ info->res = request_mem_region(res->start, size, "armflash"); ++ if (!info->res) { ++ err = -EBUSY; ++ goto no_resource; ++ } + +- armflash_flash_init(); +- armflash_flash_wp(1); ++ base = ioremap(res->start, size); ++ if (!base) { ++ err = -ENOMEM; ++ goto no_mem; ++ } + + /* + * look for CFI based flash parts fitted to this board + */ +- armflash_map.size = size; +- armflash_map.buswidth = 4; +- armflash_map.map_priv_2 = (unsigned long) base; ++ info->map.size = size; ++ info->map.buswidth = plat->width; ++ info->map.phys = res->start; ++ info->map.virt = (unsigned long) base; ++ info->map.name = dev->dev.bus_id; ++ info->map.set_vpp = armflash_set_vpp; ++ ++ simple_map_init(&info->map); + + /* + * Also, the CFI layer automatically works out what size + * of chips we have, and does the necessary identification + * for us automatically. + */ +- mtd = do_map_probe("cfi_probe", &armflash_map); +- if (!mtd) +- return -ENXIO; +- +- mtd->module = THIS_MODULE; +- +- ret = parse_afs_partitions(mtd, &parts); +- if (ret > 0) { +- ret = add_mtd_partitions(mtd, parts, ret); +- if (ret) +- printk(KERN_ERR "mtd partition registration " +- "failed: %d\n", ret); ++ info->mtd = do_map_probe(plat->map_name, &info->map); ++ if (!info->mtd) { ++ err = -ENXIO; ++ goto no_device; + } + ++ info->mtd->owner = THIS_MODULE; ++ ++ err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->parts, err); ++ if (err) ++ printk(KERN_ERR ++ "mtd partition registration failed: %d\n", err); ++ } ++ ++ if (err == 0) ++ dev_set_drvdata(&dev->dev, info); ++ + /* + * If we got an error, free all resources. + */ +- if (ret < 0) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ if (err < 0) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); + } ++ if (info->parts) ++ kfree(info->parts); + +- return ret; +-} +- +-static void armflash_cfi_exit(void) +-{ +- if (mtd) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ no_device: ++ iounmap(base); ++ no_mem: ++ release_mem_region(res->start, size); ++ no_resource: ++ if (plat && plat->exit) ++ plat->exit(); ++ kfree(info); + } +- if (parts) +- kfree(parts); ++ out: ++ return err; + } + +-static int __init armflash_init(void) ++static int armflash_remove(struct device *_dev) + { +- int err = -EBUSY; +- void *base; ++ struct platform_device *dev = to_platform_device(_dev); ++ struct armflash_info *info = dev_get_drvdata(&dev->dev); + +- if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) +- goto out; ++ dev_set_drvdata(&dev->dev, NULL); + +- base = ioremap(FLASH_BASE, FLASH_SIZE); +- err = -ENOMEM; +- if (base == NULL) +- goto release; ++ if (info) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ if (info->parts) ++ kfree(info->parts); + +- err = armflash_cfi_init(base, FLASH_SIZE); +- if (err) { +- iounmap(base); +-release: +- release_mem_region(FLASH_BASE, FLASH_SIZE); ++ iounmap((void *)info->map.virt); ++ release_resource(info->res); ++ kfree(info->res); ++ ++ if (info->plat && info->plat->exit) ++ info->plat->exit(); ++ ++ kfree(info); + } +-out: +- return err; ++ ++ return 0; ++} ++ ++static struct device_driver armflash_driver = { ++ .name = "armflash", ++ .bus = &platform_bus_type, ++ .probe = armflash_probe, ++ .remove = armflash_remove, ++}; ++ ++static int __init armflash_init(void) ++{ ++ return driver_register(&armflash_driver); + } + + static void __exit armflash_exit(void) + { +- armflash_cfi_exit(); +- iounmap((void *)armflash_map.map_priv_2); +- release_mem_region(FLASH_BASE, FLASH_SIZE); +- armflash_flash_exit(); ++ driver_unregister(&armflash_driver); + } + + module_init(armflash_init); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/iq80310.c linux/drivers/mtd/maps/iq80310.c +--- linux-mips-2.4.27/drivers/mtd/maps/iq80310.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/iq80310.c 2004-11-19 10:25:11.924195992 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $ ++ * $Id: iq80310.c,v 1.17 2003/06/23 11:48:18 dwmw2 Exp $ + * + * Mapping for the Intel XScale IQ80310 evaluation board + * +@@ -14,6 +14,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -26,127 +28,72 @@ + + static struct mtd_info *mymtd; + +-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- + static struct map_info iq80310_map = { +- name: "IQ80310 flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: iq80310_read8, +- read16: iq80310_read16, +- read32: iq80310_read32, +- copy_from: iq80310_copy_from, +- write8: iq80310_write8, +- write16: iq80310_write16, +- write32: iq80310_write32, +- copy_to: iq80310_copy_to ++ .name = "IQ80310 flash", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR + }; + + static struct mtd_partition iq80310_partitions[4] = { + { +- name: "Firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ +- name: "Kernel", +- size: 0x000a0000, +- offset: 0x00080000, ++ .name = "Kernel", ++ .size = 0x000a0000, ++ .offset = 0x00080000, + },{ +- name: "Filesystem", +- size: 0x00600000, +- offset: 0x00120000 ++ .name = "Filesystem", ++ .size = 0x00600000, ++ .offset = 0x00120000 + },{ +- name: "RedBoot", +- size: 0x000e0000, +- offset: 0x00720000, +- mask_flags: MTD_WRITEABLE ++ .name = "RedBoot", ++ .size = 0x000e0000, ++ .offset = 0x00720000, ++ .mask_flags = MTD_WRITEABLE + } + }; + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + static struct mtd_info *mymtd; + static struct mtd_partition *parsed_parts; +- +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + static int __init init_iq80310(void) + { + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; +- char *part_type = "static"; ++ int ret; + +- iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); +- if (!iq80310_map.map_priv_1) { ++ iq80310_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ if (!iq80310_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&iq80310_map); ++ + mymtd = do_map_probe("cfi_probe", &iq80310_map); + if (!mymtd) { +- iounmap((void *)iq80310_map.map_priv_1); ++ iounmap((void *)iq80310_map.virt); + return -ENXIO; + } +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); + +- if (ret > 0) { +- part_type = "RedBoot"; ++ if (ret > 0) + parsed_nr_parts = ret; +- } +- } +-#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } else { + parts = iq80310_partitions; +- nb_parts = NB_OF(iq80310_partitions); ++ nb_parts = ARRAY_SIZE(iq80310_partitions); + } +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; + } +@@ -159,8 +106,8 @@ + if (parsed_parts) + kfree(parsed_parts); + } +- if (iq80310_map.map_priv_1) +- iounmap((void *)iq80310_map.map_priv_1); ++ if (iq80310_map.virt) ++ iounmap((void *)iq80310_map.virt); + } + + module_init(init_iq80310); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ixp425.c linux/drivers/mtd/maps/ixp425.c +--- linux-mips-2.4.27/drivers/mtd/maps/ixp425.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ixp425.c 2004-11-19 10:25:11.925195840 +0100 +@@ -0,0 +1,220 @@ ++/* ++ * $Id: ixp425.c,v 1.1 2003/11/26 21:26:09 dsaxena Exp $ ++ * ++ * drivers/mtd/maps/ixp425.c ++ * ++ * MTD Map file for IXP425 based systems. Please do not make per-board ++ * map driver as the code will be 90% identical. For now just add ++ * if(machine_is_XXX()) checks to the code. I'll clean this stuff to ++ * use platform_data in the the future so we can get rid of that too. ++ * ++ * Original Author: Intel Corporation ++ * Maintainer: Deepak Saxena ++ * ++ * Copyright (C) 2002 Intel Corporation ++ * Copyright (C) 2003 MontaVista Software, Inc. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define WINDOW_ADDR 0x50000000 ++#define BUSWIDTH 2 ++ ++#ifndef __ARMEB__ ++#define BYTE0(h) ((h) & 0xFF) ++#define BYTE1(h) (((h) >> 8) & 0xFF) ++#else ++#define BYTE0(h) (((h) >> 8) & 0xFF) ++#define BYTE1(h) ((h) & 0xFF) ++#endif ++ ++static __u16 ++ixp425_read16(struct map_info *map, unsigned long ofs) ++{ ++ return *(__u16 *) (map->map_priv_1 + ofs); ++} ++ ++/* ++ * The IXP425 expansion bus only allows 16-bit wide acceses ++ * when attached to a 16-bit wide device (such as the 28F128J3A), ++ * so we can't just memcpy_fromio(). ++ */ ++static void ++ixp425_copy_from(struct map_info *map, void *to, ++ unsigned long from, ssize_t len) ++{ ++ int i; ++ u8 *dest = (u8 *) to; ++ u16 *src = (u16 *) (map->map_priv_1 + from); ++ u16 data; ++ ++ for (i = 0; i < (len / 2); i++) { ++ data = src[i]; ++ dest[i * 2] = BYTE0(data); ++ dest[i * 2 + 1] = BYTE1(data); ++ } ++ ++ if (len & 1) ++ dest[len - 1] = BYTE0(src[i]); ++} ++ ++static void ++ixp425_write16(struct map_info *map, __u16 d, unsigned long adr) ++{ ++ *(__u16 *) (map->map_priv_1 + adr) = d; ++} ++ ++static struct map_info ixp425_map = { ++ .name = "IXP425 Flash", ++ .buswidth = BUSWIDTH, ++ .read16 = ixp425_read16, ++ .copy_from = ixp425_copy_from, ++ .write16 = ixp425_write16, ++}; ++ ++/* ++ * Put flash back in read mode so RedBoot can boot properly. ++ */ ++int ixp425_mtd_reboot(struct notifier_block *n, unsigned long code, void *p) ++{ ++ if (code != SYS_RESTART) ++ return NOTIFY_DONE; ++ ++ ixp425_write16(&ixp425_map, 0xff, 0x55 * 0x2); ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block ixp425_mtd_notifier = { ++ notifier_call:ixp425_mtd_reboot, ++ next:NULL, ++ priority:0 ++}; ++ ++static struct mtd_partition *parsed_parts; ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static struct mtd_partition ixp425_partitions[] = { ++ { ++ .name = "image", ++ .offset = 0x00040000, ++ .size = 0x00400000, ++ }, { ++ .name = "user", ++ .offset = 0x00440000, ++ .size = MTDPART_SIZ_FULL ++ } ++}; ++ ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++ ++static struct mtd_info *ixp425_mtd; ++static struct resource *mtd_resource; ++ ++static void ++ixp425_exit(void) ++{ ++ if (ixp425_mtd) { ++ del_mtd_partitions(ixp425_mtd); ++ map_destroy(ixp425_mtd); ++ } ++ if (ixp425_map.map_priv_1) ++ iounmap((void *) ixp425_map.map_priv_1); ++ if (mtd_resource) ++ release_mem_region(WINDOW_ADDR, ixp425_map.size); ++ ++ if (parsed_parts) ++ kfree(parsed_parts); ++ ++ unregister_reboot_notifier(&ixp425_mtd_notifier); ++ ++ /* Disable flash write */ ++ *IXP425_EXP_CS0 &= ~IXP425_FLASH_WRITABLE; ++ ++ if(machine_is_adi_coyote()) ++ *IXP425_EXP_CS1 &= ~IXP425_FLASH_WRITABLE; ++} ++ ++static int __init ++ixp425_init(void) ++{ ++ int res = -1, npart; ++ ++ /* Enable flash write */ ++ *IXP425_EXP_CS0 |= IXP425_FLASH_WRITABLE; ++ ++ /* ++ * Coyote requires CS1 write to be enabled and has 32MB flash. ++ * This will move to the platform init code in 2.6 ++ */ ++ if(machine_is_adi_coyote()) { ++ *IXP425_EXP_CS1 |= IXP425_FLASH_WRITABLE; ++ ixp425_map.size = 0x02000000; ++ } else ++ ixp425_map.size = 0x01000000; ++ ++ ixp425_map.map_priv_1 = 0; ++ mtd_resource = ++ request_mem_region(WINDOW_ADDR, ixp425_map.size, "IXP425 Flash"); ++ if (!mtd_resource) { ++ printk(KERN_ERR ++ "ixp425 flash: Could not request mem region.\n"); ++ res = -ENOMEM; ++ goto Error; ++ } ++ ++ ixp425_map.map_priv_1 = ++ (unsigned long) ioremap(WINDOW_ADDR, ixp425_map.size); ++ if (!ixp425_map.map_priv_1) { ++ printk("ixp425 Flash: Failed to map IO region. (ioremap)\n"); ++ res = -EIO; ++ goto Error; ++ } ++ ++ ixp425_mtd = do_map_probe("cfi_probe", &ixp425_map); ++ if (!ixp425_mtd) { ++ res = -ENXIO; ++ goto Error; ++ } ++ ixp425_mtd->owner = THIS_MODULE; ++ ++ /* Try to parse RedBoot partitions */ ++ npart = parse_mtd_partitions(ixp425_mtd, probes, &parsed_parts, 0); ++ if (npart > 0) ++ res = add_mtd_partitions(ixp425_mtd, parsed_parts, npart); ++ else { ++ printk("IXP425 Flash: Using static MTD partitions.\n"); ++ res = add_mtd_partitions(ixp425_mtd, ixp425_partitions, ++ NB_OF(ixp425_partitions)); ++ } ++ ++ if (res) ++ goto Error; ++ ++ register_reboot_notifier(&ixp425_mtd_notifier); ++ ++ return res; ++ ++Error: ++ ixp425_exit(); ++ return res; ++} ++ ++module_init(ixp425_init); ++module_exit(ixp425_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MTD map driver for ixp425 evaluation board"); ++MODULE_AUTHOR("Deepak Saxena"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/l440gx.c linux/drivers/mtd/maps/l440gx.c +--- linux-mips-2.4.27/drivers/mtd/maps/l440gx.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/l440gx.c 2004-11-19 10:25:11.927195536 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $ ++ * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * + * BIOS Flash chip on Intel 440GX board. + * +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,48 +28,6 @@ + + static struct mtd_info *mymtd; + +-__u8 l440gx_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 l440gx_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 l440gx_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + /* Is this really the vpp port? */ + void l440gx_set_vpp(struct map_info *map, int vpp) +@@ -85,22 +44,15 @@ + } + + struct map_info l440gx_map = { +- name: "L440GX BIOS", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: l440gx_read8, +- read16: l440gx_read16, +- read32: l440gx_read32, +- copy_from: l440gx_copy_from, +- write8: l440gx_write8, +- write16: l440gx_write16, +- write32: l440gx_write32, +- copy_to: l440gx_copy_to, ++ .name = "L440GX BIOS", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR, + #if 0 + /* FIXME verify that this is the + * appripriate code for vpp enable/disable + */ +- set_vpp: l440gx_set_vpp ++ .set_vpp = l440gx_set_vpp + #endif + }; + +@@ -113,7 +65,6 @@ + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, NULL); + +- + pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + +@@ -122,15 +73,14 @@ + return -ENODEV; + } + ++ l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); + +- l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); +- +- if (!l440gx_map.map_priv_1) { ++ if (!l440gx_map.virt) { + printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); + return -ENOMEM; + } +- +- printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1); ++ simple_map_init(&l440gx_map); ++ printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); + + /* Setup the pm iobase resource + * This code should move into some kind of generic bridge +@@ -153,7 +103,7 @@ + /* Allocate the resource region */ + if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { + printk(KERN_WARNING "Could not allocate pm iobase resource\n"); +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + return -ENXIO; + } + } +@@ -181,13 +131,13 @@ + mymtd = do_map_probe("map_rom", &l440gx_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + return -ENXIO; + } + +@@ -196,7 +146,7 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + } + + module_init(init_l440gx); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lasat.c linux/drivers/mtd/maps/lasat.c +--- linux-mips-2.4.27/drivers/mtd/maps/lasat.c 2003-08-18 04:59:02.000000000 +0200 ++++ linux/drivers/mtd/maps/lasat.c 2004-11-19 10:25:11.928195384 +0100 +@@ -1,11 +1,20 @@ + /* +- * Flash device on lasat 100 and 200 boards ++ * Flash device on Lasat 100 and 200 boards ++ * ++ * (C) 2002 Brian Murphy ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * $Id: lasat.c,v 1.6 2003/09/02 16:36:40 brm Exp $ + * + */ + + #include + #include + #include ++#include + #include + #include + #include +@@ -13,123 +22,80 @@ + #include + #include + +-static struct mtd_info *mymtd; +- +-static __u8 sp_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sp_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sp_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++static struct mtd_info *lasat_mtd; + +-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++static struct mtd_partition partition_info[LASAT_MTD_LAST]; ++static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; + +-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++static void lasat_set_vpp(struct map_info *map, int vpp) + { +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (vpp) ++ *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ else ++ *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit); + } + +-static struct map_info sp_map = { +- name: "SP flash", +- buswidth: 4, +- read8: sp_read8, +- read16: sp_read16, +- read32: sp_read32, +- copy_from: sp_copy_from, +- write8: sp_write8, +- write16: sp_write16, +- write32: sp_write32, +- copy_to: sp_copy_to ++static struct map_info lasat_map = { ++ .name = "LASAT flash", ++ .buswidth = 4, ++ .set_vpp = lasat_set_vpp + }; + +-static struct mtd_partition partition_info[LASAT_MTD_LAST]; +-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Config", "Filesystem"}; +- +-static int __init init_sp(void) ++static int __init init_lasat(void) + { + int i; +- int nparts = 0; +- /* this does not play well with the old flash code which +- * protects and uprotects the flash when necessary */ ++ /* since we use AMD chips and set_vpp is not implimented ++ * for these (yet) we still have to permanently enable flash write */ + printk(KERN_NOTICE "Unprotecting flash\n"); +- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ ENABLE_VPP((&lasat_map)); + +- sp_map.map_priv_1 = ioremap_nocache( +- lasat_flash_partition_start(LASAT_MTD_BOOTLOADER), +- lasat_board_info.li_flash_size); +- sp_map.size = lasat_board_info.li_flash_size; ++ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); ++ lasat_map.virt = (unsigned long)ioremap_nocache( ++ lasat_map.phys, lasat_board_info.li_flash_size); ++ lasat_map.size = lasat_board_info.li_flash_size; + +- printk(KERN_NOTICE "sp flash device: %lx at %lx\n", +- sp_map.size, sp_map.map_priv_1); ++ simple_map_init(&lasat_map); + + for (i=0; i < LASAT_MTD_LAST; i++) + partition_info[i].name = lasat_mtd_partnames[i]; + +- mymtd = do_map_probe("cfi_probe", &sp_map); +- if (mymtd) { ++ lasat_mtd = do_map_probe("cfi_probe", &lasat_map); ++ ++ if (!lasat_mtd) ++ lasat_mtd = do_map_probe("jedec_probe", &lasat_map); ++ ++ if (lasat_mtd) { + u32 size, offset = 0; + +- mymtd->module = THIS_MODULE; ++ lasat_mtd->owner = THIS_MODULE; + + for (i=0; i < LASAT_MTD_LAST; i++) { + size = lasat_flash_partition_size(i); +- if (size != 0) { +- nparts++; + partition_info[i].size = size; + partition_info[i].offset = offset; + offset += size; + } +- } + +- add_mtd_partitions( mymtd, partition_info, nparts ); ++ add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST ); + return 0; + } + + return -ENXIO; + } + +-static void __exit cleanup_sp(void) ++static void __exit cleanup_lasat(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); ++ if (lasat_mtd) { ++ del_mtd_partitions(lasat_mtd); ++ map_destroy(lasat_mtd); + } +- if (sp_map.map_priv_1) { +- sp_map.map_priv_1 = 0; ++ if (lasat_map.virt) { ++ lasat_map.virt = 0; + } + } + +-module_init(init_sp); +-module_exit(cleanup_sp); ++module_init(init_lasat); ++module_exit(cleanup_lasat); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Brian Murphy "); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c linux/drivers/mtd/maps/lubbock-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/lubbock-flash.c 2004-11-19 10:25:11.930195080 +0100 +@@ -0,0 +1,151 @@ ++/* ++ * $Id: lubbock-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ ++ * ++ * Map driver for the Lubbock developer platform. ++ * ++ * Author: Nicolas Pitre ++ * Copyright: (C) 2001 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define ROM_ADDR 0x00000000 ++#define FLASH_ADDR 0x04000000 ++ ++#define WINDOW_SIZE 64*1024*1024 ++ ++static struct map_info lubbock_maps[2] = { { ++ .size = WINDOW_SIZE, ++ .phys = 0x00000000, ++}, { ++ .size = WINDOW_SIZE, ++ .phys = 0x04000000, ++} }; ++ ++static struct mtd_partition lubbock_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ },{ ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00040000, ++ },{ ++ .name = "Filesystem", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00140000 ++ } ++}; ++ ++static struct mtd_info *mymtds[2]; ++static struct mtd_partition *parsed_parts[2]; ++static int nr_parsed_parts[2]; ++ ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static int __init init_lubbock(void) ++{ ++ int flashboot = (CONF_SWITCHES & 1); ++ int ret = 0, i; ++ ++ lubbock_maps[0].buswidth = lubbock_maps[1].buswidth = ++ (BOOT_DEF & 1) ? 2 : 4; ++ ++ /* Compensate for the nROMBT switch which swaps the flash banks */ ++ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", ++ flashboot?"Flash":"ROM", flashboot); ++ ++ lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; ++ lubbock_maps[flashboot].name = "Lubbock Boot ROM"; ++ ++ for (i = 0; i < 2; i++) { ++ lubbock_maps[i].virt = (unsigned long)__ioremap(lubbock_maps[i].phys, WINDOW_SIZE, 0); ++ if (!lubbock_maps[i].virt) { ++ printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); ++ if (!ret) ++ ret = -ENOMEM; ++ continue; ++ } ++ simple_map_init(&lubbock_maps[i]); ++ ++ printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n", ++ lubbock_maps[i].name, lubbock_maps[i].phys, ++ lubbock_maps[i].buswidth * 8); ++ ++ mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); ++ ++ if (!mymtds[i]) { ++ iounmap((void *)lubbock_maps[i].virt); ++ if (!ret) ++ ret = -EIO; ++ continue; ++ } ++ mymtds[i]->owner = THIS_MODULE; ++ ++ int ret = parse_mtd_partitions(mymtds[i], probes, ++ &parsed_parts[i], 0); ++ ++ if (ret > 0) ++ nr_parsed_parts[i] = ret; ++ } ++ ++ if (!mymtds[0] && !mymtds[1]) ++ return ret; ++ ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) { ++ printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); ++ } else if (nr_parsed_parts[i]) { ++ add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); ++ } else if (!i) { ++ printk("Using static partitions on %s\n", lubbock_maps[i].name); ++ add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); ++ } else { ++ printk("Registering %s as whole device\n", lubbock_maps[i].name); ++ add_mtd_device(mymtds[i]); ++ } ++ } ++ return 0; ++} ++ ++static void __exit cleanup_lubbock(void) ++{ ++ int i; ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) ++ continue; ++ ++ if (nr_parsed_parts[i] || !i) ++ del_mtd_partitions(mymtds[i]); ++ else ++ del_mtd_device(mymtds[i]); ++ ++ map_destroy(mymtds[i]); ++ iounmap((void *)lubbock_maps[i].virt); ++ ++ if (parsed_parts[i]) ++ kfree(parsed_parts[i]); ++ } ++} ++ ++module_init(init_lubbock); ++module_exit(cleanup_lubbock); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nicolas Pitre "); ++MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c linux/drivers/mtd/maps/map_funcs.c +--- linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/map_funcs.c 2004-11-19 10:25:11.931194928 +0100 +@@ -0,0 +1,96 @@ ++/* ++ * $Id: map_funcs.c,v 1.3 2003/11/14 19:50:04 thayne Exp $ ++ * ++ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS ++ * is enabled. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static u8 simple_map_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->virt + ofs); ++} ++ ++static u16 simple_map_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->virt + ofs); ++} ++ ++static u32 simple_map_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->virt + ofs); ++} ++ ++static u64 simple_map_read64(struct map_info *map, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++ return 0; ++#else ++ return __raw_readll(map->virt + ofs); ++#endif ++} ++ ++static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs) ++{ ++ __raw_writeb(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs) ++{ ++ __raw_writew(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs) ++{ ++ __raw_writel(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++#else ++ __raw_writell(datum, map->virt + ofs); ++ mb(); ++#endif /* CFI_B8 */ ++} ++ ++static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->virt + from, len); ++} ++ ++static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->virt + to, from, len); ++} ++ ++void simple_map_init(struct map_info *map) ++{ ++ map->read8 = simple_map_read8; ++ map->read16 = simple_map_read16; ++ map->read32 = simple_map_read32; ++ map->read64 = simple_map_read64; ++ map->write8 = simple_map_write8; ++ map->write16 = simple_map_write16; ++ map->write32 = simple_map_write32; ++ map->write64 = simple_map_write64; ++ map->copy_from = simple_map_copy_from; ++ map->copy_to = simple_map_copy_to; ++} ++ ++EXPORT_SYMBOL(simple_map_init); ++ ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c +--- linux-mips-2.4.27/drivers/mtd/maps/mbx860.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/mbx860.c 2004-11-19 10:25:11.933194624 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $ ++ * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Handle mapping of the flash on MBX860 boards + * +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,91 +37,46 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "MBX flash BOOT partition", +- offset: 0, +- size: BOOT_PARTITION_SIZE_KiB*1024 }, +- { name: "MBX flash DATA partition", +- offset: BOOT_PARTITION_SIZE_KiB*1024, +- size: (KERNEL_PARTITION_SIZE_KiB)*1024 }, +- { name: "MBX flash APPLICATION partition", +- offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } ++ { .name = "MBX flash BOOT partition", ++ .offset = 0, ++ .size = BOOT_PARTITION_SIZE_KiB*1024 }, ++ { .name = "MBX flash DATA partition", ++ .offset = BOOT_PARTITION_SIZE_KiB*1024, ++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024 }, ++ { .name = "MBX flash APPLICATION partition", ++ .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } + }; + + + static struct mtd_info *mymtd; + +-__u8 mbx_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-__u16 mbx_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-__u32 mbx_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + struct map_info mbx_map = { +- name: "MBX flash", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: mbx_read8, +- read16: mbx_read16, +- read32: mbx_read32, +- copy_from: mbx_copy_from, +- write8: mbx_write8, +- write16: mbx_write16, +- write32: mbx_write32, +- copy_to: mbx_copy_to ++ .name = "MBX flash", ++ .size = WINDOW_SIZE, ++ .phys = WINDOW_ADDR, ++ .buswidth = 4, + }; + + int __init init_mbx(void) + { +- printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); ++ mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!mbx_map.map_priv_1) { ++ if (!mbx_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&mbx_map); ++ + mymtd = do_map_probe("jedec_probe", &mbx_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + return 0; + } + +- iounmap((void *)mbx_map.map_priv_1); ++ iounmap((void *)mbx_map.virt); + return -ENXIO; + } + +@@ -130,9 +86,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (mbx_map.map_priv_1) { +- iounmap((void *)mbx_map.map_priv_1); +- mbx_map.map_priv_1 = 0; ++ if (mbx_map.virt) { ++ iounmap((void *)mbx_map.virt); ++ mbx_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c linux/drivers/mtd/maps/mpc1211.c +--- linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/mpc1211.c 2004-11-19 10:25:11.934194472 +0100 +@@ -0,0 +1,79 @@ ++/* ++ * Flash on MPC-1211 ++ * ++ * (C) 2002 Interface, Saito.K & Jeanne ++ * ++ * GPL'd ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *flash_mtd; ++static struct mtd_partition *parsed_parts; ++ ++struct map_info mpc1211_flash_map = { ++ .name = "MPC-1211 FLASH", ++ .size = 0x80000, ++ .buswidth = 1, ++}; ++ ++static struct mtd_partition mpc1211_partitions[] = { ++ { ++ .name = "IPL & ETH-BOOT", ++ .offset = 0x00000000, ++ .size = 0x10000, ++ }, ++ { ++ .name = "Flash FS", ++ .offset = 0x00010000, ++ .size = MTDPART_SIZ_FULL, ++ } ++}; ++ ++static int __init init_mpc1211_maps(void) ++{ ++ int nr_parts; ++ ++ mpc1211_flash_map.phys = 0; ++ mpc1211_flash_map.virt = P2SEGADDR(0); ++ ++ simple_map_init(&mpc1211_flash_map); ++ ++ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); ++ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map); ++ if (!flash_mtd) { ++ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n"); ++ return -ENXIO; ++ } ++ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff); ++ flash_mtd->module = THIS_MODULE; ++ ++ parsed_parts = mpc1211_partitions; ++ nr_parts = ARRAY_SIZE(mpc1211_partitions); ++ ++ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); ++ return 0; ++} ++ ++static void __exit cleanup_mpc1211_maps(void) ++{ ++ if (parsed_parts) ++ del_mtd_partitions(flash_mtd); ++ else ++ del_mtd_device(flash_mtd); ++ map_destroy(flash_mtd); ++} ++ ++module_init(init_mpc1211_maps); ++module_exit(cleanup_mpc1211_maps); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Saito.K & Jeanne "); ++MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/netsc520.c linux/drivers/mtd/maps/netsc520.c +--- linux-mips-2.4.27/drivers/mtd/maps/netsc520.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/netsc520.c 2004-11-19 10:25:11.935194320 +0100 +@@ -3,7 +3,7 @@ + * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) + * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH + * +- * $Id: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -50,95 +51,41 @@ + ** recoverable afterwards. + */ + +-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ + { +- name: "NetSc520 boot kernel", +- offset: 0, +- size: 0xc0000 ++ .name = "NetSc520 boot kernel", ++ .offset = 0, ++ .size = 0xc0000 + }, + { +- name: "NetSc520 Low BIOS", +- offset: 0xc0000, +- size: 0x40000 ++ .name = "NetSc520 Low BIOS", ++ .offset = 0xc0000, ++ .size = 0x40000 + }, + { +- name: "NetSc520 file system", +- offset: 0x100000, +- size: 0xe80000 ++ .name = "NetSc520 file system", ++ .offset = 0x100000, ++ .size = 0xe80000 + }, + { +- name: "NetSc520 High BIOS", +- offset: 0xf80000, +- size: 0x80000 ++ .name = "NetSc520 High BIOS", ++ .offset = 0xf80000, ++ .size = 0x80000 + }, + }; + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +-/* +- * If no idea what is going on here. This is taken from the FlashFX stuff. +- */ +-#define ROMCS 1 +- +- + #define WINDOW_SIZE 0x00100000 + #define WINDOW_ADDR 0x00200000 + + static struct map_info netsc520_map = { +- name: "netsc520 Flash Bank", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: netsc520_read8, +- read16: netsc520_read16, +- read32: netsc520_read32, +- copy_from: netsc520_copy_from, +- write8: netsc520_write8, +- write16: netsc520_write16, +- write32: netsc520_write32, +- copy_to: netsc520_copy_to, +- map_priv_2: WINDOW_ADDR ++ .name = "netsc520 Flash Bank", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) +@@ -147,13 +94,16 @@ + + static int __init init_netsc520(void) + { +- printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2); +- netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size); ++ printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); ++ netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size); + +- if (!netsc520_map.map_priv_1) { ++ if (!netsc520_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&netsc520_map); ++ + mymtd = do_map_probe("cfi_probe", &netsc520_map); + if(!mymtd) + mymtd = do_map_probe("map_ram", &netsc520_map); +@@ -161,11 +111,11 @@ + mymtd = do_map_probe("map_rom", &netsc520_map); + + if (!mymtd) { +- iounmap((void *)netsc520_map.map_priv_1); ++ iounmap((void *)netsc520_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); + return 0; + } +@@ -176,9 +126,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (netsc520_map.map_priv_1) { +- iounmap((void *)netsc520_map.map_priv_1); +- netsc520_map.map_priv_1 = 0; ++ if (netsc520_map.virt) { ++ iounmap((void *)netsc520_map.virt); ++ netsc520_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/nettel.c linux/drivers/mtd/maps/nettel.c +--- linux-mips-2.4.27/drivers/mtd/maps/nettel.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/nettel.c 2004-11-19 10:25:11.937194016 +0100 +@@ -6,7 +6,7 @@ + * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) + * +- * $Id: nettel.c,v 1.1 2002/08/08 06:30:13 gerg Exp $ ++ * $Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $ + */ + + /****************************************************************************/ +@@ -59,128 +59,72 @@ + + /****************************************************************************/ + +-static __u8 nettel_read8(struct map_info *map, unsigned long ofs) +-{ +- return(readb(map->map_priv_1 + ofs)); +-} +- +-static __u16 nettel_read16(struct map_info *map, unsigned long ofs) +-{ +- return(readw(map->map_priv_1 + ofs)); +-} +- +-static __u32 nettel_read32(struct map_info *map, unsigned long ofs) +-{ +- return(readl(map->map_priv_1 + ofs)); +-} +- +-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + /****************************************************************************/ + + #ifdef CONFIG_MTD_CFI_INTELEXT + static struct map_info nettel_intel_map = { +- name: "SnapGear Intel", +- size: 0, +- buswidth: INTEL_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear Intel", ++ .size = 0, ++ .buswidth = INTEL_BUSWIDTH, + }; + + static struct mtd_partition nettel_intel_partitions[] = { + { +- name: "SnapGear kernel", +- offset: 0, +- size: 0x000e0000 ++ .name = "SnapGear kernel", ++ .offset = 0, ++ .size = 0x000e0000 + }, + { +- name: "SnapGear filesystem", +- offset: 0x00100000, ++ .name = "SnapGear filesystem", ++ .offset = 0x00100000, + }, + { +- name: "SnapGear config", +- offset: 0x000e0000, +- size: 0x00020000 ++ .name = "SnapGear config", ++ .offset = 0x000e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear Intel", +- offset: 0 ++ .name = "SnapGear Intel", ++ .offset = 0 + }, + { +- name: "SnapGear BIOS Config", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS Config", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + }; + #endif + + static struct map_info nettel_amd_map = { +- name: "SnapGear AMD", +- size: AMD_WINDOW_MAXSIZE, +- buswidth: AMD_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear AMD", ++ .size = AMD_WINDOW_MAXSIZE, ++ .buswidth = AMD_BUSWIDTH, + }; + + static struct mtd_partition nettel_amd_partitions[] = { + { +- name: "SnapGear BIOS config", +- offset: 0x000e0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS config", ++ .offset = 0x000e0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x000f0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS", ++ .offset = 0x000f0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear AMD", +- offset: 0 ++ .name = "SnapGear AMD", ++ .offset = 0 + }, + { +- name: "SnapGear high BIOS", +- offset: 0x001f0000, +- size: 0x00010000 ++ .name = "SnapGear high BIOS", ++ .offset = 0x001f0000, ++ .size = 0x00010000 + } + }; + +@@ -328,18 +272,20 @@ + *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); + __asm__ ("wbinvd"); + +- nettel_amd_map.map_priv_1 = (unsigned long) ++ nettel_amd_map.phys = amdaddr; ++ nettel_amd_map.virt = (unsigned long) + ioremap_nocache(amdaddr, maxsize); +- if (!nettel_amd_map.map_priv_1) { ++ if (!nettel_amd_map.virt) { + printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); + return(-EIO); + } ++ simple_map_init(&nettel_amd_map); + + if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { + printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", + amd_mtd->size>>10); + +- amd_mtd->module = THIS_MODULE; ++ amd_mtd->owner = THIS_MODULE; + + /* The high BIOS partition is only present for 2MB units */ + num_amd_partitions = NUM_AMD_PARTITIONS; +@@ -387,8 +333,8 @@ + + /* Destroy useless AMD MTD mapping */ + amd_mtd = NULL; +- iounmap((void *) nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = (unsigned long) NULL; ++ iounmap((void *) nettel_amd_map.virt); ++ nettel_amd_map.virt = (unsigned long) NULL; + #else + /* Only AMD flash supported */ + return(-ENXIO); +@@ -411,16 +357,18 @@ + + /* Probe for the the size of the first Intel flash */ + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) ++ nettel_intel_map.phys = intel0addr; ++ nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ if (!nettel_intel_map.virt) { + printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); + return(-EIO); + } ++ simple_map_init(&nettel_intel_map); + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); + if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -441,19 +389,19 @@ + /* Delete the old map and probe again to do both chips */ + map_destroy(intel_mtd); + intel_mtd = NULL; +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) ++ nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ if (!nettel_intel_map.virt) { + printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); + return(-EIO); + } + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); + if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -468,7 +416,7 @@ + printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", + (intel_mtd->size >> 10)); + +- intel_mtd->module = THIS_MODULE; ++ intel_mtd->owner = THIS_MODULE; + + #ifndef CONFIG_BLK_DEV_INITRD + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); +@@ -523,18 +471,18 @@ + del_mtd_partitions(amd_mtd); + map_destroy(amd_mtd); + } +- if (nettel_amd_map.map_priv_1) { +- iounmap((void *)nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = 0; ++ if (nettel_amd_map.virt) { ++ iounmap((void *)nettel_amd_map.virt); ++ nettel_amd_map.virt = 0; + } + #ifdef CONFIG_MTD_CFI_INTELEXT + if (intel_mtd) { + del_mtd_partitions(intel_mtd); + map_destroy(intel_mtd); + } +- if (nettel_intel_map.map_priv_1) { +- iounmap((void *)nettel_intel_map.map_priv_1); +- nettel_intel_map.map_priv_1 = 0; ++ if (nettel_intel_map.virt) { ++ iounmap((void *)nettel_intel_map.virt); ++ nettel_intel_map.virt = 0; + } + #endif + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ocelot.c linux/drivers/mtd/maps/ocelot.c +--- linux-mips-2.4.27/drivers/mtd/maps/ocelot.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/ocelot.c 2004-11-19 10:25:11.938193864 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Flash on Momenco Ocelot + */ +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -20,47 +21,23 @@ + #define NVRAM_WINDOW_SIZE 0x00007FF0 + #define NVRAM_BUSWIDTH 1 + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- + static unsigned int cacheflush = 0; + + static struct mtd_info *flash_mtd; + static struct mtd_info *nvram_mtd; + +-__u8 ocelot_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- cacheflush = 1; +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- if (cacheflush) { +- dma_cache_inv(map->map_priv_2, map->size); +- cacheflush = 0; +- } +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++ struct map_info *map = (struct map_info *)mtd->priv; ++ size_t done = 0; + +-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ + /* If we use memcpy, it does word-wide writes. Even though we told the + GT64120A that it's an 8-bit wide region, word-wide writes don't work. + We end up just writing the first byte of the four to all four bytes. + So we have this loop instead */ ++ *retlen = len; + while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); ++ __raw_writeb(*(unsigned char *) from, map->virt + to); + from++; + to++; + len--; +@@ -70,24 +47,21 @@ + static struct mtd_partition *parsed_parts; + + struct map_info ocelot_flash_map = { +- name: "Ocelot boot flash", +- size: FLASH_WINDOW_SIZE, +- buswidth: FLASH_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from_cache, +- write8: ocelot_write8, ++ .name = "Ocelot boot flash", ++ .size = FLASH_WINDOW_SIZE, ++ .buswidth = FLASH_BUSWIDTH, ++ .phys = FLASH_WINDOW_ADDR, + }; + + struct map_info ocelot_nvram_map = { +- name: "Ocelot NVRAM", +- size: NVRAM_WINDOW_SIZE, +- buswidth: NVRAM_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from, +- write8: ocelot_write8, +- copy_to: ocelot_copy_to ++ .name = "Ocelot NVRAM", ++ .size = NVRAM_WINDOW_SIZE, ++ .buswidth = NVRAM_BUSWIDTH, ++ .phys = NVRAM_WINDOW_ADDR, + }; + ++static const char *probes[] = { "RedBoot", NULL }; ++ + static int __init init_ocelot_maps(void) + { + void *pld; +@@ -107,12 +81,13 @@ + iounmap(pld); + + /* Now ioremap the NVRAM space */ +- ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); +- if (!ocelot_nvram_map.map_priv_1) { ++ ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); ++ if (!ocelot_nvram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); + return -EIO; + } +- // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1; ++ ++ simple_map_init(&ocelot_nvram_map); + + /* And do the RAM probe on it to get an MTD device */ + nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); +@@ -120,22 +95,21 @@ + printk("NVRAM probe failed\n"); + goto fail_1; + } +- nvram_mtd->module = THIS_MODULE; ++ nvram_mtd->owner = THIS_MODULE; + nvram_mtd->erasesize = 16; ++ /* Override the write() method */ ++ nvram_mtd->write = ocelot_ram_write; + + /* Now map the flash space */ +- ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); +- if (!ocelot_flash_map.map_priv_1) { ++ ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); ++ if (!ocelot_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); + goto fail_2; + } + /* Now the cached version */ +- ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); ++ ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); + +- if (!ocelot_flash_map.map_priv_2) { +- /* Doesn't matter if it failed. Just use the uncached version */ +- ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1; +- } ++ simple_map_init(&ocelot_flash_map); + + /* Only probe for flash if the write jumper is present */ + if (brd_status & 0x40) { +@@ -155,10 +129,10 @@ + + add_mtd_device(nvram_mtd); + +- flash_mtd->module = THIS_MODULE; +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); ++ flash_mtd->owner = THIS_MODULE; ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); + +- if (nr_parts) ++ if (nr_parts > 0) + add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + else + add_mtd_device(flash_mtd); +@@ -166,14 +140,13 @@ + return 0; + + fail3: +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 && +- ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + fail_2: + map_destroy(nvram_mtd); + fail_1: +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + return -ENXIO; + } +@@ -182,16 +155,16 @@ + { + del_mtd_device(nvram_mtd); + map_destroy(nvram_mtd); +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + if (parsed_parts) + del_mtd_partitions(flash_mtd); + else + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + } + + module_init(init_ocelot_maps); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c linux/drivers/mtd/maps/octagon-5066.c +--- linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c 2003-08-13 19:19:18.000000000 +0200 ++++ linux/drivers/mtd/maps/octagon-5066.c 2004-11-19 10:25:11.940193560 +0100 +@@ -1,4 +1,4 @@ +-// $Id: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $ ++// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $ + /* ###################################################################### + + Octagon 5066 MTD Driver. +@@ -31,6 +31,7 @@ + #include + + #include ++#include + + #define WINDOW_START 0xe8000 + #define WINDOW_LENGTH 0x8000 +@@ -151,32 +152,34 @@ + + static struct map_info oct5066_map[2] = { + { +- name: "Octagon 5066 Socket", +- size: 512 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 1<<6 ++ .name = "Octagon 5066 Socket", ++ .phys = NO_XIP, ++ .size = 512 * 1024, ++ .buswidth = 1, ++ .read8 = oct5066_read8, ++ .read16 = oct5066_read16, ++ .read32 = oct5066_read32, ++ .copy_from = oct5066_copy_from, ++ .write8 = oct5066_write8, ++ .write16 = oct5066_write16, ++ .write32 = oct5066_write32, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 1<<6 + }, + { +- name: "Octagon 5066 Internal Flash", +- size: 2 * 1024 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 2<<6 ++ .name = "Octagon 5066 Internal Flash", ++ .phys = NO_XIP, ++ .size = 2 * 1024 * 1024, ++ .buswidth = 1, ++ .read8 = oct5066_read8, ++ .read16 = oct5066_read16, ++ .read32 = oct5066_read32, ++ .copy_from = oct5066_copy_from, ++ .write8 = oct5066_write8, ++ .write16 = oct5066_write16, ++ .write32 = oct5066_write32, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 2<<6 + } + }; + +@@ -244,6 +247,7 @@ + } + if (OctProbe() != 0) { + printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); ++ iounmap((void *)iomapadr); + ret = -EAGAIN; + goto out_unmap; + } +@@ -261,7 +265,7 @@ + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); + if (oct5066_mtd[i]) { +- oct5066_mtd[i]->module = THIS_MODULE; ++ oct5066_mtd[i]->owner = THIS_MODULE; + add_mtd_device(oct5066_mtd[i]); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c linux/drivers/mtd/maps/omap-toto-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/omap-toto-flash.c 2004-11-19 10:25:11.941193408 +0100 +@@ -0,0 +1,137 @@ ++/* ++ * NOR Flash memory access on TI Toto board ++ * ++ * jzhang@ti.com (C) 2003 Texas Instruments. ++ * ++ * (C) 2002 MontVista Software, Inc. ++ * ++ * $Id: omap-toto-flash.c,v 1.1 2003/10/21 13:55:21 dwmw2 Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#ifndef CONFIG_ARCH_OMAP ++#error This is for OMAP architecture only ++#endif ++ ++//these lines need be moved to a hardware header file ++#define OMAP_TOTO_FLASH_BASE 0xd8000000 ++#define OMAP_TOTO_FLASH_SIZE 0x80000 ++ ++static struct map_info omap_toto_map_flash = { ++ .name = "OMAP Toto flash", ++ .buswidth = 2, ++ .virt = OMAP_TOTO_FLASH_BASE, ++}; ++ ++ ++static struct mtd_partition toto_flash_partitions[] = { ++ { ++ .name = "BootLoader", ++ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/ ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ReservedSpace", ++ .size = 0x00030000, ++ .offset = MTDPART_OFS_APPEND, ++ //mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "EnvArea", /* bottom 64KiB for env vars */ ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static struct mtd_partition *parsed_parts; ++ ++static struct mtd_info *flash_mtd; ++ ++static int __init init_flash (void) ++{ ++ ++ struct mtd_partition *parts; ++ int nb_parts = 0; ++ int parsed_nr_parts = 0; ++ const char *part_type; ++ ++ /* ++ * Static partition definition selection ++ */ ++ part_type = "static"; ++ ++ parts = toto_flash_partitions; ++ nb_parts = ARRAY_SIZE(toto_flash_partitions); ++ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE; ++ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE); ++ ++ simple_map_init(&omap_toto_map_flash); ++ /* ++ * Now let's probe for the actual flash. Do it here since ++ * specific machine settings might have been set above. ++ */ ++ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n", ++ omap_toto_map_flash.buswidth*8); ++ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash); ++ if (!flash_mtd) ++ return -ENXIO; ++ ++ if (parsed_nr_parts > 0) { ++ parts = parsed_parts; ++ nb_parts = parsed_nr_parts; ++ } ++ ++ if (nb_parts == 0) { ++ printk(KERN_NOTICE "OMAP toto flash: no partition info available," ++ "registering whole flash at once\n"); ++ if (add_mtd_device(flash_mtd)){ ++ return -ENXIO; ++ } ++ } else { ++ printk(KERN_NOTICE "Using %s partition definition\n", ++ part_type); ++ return add_mtd_partitions(flash_mtd, parts, nb_parts); ++ } ++ return 0; ++} ++ ++int __init omap_toto_mtd_init(void) ++{ ++ int status; ++ ++ if (status = init_flash()) { ++ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n"); ++ } ++ return status; ++} ++ ++static void __exit omap_toto_mtd_cleanup(void) ++{ ++ if (flash_mtd) { ++ del_mtd_partitions(flash_mtd); ++ map_destroy(flash_mtd); ++ if (parsed_parts) ++ kfree(parsed_parts); ++ } ++} ++ ++module_init(omap_toto_mtd_init); ++module_exit(omap_toto_mtd_cleanup); ++ ++MODULE_AUTHOR("Jian Zhang"); ++MODULE_DESCRIPTION("OMAP Toto board map driver"); ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c 2003-05-19 08:27:22.000000000 +0200 ++++ linux/drivers/mtd/maps/pb1xxx-flash.c 2004-11-19 10:25:11.943193104 +0100 +@@ -3,12 +3,13 @@ + * + * (C) 2001 Pete Popov + * +- * $Id: pb1xxx-flash.c,v 1.4 2002/09/13 13:51:54 dwmw2 Exp $ ++ * $Id: pb1xxx-flash.c,v 1.10 2003/08/28 06:50:24 ppopov Exp $ + */ + + #include + #include + #include ++#include + #include + + #include +@@ -25,210 +26,110 @@ + #endif + + #ifdef CONFIG_MIPS_PB1000 ++ + #define WINDOW_ADDR 0x1F800000 + #define WINDOW_SIZE 0x800000 +-#endif +- +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- ret = __raw_readb(map->map_priv_1 + ofs); +- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- ret = __raw_readw(map->map_priv_1 + ofs); +- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- __u32 ret; +- ret = __raw_readl(map->map_priv_1 + ofs); +- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from); +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- +- +- +-static struct map_info pb1xxx_map = { +- name: "Pb1xxx flash", +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to, +-}; + +- +-#ifdef CONFIG_MIPS_PB1000 +- +-static unsigned long flash_size = 0x00800000; +-static unsigned char flash_buswidth = 4; + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "yamon env", +- size: 0x00020000, +- offset: 0, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "User FS", +- size: 0x003e0000, +- offset: 0x20000, +- },{ +- name: "boot code", +- size: 0x100000, +- offset: 0x400000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw/kernel", +- size: 0x300000, +- offset: 0x500000 +- } ++ .name = "yamon env", ++ .size = 0x00020000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE}, ++ { ++ .name = "User FS", ++ .size = 0x003e0000, ++ .offset = 0x20000,}, ++ { ++ .name = "boot code", ++ .size = 0x100000, ++ .offset = 0x400000, ++ .mask_flags = MTD_WRITEABLE}, ++ { ++ .name = "raw/kernel", ++ .size = 0x300000, ++ .offset = 0x500000} + }; + + #elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) + +-static unsigned char flash_buswidth = 4; + #if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) +-/* both 32MiB banks will be used. Combine the first 32MiB bank and the +- * first 28MiB of the second bank together into a single jffs/jffs2 ++/* both 32MB banks will be used. Combine the first 32MB bank and the ++ * first 28MB of the second bank together into a single jffs/jffs2 + * partition. + */ +-static unsigned long flash_size = 0x04000000; + #define WINDOW_ADDR 0x1C000000 + #define WINDOW_SIZE 0x4000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x3c00000, +- offset: 0x0000000 +- },{ +- name: "yamon", +- size: 0x0100000, +- offset: 0x3c00000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw kernel", +- size: 0x02c0000, +- offset: 0x3d00000 ++ .name = "User FS", ++ .size = 0x3c00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = 0x3c00000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "raw kernel", ++ .size = 0x02c0000, ++ .offset = 0x3d00000 + } + }; + #elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER) +-static unsigned long flash_size = 0x02000000; + #define WINDOW_ADDR 0x1E000000 + #define WINDOW_SIZE 0x2000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x1c00000, +- offset: 0x0000000 +- },{ +- name: "yamon", +- size: 0x0100000, +- offset: 0x1c00000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw kernel", +- size: 0x02c0000, +- offset: 0x1d00000 ++ .name = "User FS", ++ .size = 0x1c00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = 0x1c00000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "raw kernel", ++ .size = 0x02c0000, ++ .offset = 0x1d00000 + } + }; + #elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) +-static unsigned long flash_size = 0x02000000; + #define WINDOW_ADDR 0x1C000000 + #define WINDOW_SIZE 0x2000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x1e00000, +- offset: 0x0000000 +- },{ +- name: "raw kernel", +- size: 0x0200000, +- offset: 0x1e00000, ++ .name = "User FS", ++ .size = 0x1e00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "raw kernel", ++ .size = 0x0200000, ++ .offset = 0x1e00000, + } + }; + #else + #error MTD_PB1500 define combo error /* should never happen */ + #endif +-#elif defined(CONFIG_MTD_BOSPORUS) +-static unsigned char flash_buswidth = 2; +-static unsigned long flash_size = 0x02000000; +-#define WINDOW_ADDR 0x1F000000 +-#define WINDOW_SIZE 0x2000000 +-static struct mtd_partition pb1xxx_partitions[] = { +- { +- name: "User FS", +- size: 0x00400000, +- offset: 0x00000000, +- },{ +- name: "Yamon-2", +- size: 0x00100000, +- offset: 0x00400000, +- },{ +- name: "Root FS", +- size: 0x00700000, +- offset: 0x00500000, +- },{ +- name: "Yamon-1", +- size: 0x00100000, +- offset: 0x00C00000, +- },{ +- name: "Kernel", +- size: 0x00300000, +- offset: 0x00D00000, +- } +-}; + #else + #error Unsupported board + #endif + ++#define NAME "Pb1x00 Linux Flash" ++#define PADDR WINDOW_ADDR ++#define BUSWIDTH 4 ++#define SIZE WINDOW_SIZE ++#define PARTITIONS 4 ++ ++static struct map_info pb1xxx_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR, ++}; + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- +-static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; ++static struct mtd_info *pb1xxx_mtd; + + int __init pb1xxx_mtd_init(void) + { +@@ -236,40 +137,37 @@ + int nb_parts = 0; + char *part_type; + +- /* Default flash buswidth */ +- pb1xxx_map.buswidth = flash_buswidth; +- + /* + * Static partition definition selection + */ + part_type = "static"; + parts = pb1xxx_partitions; +- nb_parts = NB_OF(pb1xxx_partitions); +- pb1xxx_map.size = flash_size; ++ nb_parts = ARRAY_SIZE(pb1xxx_partitions); + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", +- pb1xxx_map.buswidth*8); +- pb1xxx_map.map_priv_1 = +- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); +- mymtd = do_map_probe("cfi_probe", &pb1xxx_map); +- if (!mymtd) return -ENXIO; +- mymtd->module = THIS_MODULE; ++ BUSWIDTH*8); ++ pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ ++ simple_map_init(&pb1xxx_mtd_map); ++ ++ pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map); ++ if (!pb1xxx_mtd) return -ENXIO; ++ pb1xxx_mtd->owner = THIS_MODULE; + +- add_mtd_partitions(mymtd, parts, nb_parts); ++ add_mtd_partitions(pb1xxx_mtd, parts, nb_parts); + return 0; + } + + static void __exit pb1xxx_mtd_cleanup(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); ++ if (pb1xxx_mtd) { ++ del_mtd_partitions(pb1xxx_mtd); ++ map_destroy(pb1xxx_mtd); ++ iounmap((void *) pb1xxx_mtd_map.virt); + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c +--- linux-mips-2.4.27/drivers/mtd/maps/pci.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/pci.c 2004-11-19 10:25:11.944192952 +0100 +@@ -7,7 +7,7 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +- * $Id: pci.c,v 1.2 2003/01/24 13:11:43 dwmw2 Exp $ ++ * $Id: pci.c,v 1.5 2003/05/20 20:59:31 dwmw2 Exp $ + * + * Generic PCI memory map driver. We support the following boards: + * - Intel IQ80310 ATU. +@@ -98,10 +98,10 @@ + } + + static struct mtd_pci_info intel_iq80310_info = { +- init: intel_iq80310_init, +- exit: intel_iq80310_exit, +- translate: intel_iq80310_translate, +- map_name: "cfi_probe", ++ .init = intel_iq80310_init, ++ .exit = intel_iq80310_exit, ++ .translate = intel_iq80310_translate, ++ .map_name = "cfi_probe", + }; + + /* +@@ -181,10 +181,10 @@ + } + + static struct mtd_pci_info intel_dc21285_info = { +- init: intel_dc21285_init, +- exit: intel_dc21285_exit, +- translate: intel_dc21285_translate, +- map_name: "jedec_probe", ++ .init = intel_dc21285_init, ++ .exit = intel_dc21285_exit, ++ .translate = intel_dc21285_translate, ++ .map_name = "jedec_probe", + }; + + /* +@@ -193,22 +193,20 @@ + + static struct pci_device_id mtd_pci_ids[] __devinitdata = { + { +- vendor: PCI_VENDOR_ID_INTEL, +- device: 0x530d, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID, +- class: PCI_CLASS_MEMORY_OTHER << 8, +- class_mask: 0xffff00, +- driver_data: (unsigned long)&intel_iq80310_info, ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x530d, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .class = PCI_CLASS_MEMORY_OTHER << 8, ++ .class_mask = 0xffff00, ++ .driver_data = (unsigned long)&intel_iq80310_info, + }, + { +- vendor: PCI_VENDOR_ID_DEC, +- device: PCI_DEVICE_ID_DEC_21285, +- subvendor: 0, /* DC21285 defaults to 0 on reset */ +- subdevice: 0, /* DC21285 defaults to 0 on reset */ +- class: 0, +- class_mask: 0, +- driver_data: (unsigned long)&intel_dc21285_info, ++ .vendor = PCI_VENDOR_ID_DEC, ++ .device = PCI_DEVICE_ID_DEC_21285, ++ .subvendor = 0, /* DC21285 defaults to 0 on reset */ ++ .subdevice = 0, /* DC21285 defaults to 0 on reset */ ++ .driver_data = (unsigned long)&intel_dc21285_info, + }, + { 0, } + }; +@@ -275,14 +273,15 @@ + } + + static struct map_info mtd_pci_map = { +- read8: mtd_pci_read8, +- read16: mtd_pci_read16, +- read32: mtd_pci_read32, +- copy_from: mtd_pci_copyfrom, +- write8: mtd_pci_write8, +- write16: mtd_pci_write16, +- write32: mtd_pci_write32, +- copy_to: mtd_pci_copyto, ++ .phys = NO_XIP, ++ .read8 = mtd_pci_read8, ++ .read16 = mtd_pci_read16, ++ .read32 = mtd_pci_read32, ++ .copy_from = mtd_pci_copyfrom, ++ .write8 = mtd_pci_write8, ++ .write16 = mtd_pci_write16, ++ .write32 = mtd_pci_write32, ++ .copy_to = mtd_pci_copyto, + }; + + static int __devinit +@@ -322,7 +321,7 @@ + if (!mtd) + goto release; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + add_mtd_device(mtd); + + pci_set_drvdata(dev, mtd); +@@ -359,10 +358,10 @@ + } + + static struct pci_driver mtd_pci_driver = { +- name: "MTD PCI", +- probe: mtd_pci_probe, +- remove: __devexit_p(mtd_pci_remove), +- id_table: mtd_pci_ids, ++ .name = "MTD PCI", ++ .probe = mtd_pci_probe, ++ .remove = __devexit_p(mtd_pci_remove), ++ .id_table = mtd_pci_ids, + }; + + static int __init mtd_pci_maps_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/pcmciamtd.c 2004-11-19 10:25:11.946192648 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $ ++ * $Id: pcmciamtd.c,v 1.48 2003/06/24 07:14:38 spse Exp $ + * + * pcmciamtd.c - MTD driver for PCMCIA flash memory cards + * +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -24,6 +25,7 @@ + #include + + #include ++#include + + #ifdef CONFIG_MTD_DEBUG + static int debug = CONFIG_MTD_DEBUG_VERBOSE; +@@ -47,7 +49,7 @@ + + + #define DRIVER_DESC "PCMCIA Flash memory card driver" +-#define DRIVER_VERSION "$Revision: 1.39 $" ++#define DRIVER_VERSION "$Revision: 1.48 $" + + /* Size of the PCMCIA address space: 26 bits = 64 MB */ + #define MAX_PCMCIA_ADDR 0x4000000 +@@ -96,7 +98,7 @@ + MODULE_PARM(mem_speed, "i"); + MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); + MODULE_PARM(force_size, "i"); +-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); ++MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)"); + MODULE_PARM(setvpp, "i"); + MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); + MODULE_PARM(vpp, "i"); +@@ -106,11 +108,13 @@ + + + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69) + static inline void cs_error(client_handle_t handle, int func, int ret) + { + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); + } ++#endif + + + /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +@@ -529,6 +533,7 @@ + + card_settings(dev, link, &new_name); + ++ dev->pcmcia_map.phys = NO_XIP; + dev->pcmcia_map.read8 = pcmcia_read8_remap; + dev->pcmcia_map.read16 = pcmcia_read16_remap; + dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; +@@ -539,7 +544,7 @@ + dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; + + /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum +- that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the ++ that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the + whole card - otherwise we try smaller windows until we succeed */ + + req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; +@@ -552,7 +557,7 @@ + + do { + int ret; +- DEBUG(2, "requesting window with size = %dKB memspeed = %d", ++ DEBUG(2, "requesting window with size = %dKiB memspeed = %d", + req.Size >> 10, req.AccessSpeed); + link->win = (window_handle_t)link->handle; + ret = CardServices(RequestWindow, &link->win, &req); +@@ -560,7 +565,7 @@ + if(ret) { + req.Size >>= 1; + } else { +- DEBUG(2, "Got window of size %dKB", req.Size >> 10); ++ DEBUG(2, "Got window of size %dKiB", req.Size >> 10); + dev->win_size = req.Size; + break; + } +@@ -573,7 +578,7 @@ + pcmciamtd_release((u_long)link); + return; + } +- DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); ++ DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); + + /* Get write protect status */ + CS_CHECK(GetStatus, link->handle, &status); +@@ -642,21 +647,21 @@ + } + + dev->mtd_info = mtd; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + + if(new_name) { + int size = 0; + char unit = ' '; + /* Since we are using a default name, make it better by adding in the + size */ +- if(mtd->size < 1048576) { /* <1MB in size, show size in K */ ++ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ + size = mtd->size >> 10; + unit = 'K'; + } else { + size = mtd->size >> 20; + unit = 'M'; + } +- snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card"); ++ snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card"); + } + + /* If the memory found is fits completely into the mapped PCMCIA window, +@@ -828,16 +833,20 @@ + } + + ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++static struct pcmcia_driver pcmciamtd_driver = { ++ .drv = { ++ .name = "pcmciamtd" ++ }, ++ .attach = pcmciamtd_attach, ++ .detach = pcmciamtd_detach, ++ .owner = THIS_MODULE ++}; ++#endif ++ ++ + static int __init init_pcmciamtd(void) + { +- servinfo_t serv; +- +- info(DRIVER_DESC " " DRIVER_VERSION); +- CardServices(GetCardServicesInfo, &serv); +- if (serv.Revision != CS_RELEASE_CODE) { +- err("Card Services release does not match!"); +- return -1; +- } + + if(buswidth && buswidth != 1 && buswidth != 2) { + info("bad buswidth (%d), using default", buswidth); +@@ -851,15 +860,24 @@ + info("bad mem_type (%d), using default", mem_type); + mem_type = 0; + } ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++ return pcmcia_register_driver(&pcmciamtd_driver); ++#else + register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); + return 0; ++#endif + } + + + static void __exit exit_pcmciamtd(void) + { + DEBUG(1, DRIVER_DESC " unloading"); ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++ pcmcia_unregister_driver(&pcmciamtd_driver); ++#else + unregister_pccard_driver(&dev_info); ++#endif + + while(dev_list) { + dev_link_t *link = dev_list; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/physmap.c linux/drivers/mtd/maps/physmap.c +--- linux-mips-2.4.27/drivers/mtd/maps/physmap.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/physmap.c 2004-11-19 10:25:11.947192496 +0100 +@@ -1,179 +1,114 @@ + /* +- * $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $ ++ * $Id: physmap.c,v 1.30 2003/11/11 19:05:18 jsun Exp $ + * + * Normal mappings of chips in physical memory ++ * ++ * Copyright (C) 2003 MontaVista Software Inc. ++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net ++ * ++ * 031022 - [jsun] add run-time configure and partition setup + */ + + #include + #include + #include ++#include ++#include + #include + #include + #include + #include +- +-#ifdef CONFIG_MTD_PARTITIONS + #include +-#endif +- +-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START +-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN +-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH + + static struct mtd_info *mymtd; + +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++struct map_info physmap_map = {.name = "phys_mapped_flash"}; + +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition *mtd_parts; ++static int mtd_parts_nb; + +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++static int num_physmap_partitions; ++static struct mtd_partition *physmap_partitions; + +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++void physmap_set_partitions(struct mtd_partition *parts, int num_parts) + { +- memcpy_toio(map->map_priv_1 + to, from, len); ++ physmap_partitions=parts; ++ num_physmap_partitions=num_parts; + } +- +-struct map_info physmap_map = { +- name: "Physically mapped flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to +-}; +- +-#ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-static struct mtd_partition *mtd_parts = 0; +-static int mtd_parts_nb = 0; +-#else +-static struct mtd_partition physmap_partitions[] = { +-/* Put your own partition definitions here */ +-#if 0 +- { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- } +-#endif +-}; +- +-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) +- +-#endif +-#endif ++#endif /* CONFIG_MTD_PARTITIONS */ + + int __init init_physmap(void) + { + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + const char **type; + +- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); +- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); ++ physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size); + +- if (!physmap_map.map_priv_1) { ++ if (!physmap_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + ++ simple_map_init(&physmap_map); ++ + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &physmap_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +- add_mtd_device(mymtd); + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, +- "phys"); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, ++ &mtd_parts, 0); ++ + if (mtd_parts_nb > 0) + { +- printk(KERN_NOTICE +- "Using command line partition definition\n"); + add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); ++ return 0; + } +-#else +- if (NUM_PARTITIONS != 0) ++ ++ if (num_physmap_partitions != 0) + { + printk(KERN_NOTICE + "Using physmap partition definition\n"); +- add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); ++ add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); ++ return 0; + } + + #endif +-#endif ++ add_mtd_device(mymtd); ++ + return 0; + } + +- iounmap((void *)physmap_map.map_priv_1); ++ iounmap((void *)physmap_map.virt); + return -ENXIO; + } + + static void __exit cleanup_physmap(void) + { +- if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (mtd_parts_nb) { ++ del_mtd_partitions(mymtd); ++ kfree(mtd_parts); ++ } else if (num_physmap_partitions) { ++ del_mtd_partitions(mymtd); ++ } else { + del_mtd_device(mymtd); +- map_destroy(mymtd); +- } +- if (physmap_map.map_priv_1) { +- iounmap((void *)physmap_map.map_priv_1); +- physmap_map.map_priv_1 = 0; + } ++#else ++ del_mtd_device(mymtd); ++#endif ++ map_destroy(mymtd); ++ ++ iounmap((void *)physmap_map.virt); ++ physmap_map.virt = 0; + } + + module_init(init_physmap); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c linux/drivers/mtd/maps/pnc2000.c +--- linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/pnc2000.c 2004-11-19 10:25:11.949192192 +0100 +@@ -5,12 +5,13 @@ + * + * This code is GPL + * +- * $Id: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $ + */ + + #include + #include + #include ++#include + + #include + #include +@@ -24,58 +25,13 @@ + * MAP DRIVER STUFF + */ + +-__u8 pnc_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(WINDOW_ADDR + ofs); +-} +- +-__u16 pnc_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(WINDOW_ADDR + ofs); +-} +- +-__u32 pnc_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(WINDOW_ADDR + ofs); +-} +- +-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(WINDOW_ADDR + from), len); +-} +- +-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(WINDOW_ADDR + to), from, len); +-} + + struct map_info pnc_map = { +- name: "PNC-2000", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: pnc_read8, +- read16: pnc_read16, +- read32: pnc_read32, +- copy_from: pnc_copy_from, +- write8: pnc_write8, +- write16: pnc_write16, +- write32: pnc_write32, +- copy_to: pnc_copy_to ++ .name = "PNC-2000", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = 0xFFFFFFFF, ++ .virt = WINDOW_ADDR, + }; + + +@@ -84,19 +40,19 @@ + */ + static struct mtd_partition pnc_partitions[3] = { + { +- name: "PNC-2000 boot firmware", +- size: 0x20000, +- offset: 0 ++ .name = "PNC-2000 boot firmware", ++ .size = 0x20000, ++ .offset = 0 + }, + { +- name: "PNC-2000 kernel", +- size: 0x1a0000, +- offset: 0x20000 ++ .name = "PNC-2000 kernel", ++ .size = 0x1a0000, ++ .offset = 0x20000 + }, + { +- name: "PNC-2000 filesystem", +- size: 0x240000, +- offset: 0x1c0000 ++ .name = "PNC-2000 filesystem", ++ .size = 0x240000, ++ .offset = 0x1c0000 + } + }; + +@@ -110,9 +66,11 @@ + { + printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + ++ simple_map_init(&pnc_map); ++ + mymtd = do_map_probe("cfi_probe", &pnc_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + return add_mtd_partitions(mymtd, pnc_partitions, 3); + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/redwood.c linux/drivers/mtd/maps/redwood.c +--- linux-mips-2.4.27/drivers/mtd/maps/redwood.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/redwood.c 2004-11-19 10:25:11.962190216 +0100 +@@ -1,38 +1,23 @@ + /* +- * $Id: ++ * $Id: redwood.c,v 1.7 2003/11/21 23:09:52 trini Exp $ + * +- * redwood.c - mapper for IBM Redwood-4/5 board. ++ * drivers/mtd/maps/redwood.c + * +- * Copyright 2001 MontaVista Softare Inc. ++ * FLASH map for the IBM Redwood 4/5/6 boards. + * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the +- * Free Software Foundation; either version 2 of the License, or (at your +- * option) any later version. +- * +- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * History: 12/17/2001 - Armin +- * migrated to use do_map_probe ++ * Author: MontaVista Software, Inc. + * ++ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. + */ + ++#include + #include + #include + #include ++#include + + #include + #include +@@ -40,96 +25,102 @@ + + #include + ++#if !defined (CONFIG_REDWOOD_6) ++ + #define WINDOW_ADDR 0xffc00000 + #define WINDOW_SIZE 0x00400000 + +-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(map->map_priv_1 + ofs); +-} +- +-void redwood_flash_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x10000 ++#define RW_PART1_OF RW_PART0_SZ ++#define RW_PART1_SZ 0x200000 - 0x10000 ++#define RW_PART2_OF 0x200000 ++#define RW_PART2_SZ 0x10000 ++#define RW_PART3_OF 0x210000 ++#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000) ++#define RW_PART4_OF 0x3e0000 ++#define RW_PART4_SZ 0x20000 + +-struct map_info redwood_flash_map = { +- name: "IBM Redwood", +- size: WINDOW_SIZE, +- buswidth: 2, +- read8: redwood_flash_read8, +- read16: redwood_flash_read16, +- read32: redwood_flash_read32, +- copy_from: redwood_flash_copy_from, +- write8: redwood_flash_write8, +- write16: redwood_flash_write16, +- write32: redwood_flash_write32, +- copy_to: redwood_flash_copy_to ++static struct mtd_partition redwood_flash_partitions[] = { ++ { ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood kernel", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS non-volatile storage", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood filesystem", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART4_OF, ++ .size = RW_PART4_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ } + }; + ++#else /* CONFIG_REDWOOD_6 */ ++/* FIXME: the window is bigger - armin */ ++#define WINDOW_ADDR 0xff800000 ++#define WINDOW_SIZE 0x00800000 ++ ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x400000 /* 4 MiB data */ ++#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ ++#define RW_PART1_SZ 0x10000 /* 64K VPD */ ++#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ ++#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000) ++#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ ++#define RW_PART3_SZ 0x20000 + + static struct mtd_partition redwood_flash_partitions[] = { + { +- name: "Redwood OpenBIOS Vital Product Data", +- offset: 0, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood filesystem", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ + }, + { +- name: "Redwood kernel", +- offset: 0x10000, +- size: 0x200000 - 0x10000 ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { +- name: "Redwood OpenBIOS non-volatile storage", +- offset: 0x200000, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood kernel", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ + }, + { +- name: "Redwood filesystem", +- offset: 0x210000, +- size: 0x200000 - (0x10000 + 0x20000) +- }, +- { +- name: "Redwood OpenBIOS", +- offset: 0x3e0000, +- size: 0x20000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + } + }; ++ ++#endif /* CONFIG_REDWOOD_6 */ ++ ++struct map_info redwood_flash_map = { ++ .name = "IBM Redwood", ++ .size = WINDOW_SIZE, ++ .buswidth = 2, ++ .phys = WINDOW_ADDR, ++}; ++ ++ + #define NUM_REDWOOD_FLASH_PARTITIONS \ + (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0])) + +@@ -140,18 +131,19 @@ + printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", + WINDOW_SIZE, WINDOW_ADDR); + +- redwood_flash_map.map_priv_1 = ++ redwood_flash_map.virt = + (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!redwood_flash_map.map_priv_1) { ++ if (!redwood_flash_map.virt) { + printk("init_redwood_flash: failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&redwood_flash_map); + + redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map); + + if (redwood_mtd) { +- redwood_mtd->module = THIS_MODULE; ++ redwood_mtd->owner = THIS_MODULE; + return add_mtd_partitions(redwood_mtd, + redwood_flash_partitions, + NUM_REDWOOD_FLASH_PARTITIONS); +@@ -164,10 +156,15 @@ + { + if (redwood_mtd) { + del_mtd_partitions(redwood_mtd); +- iounmap((void *)redwood_flash_map.map_priv_1); ++ /* moved iounmap after map_destroy - armin */ + map_destroy(redwood_mtd); ++ iounmap((void *)redwood_flash_map.virt); + } + } + + module_init(init_redwood_flash); + module_exit(cleanup_redwood_flash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("MontaVista Software "); ++MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c linux/drivers/mtd/maps/rpxlite.c +--- linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/rpxlite.c 2004-11-19 10:25:11.963190064 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Handle mapping of the flash on the RPX Lite and CLLF boards + */ +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -17,80 +18,31 @@ + + static struct mtd_info *mymtd; + +-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +-struct map_info rpxlite_map = { +- name: "RPX", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: rpxlite_read8, +- read16: rpxlite_read16, +- read32: rpxlite_read32, +- copy_from: rpxlite_copy_from, +- write8: rpxlite_write8, +- write16: rpxlite_write16, +- write32: rpxlite_write32, +- copy_to: rpxlite_copy_to ++static struct map_info rpxlite_map = { ++ .name = "RPX", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_rpxlite(void) + { + printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!rpxlite_map.map_priv_1) { ++ if (!rpxlite_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&rpxlite_map); + mymtd = do_map_probe("cfi_probe", &rpxlite_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)rpxlite_map.map_priv_1); ++ iounmap((void *)rpxlite_map.virt); + return -ENXIO; + } + +@@ -100,9 +52,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (rpxlite_map.map_priv_1) { +- iounmap((void *)rpxlite_map.map_priv_1); +- rpxlite_map.map_priv_1 = 0; ++ if (rpxlite_map.virt) { ++ iounmap((void *)rpxlite_map.virt); ++ rpxlite_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/sa1100-flash.c 2004-11-19 10:25:11.966189608 +0100 +@@ -3,7 +3,7 @@ + * + * (C) 2000 Nicolas Pitre + * +- * $Id: sa1100-flash.c,v 1.29 2002/09/06 14:36:19 abz Exp $ ++ * $Id: sa1100-flash.c,v 1.38 2004/01/22 19:11:08 rmk Exp $ + */ + + #include +@@ -11,278 +11,212 @@ + #include + #include + #include ++#include ++#include ++#include + + #include + #include + #include ++#include + + #include ++#include + #include ++#include + ++#include + + #ifndef CONFIG_ARCH_SA1100 + #error This is for SA1100 architecture only + #endif + ++/* ++ * This isnt complete yet, so... ++ */ ++#define CONFIG_MTD_SA1100_STATICMAP 1 + +-#define WINDOW_ADDR 0xe8000000 +- +-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info sa1100_map = { +- name: "SA1100 flash", +- read8: sa1100_read8, +- read16: sa1100_read16, +- read32: sa1100_read32, +- copy_from: sa1100_copy_from, +- write8: sa1100_write8, +- write16: sa1100_write16, +- write32: sa1100_write32, +- copy_to: sa1100_copy_to, +- +- map_priv_1: WINDOW_ADDR, +- map_priv_2: -1, +-}; +- +- ++#ifdef CONFIG_MTD_SA1100_STATICMAP + /* + * Here are partition information for all known SA1100-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * +- * The *_max_flash_size is the maximum possible mapped flash size which +- * is not necessarily the actual flash size. It must be no more than +- * the value specified in the "struct map_desc *_io_desc" mapping +- * definition for the corresponding machine. ++ * Please note: ++ * 1. We no longer support static flash mappings via the machine io_desc ++ * structure. ++ * 2. The flash size given should be the largest flash size that can ++ * be accommodated. ++ * ++ * The MTD layer will detect flash chip aliasing and reduce the size of ++ * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + + #ifdef CONFIG_SA1100_ADSBITSY +-#define ADSBITSY_FLASH_SIZE 0x02000000 + static struct mtd_partition adsbitsy_partitions[] = { + { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootROM", ++ .size = 0x80000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_ASSABET + /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ +-#define ASSABET4_FLASH_SIZE 0x00400000 + static struct mtd_partition assabet4_partitions[] = { + { +- name: "bootloader", +- size: 0x00020000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00020000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootloader", ++ .size = 0x00020000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00020000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + + /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ +-#define ASSABET5_FLASH_SIZE 0x02000000 + static struct mtd_partition assabet5_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + +-#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE + #define assabet_partitions assabet5_partitions + #endif + + #ifdef CONFIG_SA1100_BADGE4 +- + /* +- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) ++ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) ++ * ++ * ++ * ++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) ++ * Eight 4 KiW Parameter Bottom Blocks (64 KiB) ++ * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + */ +-#define BADGE4_FLASH_SIZE 0x00400000 + static struct mtd_partition badge4_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x0000A000 +- }, { +- name: "params", +- offset: MTDPART_OFS_APPEND, +- size: 0x00006000 +- }, { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0x00100000 +- }, { +- name: "root", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x0000A000 ++ }, { ++ .name = "params", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0x00006000 ++ }, { ++ .name = "root", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; +- + #endif + + + #ifdef CONFIG_SA1100_CERF + #ifdef CONFIG_SA1100_CERF_FLASH_32MB +-#define CERF_FLASH_SIZE 0x02000000 +-static struct mtd_partition cerf_partitions[] = { +- { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- }, { +- name: "params", +- size: 0x00040000, +- offset: 0x00040000, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +- name: "rootdisk", +- size: 0x01E80000, +- offset: 0x00180000, +- } +-}; ++# define CERF_FLASH_SIZE 0x02000000 + #elif defined CONFIG_SA1100_CERF_FLASH_16MB +-#define CERF_FLASH_SIZE 0x01000000 ++# define CERF_FLASH_SIZE 0x01000000 ++#elif defined CONFIG_SA1100_CERF_FLASH_8MB ++# define CERF_FLASH_SIZE 0x00800000 ++#else ++# error "Undefined flash size for CERF in sa1100-flash.c" ++#endif ++ + static struct mtd_partition cerf_partitions[] = { + { +- name: "firmware", +- size: 0x00020000, +- offset: 0, +- }, { +- name: "params", +- size: 0x00020000, +- offset: 0x00020000, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00040000, +- }, { +- name: "rootdisk", +- size: 0x00EC0000, +- offset: 0x00140000, ++ .name = "Bootloader", ++ .size = 0x00020000, ++ .offset = 0x00000000, ++ }, { ++ .name = "Params", ++ .size = 0x00040000, ++ .offset = 0x00020000, ++ }, { ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00060000, ++ }, { ++ .name = "Filesystem", ++ .size = CERF_FLASH_SIZE-0x00160000, ++ .offset = 0x00160000, + } + }; +-#elif defined CONFIG_SA1100_CERF_FLASH_8MB +-# error "Unwritten type definition" +-#else +-# error "Undefined memory orientation for CERF in sa1100-flash.c" +-#endif + #endif + + #ifdef CONFIG_SA1100_CONSUS +-#define CONSUS_FLASH_SIZE 0x02000000 + static struct mtd_partition consus_partitions[] = { + { +- name: "Consus boot firmware", +- offset: 0, +- size: 0x00040000, +- mask_flags: MTD_WRITABLE, /* force read-only */ +- }, { +- name: "Consus kernel", +- offset: 0x00040000, +- size: 0x00100000, +- mask_flags: 0, ++ .name = "Consus boot firmware", ++ .offset = 0, ++ .size = 0x00040000, ++ .mask_flags = MTD_WRITABLE, /* force read-only */ ++ }, { ++ .name = "Consus kernel", ++ .offset = 0x00040000, ++ .size = 0x00100000, ++ .mask_flags = 0, + }, { +- name: "Consus disk", +- offset: 0x00140000, ++ .name = "Consus disk", ++ .offset = 0x00140000, + /* The rest (up to 16M) for jffs. We could put 0 and + make it find the size automatically, but right now + i have 32 megs. jffs will use all 32 megs if given + the chance, and this leads to horrible problems + when you try to re-flash the image because blob + won't erase the whole partition. */ +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + }, { + /* this disk is a secondary disk, which can be used as + needed, for simplicity, make it the size of the other + consus partition, although realistically it could be + the remainder of the disk (depending on the file + system used) */ +- name: "Consus disk2", +- offset: 0x01000000, +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .name = "Consus disk2", ++ .offset = 0x01000000, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + } + }; + #endif +@@ -292,96 +226,95 @@ + #define FLEXANET_FLASH_SIZE 0x02000000 + static struct mtd_partition flexanet_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "kernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "altkernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "root", +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free1", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free2", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free3", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "kernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "altkernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "root", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free1", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free2", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free3", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + } + }; + #endif + + #ifdef CONFIG_SA1100_FREEBIRD +-#define FREEBIRD_FLASH_SIZE 0x02000000 + static struct mtd_partition freebird_partitions[] = { +-#if CONFIG_SA1100_FREEBIRD_NEW ++#ifdef CONFIG_SA1100_FREEBIRD_NEW + { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00040000, +- }, { +- name: "params", +- size: 0x00040000, +- offset: 0x000C0000, +- }, { +- name: "initrd", +- size: 0x00100000, +- offset: 0x00100000, +- }, { +- name: "root cramfs", +- size: 0x00300000, +- offset: 0x00200000, +- }, { +- name: "usr cramfs", +- size: 0x00C00000, +- offset: 0x00500000, +- }, { +- name: "local", +- size: MTDPART_SIZ_FULL, +- offset: 0x01100000, ++ .name = "firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, ++ }, { ++ .name = "params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, ++ }, { ++ .name = "initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, ++ }, { ++ .name = "root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, ++ }, { ++ .name = "usr cramfs", ++ .size = 0x00C00000, ++ .offset = 0x00500000, ++ }, { ++ .name = "local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x01100000, + } + #else + { +- size: 0x00040000, +- offset: 0, ++ .size = 0x00040000, ++ .offset = 0, + }, { +- size: 0x000c0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x000c0000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + #endif + }; +@@ -389,178 +322,215 @@ + + #ifdef CONFIG_SA1100_FRODO + /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +-#define FRODO_FLASH_SIZE 0x02000000 + static struct mtd_partition frodo_partitions[] = + { + { +- name: "Boot Loader", +- size: 0x00040000, +- offset: 0x00000000 +- }, { +- name: "Parameter Block", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Linux Kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Ramdisk", +- size: 0x00680000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Flash File System", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "ramdisk", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "file system", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSCLIENT +-#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000 + static struct mtd_partition graphicsclient_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSMASTER +-#define GRAPHICSMASTER_FLASH_SIZE 0x01000000 + static struct mtd_partition graphicsmaster_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + +-#ifdef CONFIG_SA1100_H3600 +-#define H3600_FLASH_SIZE 0x02000000 +-static struct mtd_partition h3600_partitions[] = { ++#ifdef CONFIG_SA1100_H3XXX ++static struct mtd_partition h3xxx_partitions[] = { + { +- name: "H3600 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "H3600 kernel", +- size: 0x00080000, +- offset: 0x00040000, ++ .name = "H3XXX boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "H3600 params", +- size: 0x00040000, +- offset: 0x000C0000, ++#ifdef CONFIG_MTD_2PARTS_IPAQ ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00040000, ++#else ++ .name = "H3XXX kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, ++ }, { ++ .name = "H3XXX params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "H3600 root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00100000, ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00100000, + #else +- name: "H3600 initrd", +- size: 0x00100000, +- offset: 0x00100000, ++ .name = "H3XXX initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, + }, { +- name: "H3600 root cramfs", +- size: 0x00300000, +- offset: 0x00200000, ++ .name = "H3XXX root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, + }, { +- name: "H3600 usr cramfs", +- size: 0x00800000, +- offset: 0x00500000, ++ .name = "H3XXX usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00500000, + }, { +- name: "H3600 usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d00000, ++ .name = "H3XXX usr local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00d00000, ++#endif + #endif + } + }; + +-static void h3600_set_vpp(struct map_info *map, int vpp) ++static void h3xxx_set_vpp(struct map_info *map, int vpp) + { + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); + } ++#else ++#define h3xxx_set_vpp NULL ++#endif ++ ++#ifdef CONFIG_SA1100_HACKKIT ++static struct mtd_partition hackkit_partitions[] = { ++ { ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "initrd", ++ .size = 0x00180000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "rootfs", ++ .size = 0x700000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "data", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; + #endif + + #ifdef CONFIG_SA1100_HUW_WEBPANEL +-#define HUW_WEBPANEL_FLASH_SIZE 0x01000000 + static struct mtd_partition huw_webpanel_partitions[] = { + { +- name: "Loader", +- size: 0x00040000, +- offset: 0, +- }, { +- name: "Sector 1", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, ++ .name = "Loader", ++ .size = 0x00040000, ++ .offset = 0, ++ }, { ++ .name = "Sector 1", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_JORNADA720 +-#define JORNADA720_FLASH_SIZE 0x02000000 + static struct mtd_partition jornada720_partitions[] = { + { +- name: "JORNADA720 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "JORNADA720 boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "JORNADA720 kernel", +- size: 0x000c0000, +- offset: 0x00040000, ++ .name = "JORNADA720 kernel", ++ .size = 0x000c0000, ++ .offset = 0x00040000, + }, { +- name: "JORNADA720 params", +- size: 0x00040000, +- offset: 0x00100000, ++ .name = "JORNADA720 params", ++ .size = 0x00040000, ++ .offset = 0x00100000, + }, { +- name: "JORNADA720 initrd", +- size: 0x00100000, +- offset: 0x00140000, ++ .name = "JORNADA720 initrd", ++ .size = 0x00100000, ++ .offset = 0x00140000, + }, { +- name: "JORNADA720 root cramfs", +- size: 0x00300000, +- offset: 0x00240000, ++ .name = "JORNADA720 root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00240000, + }, { +- name: "JORNADA720 usr cramfs", +- size: 0x00800000, +- offset: 0x00540000, ++ .name = "JORNADA720 usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00540000, + }, { +- name: "JORNADA720 usr local", +- size: 0 /* will expand to the end of the flash */ +- offset: 0x00d00000, ++ .name = "JORNADA720 usr local", ++ .size = 0, /* will expand to the end of the flash */ ++ .offset = 0x00d00000, + } + }; + +-static void jornada720_set_vpp(int vpp) ++static void jornada720_set_vpp(struct map_info *map, int vpp) + { + if (vpp) + PPSR |= 0x80; +@@ -568,454 +538,811 @@ + PPSR &= ~0x80; + PPDR |= 0x80; + } +- ++#else ++#define jornada720_set_vpp NULL + #endif + + #ifdef CONFIG_SA1100_PANGOLIN +-#define PANGOLIN_FLASH_SIZE 0x04000000 + static struct mtd_partition pangolin_partitions[] = { + { +- name: "boot firmware", +- size: 0x00080000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +- name: "initrd", +- size: 0x00280000, +- offset: 0x00180000, +- }, { +- name: "initrd-test", +- size: 0x03C00000, +- offset: 0x00400000, ++ .name = "boot firmware", ++ .size = 0x00080000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, ++ }, { ++ .name = "initrd", ++ .size = 0x00280000, ++ .offset = 0x00180000, ++ }, { ++ .name = "initrd-test", ++ .size = 0x03C00000, ++ .offset = 0x00400000, + } + }; + #endif + + #ifdef CONFIG_SA1100_PT_SYSTEM3 + /* erase size is 0x40000 == 256k partitions have to have this boundary */ +-#define SYSTEM3_FLASH_SIZE 0x01000000 + static struct mtd_partition system3_partitions[] = { + { +- name: "BLOB", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "config", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND, +- }, { +- name: "root", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SHANNON +-#define SHANNON_FLASH_SIZE 0x00400000 + static struct mtd_partition shannon_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x20000 ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x20000 + }, + { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0xe0000 ++ .name = "kernel", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0xe0000 + }, + { +- name: "initrd", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "initrd", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; + + #endif + + #ifdef CONFIG_SA1100_SHERMAN +-#define SHERMAN_FLASH_SIZE 0x02000000 + static struct mtd_partition sherman_partitions[] = { + { +- size: 0x50000, +- offset: 0, ++ .size = 0x50000, ++ .offset = 0, + }, { +- size: 0x70000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x70000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x600000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x600000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0xA0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0xA0000, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SIMPAD +-#define SIMPAD_FLASH_SIZE 0x02000000 + static struct mtd_partition simpad_partitions[] = { + { +- name: "SIMpad boot firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "SIMpad kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +-#ifdef CONFIG_JFFS2_FS +- name: "SIMpad root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00180000, ++ .name = "SIMpad boot firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "SIMpad kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++#ifdef CONFIG_ROOT_CRAMFS ++ .name = "SIMpad root cramfs", ++ .size =0x00D80000, ++ .offset = MTDPART_OFS_APPEND ++ ++ }, { ++ .name = "SIMpad local jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + #else +- name: "SIMpad initrd", +- size: 0x00300000, +- offset: 0x00180000, +- }, { +- name: "SIMpad root cramfs", +- size: 0x00300000, +- offset: 0x00480000, +- }, { +- name: "SIMpad usr cramfs", +- size: 0x005c0000, +- offset: 0x00780000, +- }, { +- name: "SIMpad usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d40000, ++ .name = "SIMpad root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + #endif + } + }; + #endif /* CONFIG_SA1100_SIMPAD */ + + #ifdef CONFIG_SA1100_STORK +-#define STORK_FLASH_SIZE 0x02000000 + static struct mtd_partition stork_partitions[] = { + { +- name: "STORK boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "STORK params", +- size: 0x00040000, +- offset: 0x00040000, +- }, { +- name: "STORK kernel", +- size: 0x00100000, +- offset: 0x00080000, ++ .name = "STORK boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "STORK params", ++ .size = 0x00040000, ++ .offset = 0x00040000, ++ }, { ++ .name = "STORK kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "STORK root jffs2", +- offset: 0x00180000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK root jffs2", ++ .offset = 0x00180000, ++ .size = MTDPART_SIZ_FULL, + #else +- name: "STORK initrd", +- size: 0x00100000, +- offset: 0x00180000, +- }, { +- name: "STORK root cramfs", +- size: 0x00300000, +- offset: 0x00280000, +- }, { +- name: "STORK usr cramfs", +- size: 0x00800000, +- offset: 0x00580000, +- }, { +- name: "STORK usr local", +- offset: 0x00d80000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK initrd", ++ .size = 0x00100000, ++ .offset = 0x00180000, ++ }, { ++ .name = "STORK root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00280000, ++ }, { ++ .name = "STORK usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00580000, ++ }, { ++ .name = "STORK usr local", ++ .offset = 0x00d80000, ++ .size = MTDPART_SIZ_FULL, ++#endif ++ } ++}; + #endif ++ ++#ifdef CONFIG_SA1100_TRIZEPS ++static struct mtd_partition trizeps_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00100000, ++ .offset = 0, ++ }, { ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_YOPY +-#define YOPY_FLASH_SIZE 0x08000000 + static struct mtd_partition yopy_partitions[] = { + { +- name: "boot firmware", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "boot firmware", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00080000, ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00080000, + }, { +- name: "initrd", +- size: 0x00300000, +- offset: 0x00100000, ++ .name = "initrd", ++ .size = 0x00300000, ++ .offset = 0x00100000, + }, { +- name: "root", +- size: 0x01000000, +- offset: 0x00400000, ++ .name = "root", ++ .size = 0x01000000, ++ .offset = 0x00400000, + } + }; + #endif + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; +- +-int __init sa1100_mtd_init(void) ++static int __init sa1100_static_partitions(struct mtd_partition **parts) + { +- struct mtd_partition *parts; +- int nb_parts = 0, ret; +- int parsed_nr_parts = 0; +- const char *part_type; +- unsigned long base = -1UL; +- +- /* Default flash buswidth */ +- sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; +- +- /* +- * Static partition definition selection +- */ +- part_type = "static"; ++ int nb_parts = 0; + + #ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { +- parts = adsbitsy_partitions; ++ *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); +- sa1100_map.size = ADSBITSY_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } + #endif + #ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { +- parts = assabet_partitions; ++ *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); +- sa1100_map.size = ASSABET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { +- parts = badge4_partitions; ++ *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); +- sa1100_map.size = BADGE4_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { +- parts = cerf_partitions; ++ *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); +- sa1100_map.size = CERF_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { +- parts = consus_partitions; ++ *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); +- sa1100_map.size = CONSUS_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { +- parts = flexanet_partitions; ++ *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); +- sa1100_map.size = FLEXANET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) { +- parts = freebird_partitions; ++ *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); +- sa1100_map.size = FREEBIRD_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { +- parts = frodo_partitions; ++ *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); +- sa1100_map.size = FRODO_FLASH_SIZE; +- base = 0x00000000; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSCLIENT + if (machine_is_graphicsclient()) { +- parts = graphicsclient_partitions; ++ *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); +- sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSMASTER + if (machine_is_graphicsmaster()) { +- parts = graphicsmaster_partitions; ++ *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); +- sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif +-#ifdef CONFIG_SA1100_H3600 +- if (machine_is_h3600()) { +- parts = h3600_partitions; +- nb_parts = ARRAY_SIZE(h3600_partitions); +- sa1100_map.size = H3600_FLASH_SIZE; +- sa1100_map.set_vpp = h3600_set_vpp; ++#ifdef CONFIG_SA1100_H3XXX ++ if (machine_is_h3xxx()) { ++ *parts = h3xxx_partitions; ++ nb_parts = ARRAY_SIZE(h3xxx_partitions); ++ } ++#endif ++#ifdef CONFIG_SA1100_HACKKIT ++ if (machine_is_hackkit()) { ++ *parts = hackkit_partitions; ++ nb_parts = ARRAY_SIZE(hackkit_partitions); + } + #endif + #ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { +- parts = huw_webpanel_partitions; ++ *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); +- sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { +- parts = jornada720_partitions; ++ *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); +- sa1100_map.size = JORNADA720_FLASH_SIZE; +- sa1100_map.set_vpp = jornada720_set_vpp; + } + #endif + #ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { +- parts = pangolin_partitions; ++ *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); +- sa1100_map.size = PANGOLIN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { +- parts = system3_partitions; ++ *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); +- sa1100_map.size = SYSTEM3_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { +- parts = shannon_partitions; ++ *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); +- sa1100_map.size = SHANNON_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHERMAN + if (machine_is_sherman()) { +- parts = sherman_partitions; ++ *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); +- sa1100_map.size = SHERMAN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { +- parts = simpad_partitions; ++ *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); +- sa1100_map.size = SIMPAD_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { +- parts = stork_partitions; ++ *parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); +- sa1100_map.size = STORK_FLASH_SIZE; ++ } ++#endif ++#ifdef CONFIG_SA1100_TRIZEPS ++ if (machine_is_trizeps()) { ++ *parts = trizeps_partitions; ++ nb_parts = ARRAY_SIZE(trizeps_partitions); + } + #endif + #ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { +- parts = yopy_partitions; ++ *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); +- sa1100_map.size = YOPY_FLASH_SIZE; + } + #endif + ++ return nb_parts; ++} ++#endif ++ ++struct sa_info { ++ unsigned long base; ++ unsigned long size; ++ int width; ++ void *vbase; ++ void (*set_vpp)(struct map_info *, int); ++ struct map_info *map; ++ struct mtd_info *mtd; ++ struct resource *res; ++}; ++ ++#define NR_SUBMTD 4 ++ ++static struct sa_info info[NR_SUBMTD]; ++ ++static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) ++{ ++ struct mtd_info *subdev[nr]; ++ struct map_info *maps; ++ int i, found = 0, ret = 0; ++ + /* +- * For simple flash devices, use ioremap to map the flash. ++ * Allocate the map_info structs in one go. + */ +- if (base != (unsigned long)-1) { +- if (!request_mem_region(base, sa1100_map.size, "flash")) +- return -EBUSY; +- sa1100_map.map_priv_2 = base; +- sa1100_map.map_priv_1 = (unsigned long) +- ioremap(base, sa1100_map.size); ++ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); ++ if (!maps) ++ return -ENOMEM; ++ ++ memset(maps, 0, sizeof(struct map_info) * nr); ++ ++ /* ++ * Claim and then map the memory regions. ++ */ ++ for (i = 0; i < nr; i++) { ++ if (sa[i].base == (unsigned long)-1) ++ break; ++ ++ sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash"); ++ if (!sa[i].res) { ++ ret = -EBUSY; ++ break; ++ } ++ ++ sa[i].map = maps + i; ++ ++ sa[i].vbase = ioremap(sa[i].base, sa[i].size); ++ if (!sa[i].vbase) { + ret = -ENOMEM; +- if (!sa1100_map.map_priv_1) +- goto out_err; ++ break; + } + ++ sa[i].map->virt = (unsigned long)sa[i].vbase; ++ sa[i].map->phys = sa[i].base; ++ sa[i].map->set_vpp = sa[i].set_vpp; ++ sa[i].map->buswidth = sa[i].width; ++ sa[i].map->size = sa[i].size; ++ ++ simple_map_init(sa[i].map); ++ + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ +- printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); +- mymtd = do_map_probe("cfi_probe", &sa1100_map); ++ sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); ++ if (sa[i].mtd == NULL) { + ret = -ENXIO; +- if (!mymtd) +- goto out_err; +- mymtd->module = THIS_MODULE; ++ break; ++ } ++ sa[i].mtd->owner = THIS_MODULE; ++ subdev[i] = sa[i].mtd; ++ ++ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " ++ "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, ++ sa[i].width * 8); ++ found += 1; ++ } + + /* +- * Dynamic partition selection stuff (might override the static ones) ++ * ENXIO is special. It means we didn't find a chip when ++ * we probed. We need to tear down the mapping, free the ++ * resource and mark it as such. + */ +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ if (ret == -ENXIO) { ++ iounmap(sa[i].vbase); ++ sa[i].vbase = NULL; ++ release_resource(sa[i].res); ++ sa[i].res = NULL; ++ } + +- if (ret > 0) { +- part_type = "RedBoot"; +- parsed_nr_parts = ret; ++ /* ++ * If we found one device, don't bother with concat support. ++ * If we found multiple devices, use concat if we have it ++ * available, otherwise fail. ++ */ ++ if (ret == 0 || ret == -ENXIO) { ++ if (found == 1) { ++ *rmtd = subdev[0]; ++ ret = 0; ++ } else if (found > 1) { ++ /* ++ * We detected multiple devices. Concatenate ++ * them together. ++ */ ++#ifdef CONFIG_MTD_CONCAT ++ *rmtd = mtd_concat_create(subdev, found, ++ "sa1100 flash"); ++ if (*rmtd == NULL) ++ ret = -ENXIO; ++#else ++ printk(KERN_ERR "SA1100 flash: multiple devices " ++ "found but MTD concat support disabled.\n"); ++ ret = -ENXIO; ++#endif + } + } ++ ++ /* ++ * If we failed, clean up. ++ */ ++ if (ret) { ++ do { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].vbase) ++ iounmap(sa[i].vbase); ++ if (sa[i].res) ++ release_resource(sa[i].res); ++ } while (i--); ++ ++ kfree(maps); ++ } ++ ++ return ret; ++} ++ ++static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) ++{ ++ int i; ++ ++ del_mtd_partitions(mtd); ++ ++#ifdef CONFIG_MTD_CONCAT ++ if (mtd != sa[0].mtd) ++ mtd_concat_destroy(mtd); + #endif +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100"); +- if (ret > 0) { +- part_type = "Command Line"; +- parsed_nr_parts = ret; ++ ++ for (i = NR_SUBMTD; i >= 0; i--) { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].vbase) ++ iounmap(sa[i].vbase); ++ if (sa[i].res) ++ release_resource(sa[i].res); + } ++ kfree(sa[0].map); ++} ++ ++/* ++ * A Thought: can we automatically detect the flash? ++ * - Check to see if the region is busy (yes -> failure) ++ * - Is the MSC setup for flash (no -> failure) ++ * - Probe for flash ++ */ ++ ++static struct map_info sa1100_probe_map __initdata = { ++ .name = "SA1100-flash", ++}; ++ ++static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) ++{ ++ struct mtd_info *mtd; ++ ++ printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", ++ phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); ++ ++ if (check_mem_region(phys, 0x08000000)) { ++ printk("busy\n"); ++ return; + } +-#endif + +- if (parsed_nr_parts > 0) { +- parts = parsed_parts; +- nb_parts = parsed_nr_parts; ++ if ((msc & 3) == 1) { ++ printk("wrong type\n"); ++ return; + } + +- if (nb_parts == 0) { +- printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); +- add_mtd_device(mymtd); +- } else { +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); +- add_mtd_partitions(mymtd, parts, nb_parts); ++ sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4; ++ sa1100_probe_map.size = SZ_1M; ++ sa1100_probe_map.phys = phys; ++ sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M); ++ if (sa1100_probe_map.virt == 0) ++ goto fail; ++ simple_map_init(&sa1100_probe_map); ++ ++ /* Shame cfi_probe blurts out kernel messages... */ ++ mtd = do_map_probe("cfi_probe", &sa1100_probe_map); ++ if (mtd) ++ map_destroy(mtd); ++ iounmap((void *)sa1100_probe_map.virt); ++ ++ if (!mtd) ++ goto fail; ++ ++ printk("pass\n"); ++ return; ++ ++ fail: ++ printk("failed\n"); ++} ++ ++static void __init sa1100_probe_flash(void) ++{ ++ printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); ++ sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); ++ sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); ++ sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); ++ sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); ++ sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); ++ sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); ++ printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); ++} ++ ++static int __init sa1100_locate_flash(void) ++{ ++ int i, nr = -ENODEV; ++ ++ sa1100_probe_flash(); ++ ++ if (machine_is_adsbitsy()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_assabet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ info[1].base = SA1100_CS1_PHYS; /* neponset */ ++ info[1].size = SZ_32M; ++ nr = 2; ++ } ++ if (machine_is_badge4()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_cerf()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_consus()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_flexanet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_freebird()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_frodo()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_graphicsclient()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; + } +- return 0; ++ if (machine_is_graphicsmaster()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_h3xxx()) { ++ info[0].set_vpp = h3xxx_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_huw_webpanel()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_itsy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_jornada720()) { ++ info[0].set_vpp = jornada720_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_nanoengine()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[1].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pangolin()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_pfs168()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pleb()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_4M; ++ nr = 2; ++ } ++ if (machine_is_pt_system3()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_shannon()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ nr = 1; ++ } ++ if (machine_is_sherman()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_simpad()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_16M; ++ nr = 2; ++ } ++ if (machine_is_stork()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_trizeps()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_victor()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_2M; ++ nr = 1; ++ } ++ if (machine_is_yopy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_64M; ++ nr = 2; ++ } ++ ++ if (nr < 0) ++ return nr; + +- out_err: +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++ /* ++ * Retrieve the buswidth from the MSC registers. ++ * We currently only implement CS0 and CS1 here. ++ */ ++ for (i = 0; i < nr; i++) { ++ switch (info[i].base) { ++ default: ++ printk(KERN_WARNING "SA1100 flash: unknown base address " ++ "0x%08lx, assuming CS0\n", info[i].base); ++ case SA1100_CS0_PHYS: ++ info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; ++ break; ++ ++ case SA1100_CS1_PHYS: ++ info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; ++ break; + } +- return ret; ++ } ++ ++ return nr; + } + +-static void __exit sa1100_mtd_cleanup(void) ++static struct mtd_partition *parsed_parts; ++const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; ++ ++static void __init sa1100_locate_partitions(struct mtd_info *mtd) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); ++ const char *part_type = NULL; ++ int nr_parts = 0; ++ ++ do { ++ /* ++ * Partition selection stuff. ++ */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); ++ if (nr_parts > 0) { ++ part_type = "dynamic"; ++ break; + } +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++#endif ++#ifdef CONFIG_MTD_SA1100_STATICMAP ++ nr_parts = sa1100_static_partitions(&parsed_parts); ++ if (nr_parts > 0) { ++ part_type = "static"; ++ break; + } ++#endif ++ } while (0); ++ ++ if (nr_parts == 0) { ++ printk(KERN_NOTICE "SA1100 flash: no partition info " ++ "available, registering whole flash\n"); ++ add_mtd_device(mtd); ++ } else { ++ printk(KERN_NOTICE "SA1100 flash: using %s partition " ++ "definition\n", part_type); ++ add_mtd_partitions(mtd, parsed_parts, nr_parts); ++ } ++ ++ /* Always succeeds. */ ++} ++ ++static void __exit sa1100_destroy_partitions(void) ++{ ++ if (parsed_parts) ++ kfree(parsed_parts); ++} ++ ++static struct mtd_info *mymtd; ++ ++static int __init sa1100_mtd_init(void) ++{ ++ int ret; ++ int nr; ++ ++ nr = sa1100_locate_flash(); ++ if (nr < 0) ++ return nr; ++ ++ ret = sa1100_setup_mtd(info, nr, &mymtd); ++ if (ret == 0) ++ sa1100_locate_partitions(mymtd); ++ ++ return ret; ++} ++ ++static void __exit sa1100_mtd_cleanup(void) ++{ ++ sa1100_destroy_mtd(info, mymtd); ++ sa1100_destroy_partitions(); + } + + module_init(sa1100_mtd_init); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c linux/drivers/mtd/maps/sbc8240.c +--- linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/sbc8240.c 2004-11-19 10:25:11.967189456 +0100 +@@ -0,0 +1,417 @@ ++/* ++ * Handle mapping of the flash memory access routines on the SBC8240 board. ++ * ++ * Carolyn Smith, Tektronix, Inc. ++ * ++ * This code is GPLed ++ * ++ * $Id: sbc8240.c,v 1.2 2003/09/30 19:37:00 thayne Exp $ ++ * ++ */ ++ ++/* ++ * The SBC8240 has 2 flash banks. ++ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors. ++ * It contains the U-Boot code (7 sectors) and the environment (1 sector). ++ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector, ++ * 2 x 8 KiB sectors, 1 x 16 KiB sectors. ++ * Both parts are JEDEC compatible. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#include ++#endif ++ ++#define DEBUG ++ ++#ifdef DEBUG ++# define debugk(fmt,args...) printk(fmt ,##args) ++#else ++# define debugk(fmt,args...) ++#endif ++ ++ ++#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */ ++#define WINDOW_SIZE0 0x00080000 ++#define BUSWIDTH0 1 ++ ++#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */ ++#define WINDOW_SIZE1 0x00400000 ++#define BUSWIDTH1 8 ++ ++#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */ ++#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */ ++ ++ ++static __u8 sbc8240_read8 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->map_priv_1 + ofs); ++} ++ ++static __u16 sbc8240_read16 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->map_priv_1 + ofs); ++} ++ ++static __u32 sbc8240_read32 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->map_priv_1 + ofs); ++} ++ ++unsigned long long result64; ++ ++static __u64 sbc8240_read64 (struct map_info *map, unsigned long ofs) ++{ ++ unsigned long flags, msr, saved_msr; ++ volatile long saved_fr[2]; ++ volatile unsigned long long result; ++ volatile unsigned long *p; ++ ++ save_flags(flags); ++ cli(); ++ ++ /* turn off floating point unavailable exceptions */ ++ ++ __asm__ __volatile__ ( ++ "mfmsr %0" ++ : "=r" (msr) :); ++ ++ saved_msr = msr; ++ msr |= MSR_FP; ++ msr &= ~(MSR_FE0 | MSR_FE1); ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (msr)); ++ ++ /* read the data via a floating point register */ ++ ++ ofs = map->map_priv_1 + ofs; ++ p = (unsigned long *) &result64; ++ ++ __asm__ __volatile__ ( ++ "lfd 1,0(%1)\n" ++ "stfd 1,0(%0)\n" ++ : : "r" (p), "r" (ofs) ++ ); ++ ++ /* restore state */ ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (saved_msr)); ++ ++ restore_flags(flags); ++ ++ p = (unsigned long *) &result64; ++ debugk("sbc8240_read64 ofs 0x%x result 0x%08x%08x\n", ofs, *p, *(p+1)); ++ ++ return result64; ++} ++ ++static void sbc8240_copy_from (struct map_info *map, ++ void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio (to, (void *) (map->map_priv_1 + from), len); ++} ++ ++static void sbc8240_write8 (struct map_info *map, __u8 d, unsigned long adr) ++{ ++ __raw_writeb(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write16 (struct map_info *map, __u16 d, ++ unsigned long adr) ++{ ++ __raw_writew(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write32 (struct map_info *map, __u32 d, ++ unsigned long adr) ++{ ++ __raw_writel(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write64 (struct map_info *map, __u64 data, ++ unsigned long adr) ++{ ++ unsigned long long tmp; ++ unsigned long flags, msr, saved_msr, *p; ++ volatile long saved_fr[2]; ++ ++ save_flags(flags); ++ cli(); ++ ++ /* turn off floating point unavailable exceptions */ ++ ++ __asm__ __volatile__ ( ++ "mfmsr %0" ++ : "=r" (msr) :); ++ ++ saved_msr = msr; ++ msr |= MSR_FP; ++ msr &= ~(MSR_FE0 | MSR_FE1); ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (msr)); ++ ++ ++ /* write the data via a floating point register */ ++ ++ tmp = data; ++ p = (unsigned long *) &tmp; ++ adr = map->map_priv_1 + adr; ++ debugk("sbc8240_write64 adr 0x%x data 0x%08x%08x\n", adr, *p, *(p+1)); ++ ++ __asm__ __volatile__ ( ++ "stfd 1,0(%2)\n" ++ "lfd 1,0(%0)\n" ++ "stfd 1,0(%1)\n" ++ "lfd 1,0(%2)\n" ++ : : "r" (p), "r" (adr), "b" (saved_fr) ++ ); ++ ++ /* restore state */ ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (saved_msr)); ++ ++ restore_flags(flags); ++} ++ ++static void sbc8240_copy_to (struct map_info *map, ++ unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio ((void *) (map->map_priv_1 + to), from, len); ++} ++ ++static struct map_info sbc8240_map[2] = { ++ { ++ .name = "sbc8240 Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .buswidth = BUSWIDTH0, ++ .read8 = sbc8240_read8, ++ .read16 = sbc8240_read16, ++ .read32 = sbc8240_read32, ++ .read64 = sbc8240_read64, ++ .copy_from = sbc8240_copy_from, ++ .write8 = sbc8240_write8, ++ .write16 = sbc8240_write16, ++ .write32 = sbc8240_write32, ++ .write64 = sbc8240_write64, ++ .copy_to = sbc8240_copy_to ++ }, ++ { ++ .name = "sbc8240 Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .buswidth = BUSWIDTH1, ++ .read8 = sbc8240_read8, ++ .read16 = sbc8240_read16, ++ .read32 = sbc8240_read32, ++ .read64 = sbc8240_read64, ++ .copy_from = sbc8240_copy_from, ++ .write8 = sbc8240_write8, ++ .write16 = sbc8240_write16, ++ .write32 = sbc8240_write32, ++ .write64 = sbc8240_write64, ++ .copy_to = sbc8240_copy_to ++ } ++}; ++ ++#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info)) ++ ++/* ++ * The following defines the partition layout of SBC8240 boards. ++ * ++ * See include/linux/mtd/partitions.h for definition of the ++ * mtd_partition structure. ++ * ++ * The *_max_flash_size is the maximum possible mapped flash size ++ * which is not necessarily the actual flash size. It must correspond ++ * to the value specified in the mapping definition defined by the ++ * "struct map_desc *_io_desc" for the corresponding machine. ++ */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++static struct mtd_partition sbc8240_uboot_partitions [] = { ++ /* Bank 0 */ ++ { ++ .name = "U-boot", /* U-Boot Firmware */ ++ .offset = 0, ++ .size = 0x00070000, /* 7 x 64 KiB sectors */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, ++ { ++ .name = "environment", /* U-Boot environment */ ++ .offset = 0x00070000, ++ .size = 0x00010000, /* 1 x 64 KiB sector */ ++ }, ++}; ++ ++static struct mtd_partition sbc8240_fs_partitions [] = { ++ { ++ .name = "jffs", /* JFFS filesystem */ ++ .offset = 0, ++ .size = 0x003C0000, /* 4 * 15 * 64KiB */ ++ }, ++ { ++ .name = "tmp32", ++ .offset = 0x003C0000, ++ .size = 0x00020000, /* 4 * 32KiB */ ++ }, ++ { ++ .name = "tmp8a", ++ .offset = 0x003E0000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp8b", ++ .offset = 0x003E8000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp16", ++ .offset = 0x003F0000, ++ .size = 0x00010000, /* 4 * 16KiB */ ++ } ++}; ++ ++#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++ ++/* trivial struct to describe partition information */ ++struct mtd_part_def ++{ ++ int nums; ++ unsigned char *type; ++ struct mtd_partition* mtd_part; ++}; ++ ++static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS]; ++static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; ++ ++ ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ ++int __init init_sbc8240_mtd (void) ++{ ++ static struct _cjs { ++ u_long addr; ++ u_long size; ++ } pt[NUM_FLASH_BANKS] = { ++ { ++ .addr = WINDOW_ADDR0, ++ .size = WINDOW_SIZE0 ++ }, ++ { ++ .addr = WINDOW_ADDR1, ++ .size = WINDOW_SIZE1 ++ }, ++ }; ++ ++ int devicesfound = 0; ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr); ++ ++ sbc8240_map[i].map_priv_1 = ++ (unsigned long) ioremap (pt[i].addr, pt[i].size); ++ if (!sbc8240_map[i].map_priv_1) { ++ printk (MSG_PREFIX "failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]); ++ ++ if (sbc8240_mtd[i]) { ++ sbc8240_mtd[i]->module = THIS_MODULE; ++ devicesfound++; ++ } ++ } ++ ++ if (!devicesfound) { ++ printk(KERN_NOTICE MSG_PREFIX ++ "No suppported flash chips found!\n"); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions; ++ sbc8240_part_banks[0].type = "static image"; ++ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions); ++ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions; ++ sbc8240_part_banks[1].type = "static file system"; ++ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions); ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ ++ if (!sbc8240_mtd[i]) continue; ++ if (sbc8240_part_banks[i].nums == 0) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "No partition info available, registering whole device\n"); ++ add_mtd_device(sbc8240_mtd[i]); ++ } else { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name); ++ add_mtd_partitions (sbc8240_mtd[i], ++ sbc8240_part_banks[i].mtd_part, ++ sbc8240_part_banks[i].nums); ++ } ++ } ++#else ++ printk(KERN_NOTICE MSG_PREFIX ++ "Registering %d flash banks at once\n", devicesfound); ++ ++ for (i = 0; i < devicesfound; i++) { ++ add_mtd_device(sbc8240_mtd[i]); ++ } ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ return devicesfound == 0 ? -ENXIO : 0; ++} ++ ++static void __exit cleanup_sbc8240_mtd (void) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ if (sbc8240_mtd[i]) { ++ del_mtd_device (sbc8240_mtd[i]); ++ map_destroy (sbc8240_mtd[i]); ++ } ++ if (sbc8240_map[i].map_priv_1) { ++ iounmap ((void *) sbc8240_map[i].map_priv_1); ++ sbc8240_map[i].map_priv_1 = 0; ++ } ++ } ++} ++ ++module_init (init_sbc8240_mtd); ++module_exit (cleanup_sbc8240_mtd); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Carolyn Smith "); ++MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards"); ++ +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c +--- linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/sbc_gxx.c 2004-11-19 10:25:11.969189152 +0100 +@@ -17,7 +17,7 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +- $Id: sbc_gxx.c,v 1.21 2003/01/24 13:40:14 dwmw2 Exp $ ++ $Id: sbc_gxx.c,v 1.26 2003/05/26 08:50:36 dwmw2 Exp $ + + The SBC-MediaGX / SBC-GXx has up to 16 MiB of + Intel StrataFlash (28F320/28F640) in x8 mode. +@@ -91,14 +91,14 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "SBC-GXx flash boot partition", +- offset: 0, +- size: BOOT_PARTITION_SIZE_KiB*1024 }, +- { name: "SBC-GXx flash data partition", +- offset: BOOT_PARTITION_SIZE_KiB*1024, +- size: (DATA_PARTITION_SIZE_KiB)*1024 }, +- { name: "SBC-GXx flash application partition", +- offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } ++ { .name = "SBC-GXx flash boot partition", ++ .offset = 0, ++ .size = BOOT_PARTITION_SIZE_KiB*1024 }, ++ { .name = "SBC-GXx flash data partition", ++ .offset = BOOT_PARTITION_SIZE_KiB*1024, ++ .size = (DATA_PARTITION_SIZE_KiB)*1024 }, ++ { .name = "SBC-GXx flash application partition", ++ .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } + }; + + #define NUM_PARTITIONS 3 +@@ -203,19 +203,20 @@ + } + + static struct map_info sbc_gxx_map = { +- name: "SBC-GXx flash", +- size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount ++ .name = "SBC-GXx flash", ++ .phys = NO_XIP, ++ .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ +- buswidth: 1, +- read8: sbc_gxx_read8, +- read16: sbc_gxx_read16, +- read32: sbc_gxx_read32, +- copy_from: sbc_gxx_copy_from, +- write8: sbc_gxx_write8, +- write16: sbc_gxx_write16, +- write32: sbc_gxx_write32, +- copy_to: sbc_gxx_copy_to ++ .buswidth = 1, ++ .read8 = sbc_gxx_read8, ++ .read16 = sbc_gxx_read16, ++ .read32 = sbc_gxx_read32, ++ .copy_from = sbc_gxx_copy_from, ++ .write8 = sbc_gxx_write8, ++ .write16 = sbc_gxx_write16, ++ .write32 = sbc_gxx_write32, ++ .copy_to = sbc_gxx_copy_to + }; + + /* MTD device for all of the flash. */ +@@ -234,12 +235,6 @@ + + int __init init_sbc_gxx(void) + { +- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { +- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", +- sbc_gxx_map.name, +- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); +- return -EAGAIN; +- } + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", +@@ -247,7 +242,14 @@ + return -EIO; + } + +- request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" ); ++ if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { ++ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", ++ sbc_gxx_map.name, ++ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); ++ iounmap((void *)iomapadr); ++ return -EAGAIN; ++ } ++ + + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + sbc_gxx_map.name, +@@ -261,7 +263,7 @@ + return -ENXIO; + } + +- all_mtd->module=THIS_MODULE; ++ all_mtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c +--- linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/sc520cdp.c 2004-11-19 10:25:11.970189000 +0100 +@@ -16,7 +16,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id: sc520cdp.c,v 1.11 2002/03/08 16:34:35 rkaiser Exp $ ++ * $Id: sc520cdp.c,v 1.15 2003/05/21 12:45:20 dwmw2 Exp $ + * + * + * The SC520CDP is an evaluation board for the Elan SC520 processor available +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -84,88 +85,25 @@ + #define WINDOW_SIZE_1 0x00800000 + #define WINDOW_SIZE_2 0x00080000 + +-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + static struct map_info sc520cdp_map[] = { + { +- name: "SC520CDP Flash Bank #0", +- size: WINDOW_SIZE_0, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_0 ++ .name = "SC520CDP Flash Bank #0", ++ .size = WINDOW_SIZE_0, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR_0 + }, + { +- name: "SC520CDP Flash Bank #1", +- size: WINDOW_SIZE_1, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_1 ++ .name = "SC520CDP Flash Bank #1", ++ .size = WINDOW_SIZE_1, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR_1 + }, + { +- name: "SC520CDP DIL Flash", +- size: WINDOW_SIZE_2, +- buswidth: 1, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_2 ++ .name = "SC520CDP DIL Flash", ++ .size = WINDOW_SIZE_2, ++ .buswidth = 1, ++ .phys = WINDOW_ADDR_2 + }, + }; + +@@ -255,9 +193,9 @@ + /* map in SC520's MMCR area */ + mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); + if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */ +- /* force map_priv_2 fields to BIOS defaults: */ ++ /* force physical address fields to BIOS defaults: */ + for(i = 0; i < NUM_FLASH_BANKS; i++) +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + return; + } + +@@ -282,7 +220,7 @@ + sc520cdp_map[i].name); + printk(KERN_NOTICE "Trying default address 0x%lx\n", + par_table[i].default_address); +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + } + } + iounmap((void *)mmcr); +@@ -300,13 +238,18 @@ + #endif + + for (i = 0; i < NUM_FLASH_BANKS; i++) { +- printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2); +- sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size); ++ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", ++ sc520cdp_map[i].size, sc520cdp_map[i].phys); + +- if (!sc520cdp_map[i].map_priv_1) { ++ sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); ++ ++ if (!sc520cdp_map[i].virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&sc520cdp_map[i]); ++ + mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); + if(!mymtd[i]) + mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); +@@ -314,11 +257,11 @@ + mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); + + if (mymtd[i]) { +- mymtd[i]->module = THIS_MODULE; ++ mymtd[i]->owner = THIS_MODULE; + ++devices_found; + } + else { +- iounmap((void *)sc520cdp_map[i].map_priv_1); ++ iounmap((void *)sc520cdp_map[i].virt); + } + } + if(devices_found >= 2) { +@@ -346,9 +289,9 @@ + for (i = 0; i < NUM_FLASH_BANKS; i++) { + if (mymtd[i]) + map_destroy(mymtd[i]); +- if (sc520cdp_map[i].map_priv_1) { +- iounmap((void *)sc520cdp_map[i].map_priv_1); +- sc520cdp_map[i].map_priv_1 = 0; ++ if (sc520cdp_map[i].virt) { ++ iounmap((void *)sc520cdp_map[i].virt); ++ sc520cdp_map[i].virt = 0; + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c linux/drivers/mtd/maps/scb2_flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/scb2_flash.c 2004-11-19 10:25:11.972188696 +0100 +@@ -1,6 +1,6 @@ + /* + * MTD map driver for BIOS Flash on Intel SCB2 boards +- * $Id: scb2_flash.c,v 1.2 2003/01/24 13:09:56 dwmw2 Exp $ ++ * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $ + * Copyright (C) 2002 Sun Microsystems, Inc. + * Tim Hockin + * +@@ -14,7 +14,7 @@ + * try to request it here, but if it fails, we carry on anyway. + * + * This is how the chip is attached, so said the schematic: +- * * a 4 MiB (32 Mb) 16 bit chip ++ * * a 4 MiB (32 Mib) 16 bit chip + * * a 1 MiB memory region + * * A20 and A21 pulled up + * * D8-D15 ignored +@@ -48,6 +48,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -60,65 +61,13 @@ + #define SCB2_ADDR 0xfff00000 + #define SCB2_WINDOW 0x00100000 + +-static __u8 scb2_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scb2_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 scb2_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void scb2_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static void *scb2_ioaddr; + static struct mtd_info *scb2_mtd; + struct map_info scb2_map = { +- name: "SCB2 BIOS Flash", +- size: 0, +- buswidth: 1, +- read8: scb2_read8, +- read16: scb2_read16, +- read32: scb2_read32, +- copy_from: scb2_copy_from, +- write8: scb2_write8, +- write16: scb2_write16, +- write32: scb2_write32, +- copy_to: scb2_copy_to, ++ .name = "SCB2 BIOS Flash", ++ .size = 0, ++ .buswidth = 1, + }; + static int region_fail; + +@@ -137,6 +86,8 @@ + return -1; + } + ++ /* I wasn't here. I didn't see. dwmw2. */ ++ + /* the chip is sometimes bigger than the map - what a waste */ + mtd->size = map->size; + +@@ -211,9 +162,12 @@ + return -ENOMEM; + } + +- scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr; ++ scb2_map.phys = SCB2_ADDR; ++ scb2_map.virt = (unsigned long)scb2_ioaddr; + scb2_map.size = SCB2_WINDOW; + ++ simple_map_init(&scb2_map); ++ + /* try to find a chip */ + scb2_mtd = do_map_probe("cfi_probe", &scb2_map); + +@@ -225,7 +179,7 @@ + return -ENODEV; + } + +- scb2_mtd->module = THIS_MODULE; ++ scb2_mtd->owner = THIS_MODULE; + if (scb2_fixup_mtd(scb2_mtd) < 0) { + del_mtd_device(scb2_mtd); + map_destroy(scb2_mtd); +@@ -235,7 +189,7 @@ + return -ENODEV; + } + +- printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n", ++ printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", + scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); + + add_mtd_device(scb2_mtd); +@@ -266,19 +220,19 @@ + + static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = { + { +- vendor: PCI_VENDOR_ID_SERVERWORKS, +- device: PCI_DEVICE_ID_SERVERWORKS_CSB5, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID + }, + { 0, } + }; + + static struct pci_driver scb2_flash_driver = { +- name: "Intel SCB2 BIOS Flash", +- id_table: scb2_flash_pci_ids, +- probe: scb2_flash_probe, +- remove: __devexit_p(scb2_flash_remove), ++ .name = "Intel SCB2 BIOS Flash", ++ .id_table = scb2_flash_pci_ids, ++ .probe = scb2_flash_probe, ++ .remove = __devexit_p(scb2_flash_remove), + }; + + static int __init +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c +--- linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/scx200_docflash.c 2004-11-19 10:25:11.973188544 +0100 +@@ -2,7 +2,7 @@ + + Copyright (c) 2001,2002 Christer Weinigel + +- $Id: scx200_docflash.c,v 1.1 2003/01/24 13:20:40 dwmw2 Exp $ ++ $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $ + + National Semiconductor SCx200 flash mapped with DOCCS + */ +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -75,46 +76,9 @@ + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + #endif + +-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info scx200_docflash_map = { + .name = "NatSemi SCx200 DOCCS Flash", +- .read8 = scx200_docflash_read8, +- .read16 = scx200_docflash_read16, +- .copy_from = scx200_docflash_copy_from, +- .write8 = scx200_docflash_write8, +- .write16 = scx200_docflash_write16, +- .copy_to = scx200_docflash_copy_to + }; + + int __init init_scx200_docflash(void) +@@ -213,8 +177,11 @@ + else + scx200_docflash_map.buswidth = 2; + +- scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); +- if (!scx200_docflash_map.map_priv_1) { ++ simple_map_init(&scx200_docflash_map); ++ ++ scx200_docflash_map.phys = docmem.start; ++ scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); ++ if (!scx200_docflash_map.virt) { + printk(KERN_ERR NAME ": failed to ioremap the flash\n"); + release_resource(&docmem); + return -EIO; +@@ -223,7 +190,7 @@ + mymtd = do_map_probe(flashtype, &scx200_docflash_map); + if (!mymtd) { + printk(KERN_ERR NAME ": unable to detect flash\n"); +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ iounmap((void *)scx200_docflash_map.virt); + release_resource(&docmem); + return -ENXIO; + } +@@ -231,7 +198,7 @@ + if (size < mymtd->size) + printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #if PARTITION + partition_info[3].offset = mymtd->size-partition_info[3].size; +@@ -253,8 +220,8 @@ + #endif + map_destroy(mymtd); + } +- if (scx200_docflash_map.map_priv_1) { +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ if (scx200_docflash_map.virt) { ++ iounmap((void *)scx200_docflash_map.virt); + release_resource(&docmem); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c +--- linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/solutionengine.c 2004-11-19 10:25:11.975188240 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $ ++ * $Id: solutionengine.c,v 1.10 2003/05/21 12:45:20 dwmw2 Exp $ + * + * Flash and EPROM on Hitachi Solution Engine and similar boards. + * +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -18,60 +19,39 @@ + #include + + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-__u32 soleng_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +- + static struct mtd_info *flash_mtd; + static struct mtd_info *eprom_mtd; + + static struct mtd_partition *parsed_parts; + + struct map_info soleng_eprom_map = { +- name: "Solution Engine EPROM", +- size: 0x400000, +- buswidth: 4, +- copy_from: soleng_copy_from, ++ .name = "Solution Engine EPROM", ++ .size = 0x400000, ++ .buswidth = 4, + }; + + struct map_info soleng_flash_map = { +- name: "Solution Engine FLASH", +- size: 0x400000, +- buswidth: 4, +- read32: soleng_read32, +- copy_from: soleng_copy_from, +- write32: soleng_write32, ++ .name = "Solution Engine FLASH", ++ .size = 0x400000, ++ .buswidth = 4, + }; + ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ + #ifdef CONFIG_MTD_SUPERH_RESERVE + static struct mtd_partition superh_se_partitions[] = { + /* Reserved for boot code, read-only */ + { +- name: "flash_boot", +- offset: 0x00000000, +- size: CONFIG_MTD_SUPERH_RESERVE, +- mask_flags: MTD_WRITEABLE, ++ .name = "flash_boot", ++ .offset = 0x00000000, ++ .size = CONFIG_MTD_SUPERH_RESERVE, ++ .mask_flags = MTD_WRITEABLE, + }, + /* All else is writable (e.g. JFFS) */ + { +- name: "Flash FS", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "Flash FS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + } + }; + #endif /* CONFIG_MTD_SUPERH_RESERVE */ +@@ -81,16 +61,22 @@ + int nr_parts = 0; + + /* First probe at offset 0 */ +- soleng_flash_map.map_priv_1 = P2SEGADDR(0); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000); ++ soleng_flash_map.phys = 0; ++ soleng_flash_map.virt = P2SEGADDR(0); ++ soleng_eprom_map.phys = 0x01000000; ++ soleng_eprom_map.virt = P1SEGADDR(0x01000000); ++ simple_map_init(&soleng_eprom_map); ++ simple_map_init(&soleng_flash_map); + + printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); + flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); + if (!flash_mtd) { + /* Not there. Try swapping */ + printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n"); +- soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0); ++ soleng_flash_map.phys = 0x01000000; ++ soleng_flash_map.virt = P2SEGADDR(0x01000000); ++ soleng_eprom_map.phys = 0; ++ soleng_eprom_map.virt = P1SEGADDR(0); + flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); + if (!flash_mtd) { + /* Eep. */ +@@ -99,25 +85,20 @@ + } + } + printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n", +- soleng_flash_map.map_priv_1 & 0x1fffffff, +- soleng_eprom_map.map_priv_1 & 0x1fffffff); +- flash_mtd->module = THIS_MODULE; ++ soleng_flash_map.phys & 0x1fffffff, ++ soleng_eprom_map.phys & 0x1fffffff); ++ flash_mtd->owner = THIS_MODULE; + + eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map); + if (eprom_mtd) { +- eprom_mtd->module = THIS_MODULE; ++ eprom_mtd->owner = THIS_MODULE; + add_mtd_device(eprom_mtd); + } + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); +- if (nr_parts > 0) +- printk(KERN_NOTICE "Found RedBoot partition table.\n"); +- else if (nr_parts < 0) +- printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +-#endif /* CONFIG_MTD_REDBOOT_PARTS */ ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); ++ + #if CONFIG_MTD_SUPERH_RESERVE +- if (nr_parts == 0) { ++ if (nr_parts <= 0) { + printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", + CONFIG_MTD_SUPERH_RESERVE); + parsed_parts = superh_se_partitions; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c linux/drivers/mtd/maps/sun_uflash.c +--- linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/sun_uflash.c 2004-11-19 10:25:11.976188088 +0100 +@@ -1,4 +1,4 @@ +-/* $Id: sun_uflash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ ++/* $Id: sun_uflash.c,v 1.7 2003/05/20 20:59:32 dwmw2 Exp $ + * + * sun_uflash - Driver implementation for user-programmable flash + * present on many Sun Microsystems SME boardsets. +@@ -48,60 +48,11 @@ + struct list_head list; + }; + +-__u8 uflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readb(map->map_priv_1 + ofs)); +-} +- +-__u16 uflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readw(map->map_priv_1 + ofs)); +-} +- +-__u32 uflash_read32(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readl(map->map_priv_1 + ofs)); +-} +- +-void uflash_copy_from(struct map_info *map, void *to, unsigned long from, +- ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +-} +- +-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +-} +- +-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +-} +- +-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from, +- ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info uflash_map_templ = { +- name: "SUNW,???-????", +- size: UFLASH_WINDOW_SIZE, +- buswidth: UFLASH_BUSWIDTH, +- read8: uflash_read8, +- read16: uflash_read16, +- read32: uflash_read32, +- copy_from: uflash_copy_from, +- write8: uflash_write8, +- write16: uflash_write16, +- write32: uflash_write32, +- copy_to: uflash_copy_to ++ .name = "SUNW,???-????", ++ .size = UFLASH_WINDOW_SIZE, ++ .buswidth = UFLASH_BUSWIDTH, + }; + + int uflash_devinit(struct linux_ebus_device* edev) +@@ -145,20 +96,22 @@ + if(0 != pdev->name && 0 < strlen(pdev->name)) { + pdev->map.name = pdev->name; + } +- +- pdev->map.map_priv_1 = ++ pdev->phys = edev->resource[0].start; ++ pdev->virt = + (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size); +- if(0 == pdev->map.map_priv_1) { ++ if(0 == pdev->map.virt) { + printk("%s: failed to map device\n", __FUNCTION__); + kfree(pdev->name); + kfree(pdev); + return(-1); + } + ++ simple_map_init(&pdev->map); ++ + /* MTD registration */ + pdev->mtd = do_map_probe("cfi_probe", &pdev->map); + if(0 == pdev->mtd) { +- iounmap((void *)pdev->map.map_priv_1); ++ iounmap((void *)pdev->map.virt); + kfree(pdev->name); + kfree(pdev); + return(-ENXIO); +@@ -166,7 +119,7 @@ + + list_add(&pdev->list, &device_list); + +- pdev->mtd->module = THIS_MODULE; ++ pdev->mtd->owner = THIS_MODULE; + + add_mtd_device(pdev->mtd); + return(0); +@@ -211,9 +164,9 @@ + del_mtd_device(udev->mtd); + map_destroy(udev->mtd); + } +- if(0 != udev->map.map_priv_1) { +- iounmap((void*)udev->map.map_priv_1); +- udev->map.map_priv_1 = 0; ++ if(0 != udev->map.virt) { ++ iounmap((void*)udev->map.virt); ++ udev->map.virt = 0; + } + if(0 != udev->name) { + kfree(udev->name); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c linux/drivers/mtd/maps/tqm8xxl.c +--- linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/tqm8xxl.c 2004-11-19 10:25:11.978187784 +0100 +@@ -2,7 +2,7 @@ + * Handle mapping of the flash memory access routines + * on TQM8xxL based devices. + * +- * $Id: tqm8xxl.c,v 1.4 2002/06/20 13:41:20 mag Exp $ ++ * $Id: tqm8xxl.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * + * based on rpxlite.c + * +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -51,46 +52,6 @@ + static unsigned long num_banks; + static unsigned long start_scan_addr; + +-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs) +-{ +- return *((__u8 *)(map->map_priv_1 + ofs)); +-} +- +-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs) +-{ +- return *((__u16 *)(map->map_priv_1 + ofs)); +-} +- +-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs) +-{ +- return *((__u32 *)(map->map_priv_1 + ofs)); +-} +- +-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *)( map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + /* + * Here are partition information for all known TQM8xxL series devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition +@@ -107,50 +68,48 @@ + static unsigned long tqm8xxl_max_flash_size = 0x00800000; + + /* partition definition for first flash bank +- * also ref. to "drivers\char\flash_config.c" ++ * (cf. "drivers/char/flash_config.c") + */ + static struct mtd_partition tqm8xxl_partitions[] = { + { +- name: "ppcboot", +- offset: 0x00000000, +- size: 0x00020000, /* 128KB */ +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ppcboot", ++ .offset = 0x00000000, ++ .size = 0x00020000, /* 128KB */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "kernel", /* default kernel image */ +- offset: 0x00020000, +- size: 0x000e0000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "kernel", /* default kernel image */ ++ .offset = 0x00020000, ++ .size = 0x000e0000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "user", +- offset: 0x00100000, +- size: 0x00100000, ++ .name = "user", ++ .offset = 0x00100000, ++ .size = 0x00100000, + }, + { +- name: "initrd", +- offset: 0x00200000, +- size: 0x00200000, ++ .name = "initrd", ++ .offset = 0x00200000, ++ .size = 0x00200000, + } + }; +-/* partition definition for second flahs bank */ ++/* partition definition for second flash bank */ + static struct mtd_partition tqm8xxl_fs_partitions[] = { + { +- name: "cramfs", +- offset: 0x00000000, +- size: 0x00200000, ++ .name = "cramfs", ++ .offset = 0x00000000, ++ .size = 0x00200000, + }, + { +- name: "jffs", +- offset: 0x00200000, +- size: 0x00200000, +- //size: MTDPART_SIZ_FULL, ++ .name = "jffs", ++ .offset = 0x00200000, ++ .size = 0x00200000, ++ .//size = MTDPART_SIZ_FULL, + } + }; + #endif + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + int __init init_tqm_mtd(void) + { + int idx = 0, ret = 0; +@@ -160,67 +119,73 @@ + + flash_addr = bd->bi_flashstart; + flash_size = bd->bi_flashsize; +- //request maximum flash size address spzce ++ ++ //request maximum flash size address space + start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); + if (!start_scan_addr) { +- //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR); +- printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); ++ printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + return -EIO; + } +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { ++ ++ for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { + if(mtd_size >= flash_size) + break; + +- printk("%s: chip probing count %d\n", __FUNCTION__, idx); ++ printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); + + map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL); +- if(map_banks[idx] == NULL) +- { +- //return -ENOMEM; ++ if(map_banks[idx] == NULL) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } ++ + memset((void *)map_banks[idx], 0, sizeof(struct map_info)); + map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); +- if(map_banks[idx]->name == NULL) +- { +- //return -ENOMEM; ++ ++ if (!map_banks[idx]->name) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } +- memset((void *)map_banks[idx]->name, 0, 16); +- + sprintf(map_banks[idx]->name, "TQM8xxL%d", idx); ++ + map_banks[idx]->size = flash_size; + map_banks[idx]->buswidth = 4; +- map_banks[idx]->read8 = tqm8xxl_read8; +- map_banks[idx]->read16 = tqm8xxl_read16; +- map_banks[idx]->read32 = tqm8xxl_read32; +- map_banks[idx]->copy_from = tqm8xxl_copy_from; +- map_banks[idx]->write8 = tqm8xxl_write8; +- map_banks[idx]->write16 = tqm8xxl_write16; +- map_banks[idx]->write32 = tqm8xxl_write32; +- map_banks[idx]->copy_to = tqm8xxl_copy_to; ++ ++ simple_map_init(map_banks[idx]); ++ ++ map_banks[idx]->virt = start_scan_addr; ++ map_banks[idx]->phys = flash_addr; ++ /* FIXME: This looks utterly bogus, but I'm trying to ++ preserve the behaviour of the original (shown here)... ++ + map_banks[idx]->map_priv_1 = + start_scan_addr + ((idx > 0) ? + (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); ++ */ ++ ++ if (idx && mtd_banks[idx-1]) { ++ map_banks[idx]->virt += mtd_banks[idx-1]->size; ++ map_banks[idx]->phys += mtd_banks[idx-1]->size; ++ } ++ + //start to probe flash chips + mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); +- if(mtd_banks[idx]) +- { +- mtd_banks[idx]->module = THIS_MODULE; ++ ++ if (mtd_banks[idx]) { ++ mtd_banks[idx]->owner = THIS_MODULE; + mtd_size += mtd_banks[idx]->size; + num_banks++; +- printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, ++ ++ printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + mtd_banks[idx]->name, mtd_banks[idx]->size); + } + } + + /* no supported flash chips found */ +- if(!num_banks) +- { +- printk("TQM8xxL: No support flash chips found!\n"); ++ if (!num_banks) { ++ printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n"); + ret = -ENXIO; + goto error_mem; + } +@@ -231,12 +196,13 @@ + */ + part_banks[0].mtd_part = tqm8xxl_partitions; + part_banks[0].type = "Static image"; +- part_banks[0].nums = NB_OF(tqm8xxl_partitions); ++ part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions); ++ + part_banks[1].mtd_part = tqm8xxl_fs_partitions; + part_banks[1].type = "Static file system"; +- part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions); +- for(idx = 0; idx < num_banks ; idx++) +- { ++ part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions); ++ ++ for(idx = 0; idx < num_banks ; idx++) { + if (part_banks[idx].nums == 0) { + printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx); + add_mtd_device(mtd_banks[idx]); +@@ -254,12 +220,9 @@ + #endif + return 0; + error_mem: +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { +- if(map_banks[idx] != NULL) +- { +- if(map_banks[idx]->name != NULL) +- { ++ for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { ++ if(map_banks[idx] != NULL) { ++ if(map_banks[idx]->name != NULL) { + kfree(map_banks[idx]->name); + map_banks[idx]->name = NULL; + } +@@ -267,18 +230,15 @@ + map_banks[idx] = NULL; + } + } +- //return -ENOMEM; + error: + iounmap((void *)start_scan_addr); +- //return -ENXIO; + return ret; + } + + static void __exit cleanup_tqm_mtd(void) + { + unsigned int idx = 0; +- for(idx = 0 ; idx < num_banks ; idx++) +- { ++ for(idx = 0 ; idx < num_banks ; idx++) { + /* destroy mtd_info previously allocated */ + if (mtd_banks[idx]) { + del_mtd_partitions(mtd_banks[idx]); +@@ -288,6 +248,7 @@ + kfree(map_banks[idx]->name); + kfree(map_banks[idx]); + } ++ + if (start_scan_addr) { + iounmap((void *)start_scan_addr); + start_scan_addr = 0; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c linux/drivers/mtd/maps/tsunami_flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/tsunami_flash.c 2004-11-19 10:25:11.979187632 +0100 +@@ -2,11 +2,13 @@ + * tsunami_flash.c + * + * flash chip on alpha ds10... +- * $Id: tsunami_flash.c,v 1.1 2002/01/10 22:59:13 eric Exp $ ++ * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $ + */ + #include + #include ++#include + #include ++#include + + #define FLASH_ENABLE_PORT 0x00C00001 + #define FLASH_ENABLE_BYTE 0x01 +@@ -58,18 +60,12 @@ + static struct map_info tsunami_flash_map = { + .name = "flash chip on the Tsunami TIG bus", + .size = MAX_TIG_FLASH_SIZE, ++ .phys = NO_XIP; + .buswidth = 1, + .read8 = tsunami_flash_read8, +- .read16 = 0, +- .read32 = 0, + .copy_from = tsunami_flash_copy_from, + .write8 = tsunami_flash_write8, +- .write16 = 0, +- .write32 = 0, + .copy_to = tsunami_flash_copy_to, +- .set_vpp = 0, +- .map_priv_1 = 0, +- + }; + + static struct mtd_info *tsunami_flash_mtd; +@@ -99,7 +95,7 @@ + tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map); + } + if (tsunami_flash_mtd) { +- tsunami_flash_mtd->module = THIS_MODULE; ++ tsunami_flash_mtd->owner = THIS_MODULE; + add_mtd_device(tsunami_flash_mtd); + return 0; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/uclinux.c linux/drivers/mtd/maps/uclinux.c +--- linux-mips-2.4.27/drivers/mtd/maps/uclinux.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/uclinux.c 2004-11-19 10:25:11.980187480 +0100 +@@ -5,7 +5,7 @@ + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * +- * $Id: uclinux.c,v 1.2 2002/08/07 00:43:45 gerg Exp $ ++ * $Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $ + */ + + /****************************************************************************/ +@@ -24,58 +24,11 @@ + + /****************************************************************************/ + +-__u8 uclinux_read8(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u8 *) (map->map_priv_1 + ofs))); +-} +- +-__u16 uclinux_read16(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u16 *) (map->map_priv_1 + ofs))); +-} +- +-__u32 uclinux_read32(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u32 *) (map->map_priv_1 + ofs))); +-} +- +-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (map->map_priv_1 + to), from, len); +-} + + /****************************************************************************/ + + struct map_info uclinux_ram_map = { +- name: "RAM", +- read8: uclinux_read8, +- read16: uclinux_read16, +- read32: uclinux_read32, +- copy_from: uclinux_copy_from, +- write8: uclinux_write8, +- write16: uclinux_write16, +- write32: uclinux_write32, +- copy_to: uclinux_copy_to, ++ .name = "RAM", + }; + + struct mtd_info *uclinux_ram_mtdinfo; +@@ -83,7 +36,7 @@ + /****************************************************************************/ + + struct mtd_partition uclinux_romfs[] = { +- { name: "ROMfs", offset: 0 } ++ { .name = "ROMfs" } + }; + + #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) +@@ -94,7 +47,7 @@ + size_t *retlen, u_char **mtdbuf) + { + struct map_info *map = (struct map_info *) mtd->priv; +- *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from)); ++ *mtdbuf = (u_char *) (map->virt + ((int) from)); + *retlen = len; + return(0); + } +@@ -108,29 +61,31 @@ + extern char _ebss; + + mapp = &uclinux_ram_map; +- mapp->map_priv_2 = (unsigned long) &_ebss; ++ mapp->phys = (unsigned long) &_ebss; + mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); + mapp->buswidth = 4; + + printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", + (int) mapp->map_priv_2, (int) mapp->size); + +- mapp->map_priv_1 = (unsigned long) +- ioremap_nocache(mapp->map_priv_2, mapp->size); ++ mapp->virt = (unsigned long) ++ ioremap_nocache(mapp->phys, mapp->size); + +- if (mapp->map_priv_1 == 0) { ++ if (mapp->virt == 0) { + printk("uclinux[mtd]: ioremap_nocache() failed\n"); + return(-EIO); + } + ++ simple_map_init(mapp); ++ + mtd = do_map_probe("map_ram", mapp); + if (!mtd) { + printk("uclinux[mtd]: failed to find a mapping?\n"); +- iounmap((void *) mapp->map_priv_1); ++ iounmap((void *) mapp->virt); + return(-ENXIO); + } + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->point = uclinux_point; + mtd->priv = mapp; + +@@ -155,8 +110,8 @@ + uclinux_ram_mtdinfo = NULL; + } + if (uclinux_ram_map.map_priv_1) { +- iounmap((void *) uclinux_ram_map.map_priv_1); +- uclinux_ram_map.map_priv_1 = 0; ++ iounmap((void *) uclinux_ram_map.virt); ++ uclinux_ram_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/vmax301.c linux/drivers/mtd/maps/vmax301.c +--- linux-mips-2.4.27/drivers/mtd/maps/vmax301.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/vmax301.c 2004-11-19 10:25:11.982187176 +0100 +@@ -1,4 +1,4 @@ +-// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $ ++// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $ + /* ###################################################################### + + Tempustech VMAX SBC301 MTD Driver. +@@ -24,6 +24,7 @@ + #include + + #include ++#include + + + #define WINDOW_START 0xd8000 +@@ -142,34 +143,36 @@ + + static struct map_info vmax_map[2] = { + { +- name: "VMAX301 Internal Flash", +- size: 3*2*1024*1024, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + WINDOW_LENGTH, +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Internal Flash", ++ .phys = NO_XIP, ++ .size = 3*2*1024*1024, ++ .buswidth = 1, ++ .read8 = vmax301_read8, ++ .read16 = vmax301_read16, ++ .read32 = vmax301_read32, ++ .copy_from = vmax301_copy_from, ++ .write8 = vmax301_write8, ++ .write16 = vmax301_write16, ++ .write32 = vmax301_write32, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + WINDOW_LENGTH, ++ .map_priv_2 = 0xFFFFFFFF + }, + { +- name: "VMAX301 Socket", +- size: 0, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH), +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Socket", ++ .phys = NO_XIP, ++ .size = 0, ++ .buswidth = 1, ++ .read8 = vmax301_read8, ++ .read16 = vmax301_read16, ++ .read32 = vmax301_read32, ++ .copy_from = vmax301_copy_from, ++ .write8 = vmax301_write8, ++ .write16 = vmax301_write16, ++ .write32 = vmax301_write32, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), ++ .map_priv_2 = 0xFFFFFFFF + } + }; + +@@ -206,8 +209,8 @@ + address of the first half, because it's used more + often. + */ +- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START; +- vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START); ++ vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; ++ vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); + + for (i=0; i<2; i++) { + vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); +@@ -218,7 +221,7 @@ + if (!vmax_mtd[i]) + vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); + if (vmax_mtd[i]) { +- vmax_mtd[i]->module = THIS_MODULE; ++ vmax_mtd[i]->owner = THIS_MODULE; + add_mtd_device(vmax_mtd[i]); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c linux/drivers/mtd/mtd_blkdevs-24.c +--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/mtd_blkdevs-24.c 2004-11-19 10:25:11.640239160 +0100 +@@ -0,0 +1,699 @@ ++/* ++ * $Id: mtd_blkdevs-24.c,v 1.15 2003/10/10 08:55:03 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux 2.4 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ devfs_handle_t devfs_dir_handle; ++ int blksizes[256]; ++ int sizes[256]; ++ struct hd_struct part_table[256]; ++ struct gendisk gd; ++ spinlock_t devs_lock; /* See comment in _request function */ ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++}; ++ ++static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr, ++ int devnum) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_dev *d; ++ ++ list_for_each(this, &tr->devs) { ++ d = list_entry(this, struct mtd_blktrans_dev, list); ++ ++ if (d->devnum == devnum) ++ return d; ++ } ++ return NULL; ++} ++ ++static inline struct mtd_blktrans_ops *get_tr(int major) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_ops *t; ++ ++ list_for_each(this, &blktrans_majors) { ++ t = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ if (t->major == major) ++ return t; ++ } ++ return NULL; ++} ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ int minor; ++ ++ minor = MINOR(req->rq_dev); ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) { ++ printk(KERN_WARNING "Access beyond end of device.\n"); ++ return 0; ++ } ++ block += tr->blkcore_priv->part_table[minor].start_sect; ++ ++ switch(req->cmd) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major); ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ snprintf(current->comm, sizeof(current->comm), "%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ ++#ifndef __rh_config_h__ /* HAVE_NPTL */ ++ spin_lock_irq(¤t->sigmask_lock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(current); ++ spin_unlock_irq(¤t->sigmask_lock); ++#else ++ spin_lock_irq(¤t->sighand->siglock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++#endif ++ daemonize(); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int devnum; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ spin_lock_irq(&io_request_lock); ++ ++ if (list_empty(&rq->queue_head)) { ++ ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(&io_request_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ continue; ++ } ++ ++ req = blkdev_entry_next_request(&rq->queue_head); ++ ++ devnum = MINOR(req->rq_dev) >> tr->part_bits; ++ ++ /* The ll_rw_blk code knows not to touch the request ++ at the head of the queue */ ++ spin_unlock_irq(&io_request_lock); ++ ++ /* FIXME: Where can we store the dev, on which ++ we already have a refcount anyway? We need to ++ lock against concurrent addition/removal of devices, ++ but if we use the mtd_table_mutex we deadlock when ++ grok_partitions is called from the registration ++ callbacks. */ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ dev = tr_get_dev(tr, devnum); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ BUG_ON(!dev); ++ ++ /* Ensure serialisation of requests */ ++ down(&dev->sem); ++ ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ if (!end_that_request_first(req, res, tr->name)) { ++ spin_lock_irq(&io_request_lock); ++ blkdev_dequeue_request(req); ++ end_that_request_last(req); ++ spin_unlock_irq(&io_request_lock); ++ } ++ } ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_ops *tr = NULL; ++ struct mtd_blktrans_dev *dev = NULL; ++ int major_nr = MAJOR(i->i_rdev); ++ int minor_nr = MINOR(i->i_rdev); ++ int devnum; ++ int ret = -ENODEV; ++ ++ if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE)) ++ return -EROFS; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(major_nr); ++ ++ if (!tr) ++ goto out; ++ ++ devnum = minor_nr >> tr->part_bits; ++ ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) ++ goto out; ++ ++ if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ if (!try_inc_mod_count(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_inc_mod_count(tr->owner)) ++ goto out_tr; ++ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ out_tr: ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ out: ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ int devnum; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(i->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(i->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev) ++{ ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int i; ++ int minor = MINOR(rdev); ++ ++ if (minor & ((1<part_bits)-1) || !tr->part_bits) { ++ /* BLKRRPART on a partition. Go away. */ ++ return -ENOTTY; ++ } ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ /* We are required to prevent simultaneous open() ourselves. ++ The core doesn't do that for us. Did I ever mention how ++ much the Linux block layer sucks? Sledgehammer approach... */ ++ down(&mtd_table_mutex); ++ ++ for (i=0; i < (1<part_bits); i++) { ++ invalidate_device(MKDEV(tr->major, minor+i), 1); ++ gd->part[minor + i].start_sect = 0; ++ gd->part[minor + i].nr_sects = 0; ++ } ++ ++ grok_partitions(gd, minor, 1 << tr->part_bits, ++ tr->blkcore_priv->sizes[minor]); ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int devnum; ++ ++ switch(cmd) { ++ case BLKGETSIZE: ++ case BLKGETSIZE64: ++ case BLKBSZSET: ++ case BLKBSZGET: ++ case BLKROSET: ++ case BLKROGET: ++ case BLKRASET: ++ case BLKRAGET: ++ case BLKPG: ++ case BLKELVGET: ++ case BLKELVSET: ++ return blk_ioctl(inode->i_rdev, cmd, arg); ++ } ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(inode->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(inode->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ up(&mtd_table_mutex); ++ ++ if (!dev) ++ return -ENODEV; ++ ++ switch(cmd) { ++ case BLKRRPART: ++ return mtd_blktrans_rrpart(inode->i_rdev, tr, dev); ++ ++ case BLKFLSBUF: ++ blk_ioctl(inode->i_rdev, cmd, arg); ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ if (ret) ++ return ret; ++ ++ g.start = gd->part[MINOR(inode->i_rdev)].start_sect; ++ if (copy_to_user((void *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ for (i = new->devnum << tr->part_bits; ++ i < (new->devnum+1) << tr->part_bits; ++ i++) { ++ set_device_ro(MKDEV(tr->major, i), new->readonly); ++ tr->blkcore_priv->blksizes[i] = new->blksize; ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ /* ++ dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices ++ dwmw2: any code which sets blk_size[][] should be ++ size >> 10 /+ 2.4 and its dumb units */ ++ ++ tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = ++ (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */ ++ ++ /* But this is still in device's sectors? $DEITY knows */ ++ tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size; ++ ++ if (tr->part_bits) { ++ grok_partitions(&tr->blkcore_priv->gd, new->devnum, ++ 1 << tr->part_bits, new->size); ++ } ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ char name[2]; ++ ++ name[0] = '0' + new->devnum; ++ name[1] = 0; ++ ++ new->blkcore_priv = ++ devfs_register(tr->blkcore_priv->devfs_dir_handle, ++ name, DEVFS_FL_DEFAULT, tr->major, ++ new->devnum, S_IFBLK|S_IRUGO|S_IWUGO, ++ &mtd_blktrans_ops, NULL); ++ } ++#endif ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ struct mtd_blktrans_ops *tr = old->tr; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ devfs_unregister(old->blkcore_priv); ++ old->blkcore_priv = NULL; ++ } else { ++ devfs_register_partitions(&tr->blkcore_priv->gd, ++ old->devnum << tr->part_bits, 1); ++ } ++#endif ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ list_del(&old->list); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ for (i = (old->devnum << tr->part_bits); ++ i < ((old->devnum+1) << tr->part_bits); i++) { ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ return 0; ++} ++ ++void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request); ++ (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr; ++ ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, ++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND); ++ if (ret < 0) { ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ tr->blkcore_priv->devfs_dir_handle = ++ devfs_mk_dir(NULL, tr->name, NULL); ++ ++ blksize_size[tr->major] = tr->blkcore_priv->blksizes; ++ blk_size[tr->major] = tr->blkcore_priv->sizes; ++ ++ tr->blkcore_priv->gd.major = tr->major; ++ tr->blkcore_priv->gd.major_name = tr->name; ++ tr->blkcore_priv->gd.minor_shift = tr->part_bits; ++ tr->blkcore_priv->gd.max_p = (1<part_bits) - 1; ++ tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table; ++ tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes; ++ tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits; ++ ++ spin_lock_init(&tr->blkcore_priv->devs_lock); ++ ++ add_gendisk(&tr->blkcore_priv->gd); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; itype != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ /* Remove each of its devices */ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ blksize_size[tr->major] = NULL; ++ blk_size[tr->major] = NULL; ++ ++ del_gendisk(&tr->blkcore_priv->gd); ++ ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ ++ devfs_unregister(tr->blkcore_priv->devfs_dir_handle); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c +--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/mtd_blkdevs.c 2004-11-19 10:25:11.642238856 +0100 +@@ -0,0 +1,479 @@ ++/* ++ * $Id: mtd_blkdevs.c,v 1.19 2003/11/07 09:52:46 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux 2.5 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++ struct request_queue *rq; ++ spinlock_t queue_lock; ++}; ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (!(req->flags & REQ_CMD)) ++ return 0; ++ ++ if (block + nsect > get_capacity(req->rq_disk)) ++ return 0; ++ ++ switch(rq_data_dir(req)) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = tr->blkcore_priv->rq; ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ daemonize("%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ spin_lock_irq(¤t->sighand->siglock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ req = elv_next_request(rq); ++ ++ if (!req) { ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ continue; ++ } ++ ++ dev = req->rq_disk->private_data; ++ tr = dev->tr; ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ down(&dev->sem); ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ end_request(req, res); ++ } ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++ ++int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = -ENODEV; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (!try_module_get(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_module_get(tr->owner)) ++ goto out_tr; ++ ++ /* FIXME: Locking. A hot pluggable device can go away ++ (del_mtd_device can be called for it) without its module ++ being unloaded. */ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ out_tr: ++ module_put(tr->owner); ++ } ++ out: ++ return ret; ++} ++ ++int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ module_put(tr->owner); ++ } ++ ++ return ret; ++} ++ ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; ++ struct mtd_blktrans_ops *tr = dev->tr; ++ ++ switch (cmd) { ++ case BLKFLSBUF: ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ ++ if (ret) ++ return ret; ++ ++ g.start = get_start_sect(inode->i_bdev); ++ if (copy_to_user((void *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ struct gendisk *gd; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ gd = alloc_disk(1 << tr->part_bits); ++ if (!gd) { ++ list_del(&new->list); ++ return -ENOMEM; ++ } ++ gd->major = tr->major; ++ gd->first_minor = (new->devnum) << tr->part_bits; ++ gd->fops = &mtd_blktrans_ops; ++ ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ snprintf(gd->devfs_name, sizeof(gd->devfs_name), ++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ ++ /* 2.5 has capacity in units of 512 bytes while still ++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ ++ set_capacity(gd, (new->size * new->blksize) >> 9); ++ ++ gd->private_data = new; ++ new->blkcore_priv = gd; ++ gd->queue = tr->blkcore_priv->rq; ++ ++ if (new->readonly) ++ set_disk_ro(gd, 1); ++ ++ add_disk(gd); ++ ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_del(&old->list); ++ ++ del_gendisk(old->blkcore_priv); ++ put_disk(old->blkcore_priv); ++ ++ return 0; ++} ++ ++void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = register_blkdev(tr->major, tr->name); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ spin_lock_init(&tr->blkcore_priv->queue_lock); ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); ++ if (!tr->blkcore_priv->rq) { ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return -ENOMEM; ++ } ++ ++ tr->blkcore_priv->rq->queuedata = tr; ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, ++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND); ++ if (ret < 0) { ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ devfs_mk_dir(tr->name); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; itype != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ devfs_remove(tr->name); ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock.c linux/drivers/mtd/mtdblock.c +--- linux-mips-2.4.27/drivers/mtd/mtdblock.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdblock.c 2004-11-19 10:25:11.643238704 +0100 +@@ -1,52 +1,25 @@ + /* + * Direct MTD block device access + * +- * $Id: mtdblock.c,v 1.51 2001/11/20 11:42:33 dwmw2 Exp $ ++ * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $ + * +- * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache ++ * (C) 2000-2003 Nicolas Pitre ++ * (C) 1999-2003 David Woodhouse + */ + + #include + #include + #include + #include ++#include ++#include + #include ++#include + #include +-#include +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include +-/* for old kernels... */ +-#ifndef QUEUE_EMPTY +-#define QUEUE_EMPTY (!CURRENT) +-#endif +-#if LINUX_VERSION_CODE < 0x20300 +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) +-#else +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) +-#endif +- +-#ifdef CONFIG_DEVFS_FS +-#include +-static void mtd_notify_add(struct mtd_info* mtd); +-static void mtd_notify_remove(struct mtd_info* mtd); +-static struct mtd_notifier notifier = { +- mtd_notify_add, +- mtd_notify_remove, +- NULL +-}; +-static devfs_handle_t devfs_dir_handle = NULL; +-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; +-#endif ++#include + + static struct mtdblk_dev { +- struct mtd_info *mtd; /* Locked */ ++ struct mtd_info *mtd; + int count; + struct semaphore cache_sem; + unsigned char *cache_data; +@@ -55,19 +28,6 @@ + enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; + } *mtdblks[MAX_MTD_DEVICES]; + +-static spinlock_t mtdblks_lock; +- +-static int mtd_sizes[MAX_MTD_DEVICES]; +-static int mtd_blksizes[MAX_MTD_DEVICES]; +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- + /* + * Cache stuff... + * +@@ -151,7 +111,7 @@ + return ret; + + /* +- * Here we could argably set the cache state to STATE_CLEAN. ++ * Here we could argubly set the cache state to STATE_CLEAN. + * However this could lead to inconsistency since we will not + * be notified if this content is altered on the flash by other + * means. Let's declare it empty and leave buffering tasks to +@@ -277,57 +237,47 @@ + return 0; + } + ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ return do_cached_read(mtdblk, block<<9, 512, buf); ++} + ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ if (unlikely(!mtdblk->cache_data)) { ++ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); ++ if (!mtdblk->cache_data) ++ return -EINTR; ++ /* -EINTR is not really correct, but it is the best match ++ * documented in man 2 write for all cases. We could also ++ * return -EAGAIN sometimes, but why bother? ++ */ ++ } ++ return do_cached_write(mtdblk, block<<9, 512, buf); ++} + +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_open(struct mtd_blktrans_dev *mbd) + { + struct mtdblk_dev *mtdblk; +- struct mtd_info *mtd; +- int dev; ++ struct mtd_info *mtd = mbd->mtd; ++ int dev = mbd->devnum; + + DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); + +- if (!inode) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- if (dev >= MAX_MTD_DEVICES) +- return -EINVAL; +- +- BLK_INC_USE_COUNT; +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -ENODEV; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; +- return -ENODEV; +- } +- +- spin_lock(&mtdblks_lock); +- +- /* If it's already open, no need to piss about. */ + if (mtdblks[dev]) { + mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtd); + return 0; + } + +- /* OK, it's not open. Try to find it */ +- +- /* First we have to drop the lock, because we have to +- to things which might sleep. +- */ +- spin_unlock(&mtdblks_lock); +- ++ /* OK, it's not open. Create cache info for it */ + mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); +- if (!mtdblk) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; ++ if (!mtdblk) + return -ENOMEM; +- } ++ + memset(mtdblk, 0, sizeof(*mtdblk)); + mtdblk->count = 1; + mtdblk->mtd = mtd; +@@ -337,336 +287,102 @@ + if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && + mtdblk->mtd->erasesize) { + mtdblk->cache_size = mtdblk->mtd->erasesize; +- mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); +- if (!mtdblk->cache_data) { +- put_mtd_device(mtdblk->mtd); +- kfree(mtdblk); +- BLK_DEC_USE_COUNT; +- return -ENOMEM; +- } +- } +- +- /* OK, we've created a new one. Add it to the list. */ +- +- spin_lock(&mtdblks_lock); +- +- if (mtdblks[dev]) { +- /* Another CPU made one at the same time as us. */ +- mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtdblk->mtd); +- vfree(mtdblk->cache_data); +- kfree(mtdblk); +- return 0; ++ mtdblk->cache_data = NULL; + } + + mtdblks[dev] = mtdblk; +- mtd_sizes[dev] = mtdblk->mtd->size/1024; +- if (mtdblk->mtd->erasesize) +- mtd_blksizes[dev] = mtdblk->mtd->erasesize; +- if (mtd_blksizes[dev] > PAGE_SIZE) +- mtd_blksizes[dev] = PAGE_SIZE; +- set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); +- +- spin_unlock(&mtdblks_lock); + + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_release(struct mtd_blktrans_dev *mbd) + { +- int dev; +- struct mtdblk_dev *mtdblk; +- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); ++ int dev = mbd->devnum; ++ struct mtdblk_dev *mtdblk = mtdblks[dev]; + +- if (inode == NULL) +- release_return(-ENODEV); +- +- dev = MINOR(inode->i_rdev); +- mtdblk = mtdblks[dev]; ++ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); + + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); + +- spin_lock(&mtdblks_lock); + if (!--mtdblk->count) { + /* It was the last usage. Free the device */ + mtdblks[dev] = NULL; +- spin_unlock(&mtdblks_lock); + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); +- put_mtd_device(mtdblk->mtd); + vfree(mtdblk->cache_data); + kfree(mtdblk); +- } else { +- spin_unlock(&mtdblks_lock); + } +- + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + +- BLK_DEC_USE_COUNT; +- release_return(0); +-} +- +- +-/* +- * This is a special request_fn because it is executed in a process context +- * to be able to sleep independently of the caller. The io_request_lock +- * is held upon entry and exit. +- * The head of our request queue is considered active so there is no need +- * to dequeue requests before we are done. +- */ +-static void handle_mtdblock_request(void) +-{ +- struct request *req; +- struct mtdblk_dev *mtdblk; +- unsigned int res; +- +- for (;;) { +- INIT_REQUEST; +- req = CURRENT; +- spin_unlock_irq(&io_request_lock); +- mtdblk = mtdblks[MINOR(req->rq_dev)]; +- res = 0; +- +- if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) +- panic("%s: minor out of bounds", __FUNCTION__); +- +- if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) +- goto end_req; +- +- // Handle the request +- switch (req->cmd) +- { +- int err; +- +- case READ: +- down(&mtdblk->cache_sem); +- err = do_cached_read (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- +- case WRITE: +- // Read only device +- if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) +- break; +- +- // Do the write +- down(&mtdblk->cache_sem); +- err = do_cached_write (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- } +- +-end_req: +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static volatile int leaving = 0; +-static DECLARE_MUTEX_LOCKED(thread_sem); +-static DECLARE_WAIT_QUEUE_HEAD(thr_wq); +- +-int mtdblock_thread(void *dummy) +-{ +- struct task_struct *tsk = current; +- DECLARE_WAITQUEUE(wait, tsk); +- +- /* we might get involved when memory gets low, so use PF_MEMALLOC */ +- tsk->flags |= PF_MEMALLOC; +- strcpy(tsk->comm, "mtdblockd"); +- spin_lock_irq(&tsk->sigmask_lock); +- sigfillset(&tsk->blocked); +- recalc_sigpending(tsk); +- spin_unlock_irq(&tsk->sigmask_lock); +- daemonize(); +- +- while (!leaving) { +- add_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- spin_lock_irq(&io_request_lock); +- if (QUEUE_EMPTY || QUEUE_PLUGGED) { +- spin_unlock_irq(&io_request_lock); +- schedule(); +- remove_wait_queue(&thr_wq, &wait); +- } else { +- remove_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_RUNNING); +- handle_mtdblock_request(); +- spin_unlock_irq(&io_request_lock); +- } +- } +- +- up(&thread_sem); + return 0; + } + +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-static void mtdblock_request(RQFUNC_ARG) ++static int mtdblock_flush(struct mtd_blktrans_dev *dev) + { +- /* Don't do anything, except wake the thread if necessary */ +- wake_up(&thr_wq); +-} ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) +-{ +- struct mtdblk_dev *mtdblk; +- +- mtdblk = mtdblks[MINOR(inode->i_rdev)]; +- +-#ifdef PARANOIA +- if (!mtdblk) +- BUG(); +-#endif +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtdblk->mtd->size, (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) +- return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); ++ + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); + return 0; +- +- default: +- return -EINVAL; +- } + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl +-}; +-#endif ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +-#ifdef CONFIG_DEVFS_FS +-/* Notification that a new device has been added. Create the devfs entry for +- * it. */ +- +-static void mtd_notify_add(struct mtd_info* mtd) +-{ +- char name[8]; +- +- if (!mtd || mtd->type == MTD_ABSENT) ++ if (!dev) + return; + +- sprintf(name, "%d", mtd->index); +- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, +- DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, +- S_IFBLK | S_IRUGO | S_IWUGO, +- &mtd_fops, NULL); +-} +- +-static void mtd_notify_remove(struct mtd_info* mtd) +-{ +- if (!mtd || mtd->type == MTD_ABSENT) +- return; ++ memset(dev, 0, sizeof(*dev)); + +- devfs_unregister(devfs_rw_handle[mtd->index]); +-} +-#endif ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ dev->readonly = 1; ++ ++ add_mtd_blktrans_dev(dev); ++} ++ ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) ++{ ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); ++} ++ ++struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .open = mtdblock_open, ++ .flush = mtdblock_flush, ++ .release = mtdblock_release, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, ++}; + + int __init init_mtdblock(void) + { +- int i; +- +- spin_lock_init(&mtdblks_lock); +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); +- register_mtd_user(¬ifier); +-#else +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +-#endif +- +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- mtd_blksizes[i] = BLOCK_SIZE; +- } +- init_waitqueue_head(&thr_wq); +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = mtd_blksizes; +- blk_size[MAJOR_NR] = mtd_sizes; +- +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); +- return 0; ++ return register_mtd_blktrans(&mtdblock_tr); + } + + static void __exit cleanup_mtdblock(void) + { +- leaving = 1; +- wake_up(&thr_wq); +- down(&thread_sem); +-#ifdef CONFIG_DEVFS_FS +- unregister_mtd_user(¬ifier); +- devfs_unregister(devfs_dir_handle); +- devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); +-#else +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +-#endif +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = NULL; ++ deregister_mtd_blktrans(&mtdblock_tr); + } + + module_init(init_mtdblock); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c linux/drivers/mtd/mtdblock_ro.c +--- linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdblock_ro.c 2004-11-19 10:25:11.645238400 +0100 +@@ -1,301 +1,87 @@ + /* +- * $Id: mtdblock_ro.c,v 1.12 2001/11/20 11:42:33 dwmw2 Exp $ ++ * $Id: mtdblock_ro.c,v 1.18 2003/06/23 12:00:08 dwmw2 Exp $ + * +- * Read-only version of the mtdblock device, without the +- * read/erase/modify/writeback stuff ++ * (C) 2003 David Woodhouse ++ * ++ * Simple read-only (writable only for RAM) mtdblock driver + */ + +-#ifdef MTDBLOCK_DEBUG +-#define DEBUGLVL debug +-#endif +- +- +-#include +-#include +- ++#include ++#include + #include +-#include +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include +- +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-#ifdef MTDBLOCK_DEBUG +-static int debug = MTDBLOCK_DEBUG; +-MODULE_PARM(debug, "i"); +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- +-static int mtd_sizes[MAX_MTD_DEVICES]; ++#include + +- +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- struct mtd_info *mtd = NULL; +- +- int dev; +- +- DEBUG(1,"mtdblock_open\n"); +- +- if (inode == 0) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -EINVAL; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- return -EINVAL; +- } +- +- BLK_INC_USE_COUNT; +- +- mtd_sizes[dev] = mtd->size>>9; +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + ++ if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int dev; +- struct mtd_info *mtd; +- +- DEBUG(1, "mtdblock_release\n"); +- +- if (inode == NULL) +- release_return(-ENODEV); +- +- dev = MINOR(inode->i_rdev); +- mtd = __get_mtd_device(NULL, dev); +- +- if (!mtd) { +- printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); +- BLK_DEC_USE_COUNT; +- release_return(-ENODEV); +- } +- +- if (mtd->sync) +- mtd->sync(mtd); +- +- put_mtd_device(mtd); +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + +- BLK_DEC_USE_COUNT; +- release_return(0); ++ if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; ++ return 0; + } + +- +-static void mtdblock_request(RQFUNC_ARG) ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- struct request *current_request; +- unsigned int res = 0; +- struct mtd_info *mtd; +- +- while (1) +- { +- /* Grab the Request and unlink it from the request list, INIT_REQUEST +- will execute a return if we are done. */ +- INIT_REQUEST; +- current_request = CURRENT; +- +- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) +- { +- printk("mtd: Unsupported device!\n"); +- end_request(0); +- continue; +- } +- +- // Grab our MTD structure +- +- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); +- if (!mtd) { +- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); +- end_request(0); +- } +- +- if (current_request->sector << 9 > mtd->size || +- (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size) +- { +- printk("mtd: Attempt to read past end of device!\n"); +- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, +- current_request->sector, current_request->current_nr_sectors); +- end_request(0); +- continue; +- } +- +- /* Remove the request we are handling from the request list so nobody messes +- with it */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- /* Now drop the lock that the ll_rw_blk functions grabbed for us +- and process the request. This is necessary due to the extreme time +- we spend processing it. */ +- spin_unlock_irq(&io_request_lock); +-#endif +- +- // Handle the request +- switch (current_request->cmd) +- { +- size_t retlen; ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +- case READ: +- if (MTD_READ(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; ++ if (!dev) ++ return; + +- case WRITE: ++ memset(dev, 0, sizeof(*dev)); + +- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, +- current_request->current_nr_sectors); +- */ ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; ++ if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) != ++ (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) ++ dev->readonly = 1; + +- // Read only device +- if ((mtd->flags & MTD_CAP_RAM) == 0) +- { +- res = 0; +- break; +- } +- +- // Do the write +- if (MTD_WRITE(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; +- +- // Shouldn't happen +- default: +- printk("mtd: unknown request\n"); +- break; +- } +- +- // Grab the lock and re-thread the item onto the linked list +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- spin_lock_irq(&io_request_lock); +-#endif +- end_request(res); +- } ++ add_mtd_blktrans_dev(dev); + } + +- +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) + { +- struct mtd_info *mtd; +- +- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); +- +- if (!mtd) return -EINVAL; +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtd->size, (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (mtd->sync) +- mtd->sync(mtd); +- return 0; +- +- default: +- return -ENOTTY; +- } ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = +-{ +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl ++struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, + }; +-#endif + +-int __init init_mtdblock(void) ++static int __init mtdblock_init(void) + { +- int i; +- +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- } +- +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = mtd_sizes; +- +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- return 0; ++ return register_mtd_blktrans(&mtdblock_tr); + } + +-static void __exit cleanup_mtdblock(void) ++static void __exit mtdblock_exit(void) + { +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +- blk_size[MAJOR_NR] = NULL; +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); ++ deregister_mtd_blktrans(&mtdblock_tr); + } + +-module_init(init_mtdblock); +-module_exit(cleanup_mtdblock); +- ++module_init(mtdblock_init); ++module_exit(mtdblock_exit); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Erwin Authried et al."); ++MODULE_AUTHOR("David Woodhouse "); + MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdchar.c linux/drivers/mtd/mtdchar.c +--- linux-mips-2.4.27/drivers/mtd/mtdchar.c 2004-08-14 20:38:51.000000000 +0200 ++++ linux/drivers/mtd/mtdchar.c 2004-11-19 10:25:11.647238096 +0100 +@@ -1,8 +1,7 @@ + /* +- * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $ ++ * $Id: mtdchar.c,v 1.56 2003/11/14 19:50:03 thayne Exp $ + * + * Character-device access to raw MTD devices. +- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c + * + */ + +@@ -10,7 +9,11 @@ + #include + #include + #include ++#include + #include ++#include ++#include ++#include + + #ifdef CONFIG_DEVFS_FS + #include +@@ -18,8 +21,8 @@ + static void mtd_notify_remove(struct mtd_info* mtd); + + static struct mtd_notifier notifier = { +- add: mtd_notify_add, +- remove: mtd_notify_remove, ++ .add = mtd_notify_add, ++ .remove = mtd_notify_remove, + }; + + static devfs_handle_t devfs_dir_handle; +@@ -60,7 +63,7 @@ + + static int mtd_open(struct inode *inode, struct file *file) + { +- int minor = minor(inode->i_rdev); ++ int minor = iminor(inode); + int devnum = minor >> 1; + struct mtd_info *mtd; + +@@ -125,15 +128,11 @@ + int ret=0; + int len; + char *kbuf; +- loff_t pos = *ppos; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); + +- if (pos < 0 || pos > mtd->size) +- return 0; +- +- if (count > mtd->size - pos) +- count = mtd->size - pos; ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; + + if (!count) + return 0; +@@ -150,9 +149,9 @@ + if (!kbuf) + return -ENOMEM; + +- ret = MTD_READ(mtd, pos, len, &retlen, kbuf); ++ ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + if (!ret) { +- pos += retlen; ++ *ppos += retlen; + if (copy_to_user(buf, kbuf, retlen)) { + kfree(kbuf); + return -EFAULT; +@@ -171,8 +170,6 @@ + kfree(kbuf); + } + +- *ppos = pos; +- + return total_retlen; + } /* mtd_read */ + +@@ -182,17 +179,16 @@ + char *kbuf; + size_t retlen; + size_t total_retlen=0; +- loff_t pos = *ppos; + int ret=0; + int len; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); + +- if (pos < 0 || pos >= mtd->size) ++ if (*ppos == mtd->size) + return -ENOSPC; + +- if (count > mtd->size - pos) +- count = mtd->size - pos; ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; + + if (!count) + return 0; +@@ -214,9 +210,9 @@ + return -EFAULT; + } + +- ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf); ++ ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + if (!ret) { +- pos += retlen; ++ *ppos += retlen; + total_retlen += retlen; + count -= retlen; + buf += retlen; +@@ -228,7 +224,6 @@ + + kfree(kbuf); + } +- *ppos = pos; + + return total_retlen; + } /* mtd_write */ +@@ -450,81 +445,13 @@ + break; + } + +- case MEMWRITEDATA: +- { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen; +- +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) +- return -EFAULT; +- +- if (buf.length > 0x4096) +- return -EINVAL; +- +- if (!mtd->write_ecc) +- ret = -EOPNOTSUPP; +- else +- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; +- +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) +- return -ENOMEM; +- +- if (copy_from_user(databuf, buf.ptr, buf.length)) { +- kfree(databuf); +- return -EFAULT; +- } +- +- ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; +- +- kfree(databuf); +- break; +- +- } +- +- case MEMREADDATA: ++ case MEMSETOOBSEL: + { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen = 0; +- +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo))) + return -EFAULT; +- +- if (buf.length > 0x4096) +- return -EINVAL; +- +- if (!mtd->read_ecc) +- ret = -EOPNOTSUPP; +- else +- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; +- +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) +- return -ENOMEM; +- +- ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; +- else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) +- ret = -EFAULT; +- +- kfree(databuf); + break; + } + +- + default: + DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); + ret = -ENOTTY; +@@ -534,13 +461,13 @@ + } /* memory_ioctl */ + + static struct file_operations mtd_fops = { +- owner: THIS_MODULE, +- llseek: mtd_lseek, /* lseek */ +- read: mtd_read, /* read */ +- write: mtd_write, /* write */ +- ioctl: mtd_ioctl, /* ioctl */ +- open: mtd_open, /* open */ +- release: mtd_close, /* release */ ++ .owner = THIS_MODULE, ++ .llseek = mtd_lseek, ++ .read = mtd_read, ++ .write = mtd_write, ++ .ioctl = mtd_ioctl, ++ .open = mtd_open, ++ .release = mtd_close, + }; + + +@@ -580,26 +507,18 @@ + + static int __init init_mtdchar(void) + { +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) ++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) + { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return -EAGAIN; + } + ++#ifdef CONFIG_DEVFS_FS + devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); + + register_mtd_user(¬ifier); +-#else +- if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_CHAR_MAJOR); +- return -EAGAIN; +- } + #endif +- + return 0; + } + +@@ -608,10 +527,8 @@ + #ifdef CONFIG_DEVFS_FS + unregister_mtd_user(¬ifier); + devfs_unregister(devfs_dir_handle); +- devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +-#else +- unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + #endif ++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + } + + module_init(init_mtdchar); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdconcat.c linux/drivers/mtd/mtdconcat.c +--- linux-mips-2.4.27/drivers/mtd/mtdconcat.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdconcat.c 2004-11-19 10:25:11.649237792 +0100 +@@ -3,9 +3,11 @@ + * + * (C) 2002 Robert Kaiser + * ++ * NAND support by Christian Gan ++ * + * This code is GPL + * +- * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ ++ * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $ + */ + + #include +@@ -35,21 +37,20 @@ + #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +- + /* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ + #define CONCAT(x) ((struct mtd_concat *)(x)) + +- + /* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf) ++static int ++concat_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -57,43 +58,43 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (from >= subdev->size) +- { ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ + size = 0; + from -= subdev->size; ++ continue; + } +- else +- { + if (from + len > subdev->size) ++ /* First part goes into this subdev */ + size = subdev->size - from; + else ++ /* Entire transaction goes into this subdev */ + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } +- } + return err; + } + +-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf) ++static int ++concat_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -104,18 +105,15 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (to >= subdev->size) +- { ++ if (to >= subdev->size) { + size = 0; + to -= subdev->size; ++ continue; + } +- else +- { + if (to + len > subdev->size) + size = subdev->size - to; + else +@@ -126,25 +124,232 @@ + else + err = subdev->write(subdev, to, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } ++ return err; ++} ++ ++static int ++concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_ecc) ++ err = subdev->read_ecc(subdev, from, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) { ++ eccbuf += subdev->oobsize; ++ /* in nand.c at least, eccbufs are ++ tagged with 2 (int)eccstatus'; we ++ must account for these */ ++ eccbuf += 2 * (sizeof (int)); ++ } ++ from = 0; + } + return err; + } + +-static void concat_erase_callback (struct erase_info *instr) ++static int ++concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) + { +- wake_up((wait_queue_head_t *)instr->priv); ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_ecc) ++ err = subdev->write_ecc(subdev, to, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) ++ eccbuf += subdev->oobsize; ++ to = 0; ++ } ++ return err; ++} ++ ++static int ++concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_oob) ++ err = subdev->read_oob(subdev, from, size, ++ &retsize, buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ from = 0; ++ } ++ return err; ++} ++ ++static int ++concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_oob) ++ err = subdev->write_oob(subdev, to, size, &retsize, ++ buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ to = 0; ++ } ++ return err; ++} ++ ++static void concat_erase_callback(struct erase_info *instr) ++{ ++ wake_up((wait_queue_head_t *) instr->priv); + } + + static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +@@ -160,18 +365,18 @@ + + erase->mtd = mtd; + erase->callback = concat_erase_callback; +- erase->priv = (unsigned long)&waitq; ++ erase->priv = (unsigned long) &waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); +- if (!err) +- { ++ if (!err) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); +- if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) ++ if (erase->state != MTD_ERASE_DONE ++ && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); +@@ -181,7 +386,7 @@ + return err; + } + +-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ++static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) + { + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; +@@ -192,10 +397,10 @@ + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + +- if(instr->addr > concat->mtd.size) ++ if (instr->addr > concat->mtd.size) + return -EINVAL; + +- if(instr->len + instr->addr > concat->mtd.size) ++ if (instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* +@@ -204,23 +409,22 @@ + * region info rather than looking at each particular sub-device + * in turn. + */ +- if (!concat->mtd.numeraseregions) +- { /* the easy case: device has uniform erase block size */ +- if(instr->addr & (concat->mtd.erasesize - 1)) ++ if (!concat->mtd.numeraseregions) { ++ /* the easy case: device has uniform erase block size */ ++ if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; +- if(instr->len & (concat->mtd.erasesize - 1)) ++ if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; +- } +- else +- { /* device has variable erase size */ +- struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; ++ } else { ++ /* device has variable erase size */ ++ struct mtd_erase_region_info *erase_regions = ++ concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ +- for(i = 0; i < concat->mtd.numeraseregions && +- instr->addr >= erase_regions[i].offset; i++) +- ; ++ for (i = 0; i < concat->mtd.numeraseregions && ++ instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* +@@ -228,25 +432,26 @@ + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ +- if (instr->addr & (erase_regions[i].erasesize-1)) ++ if (instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ +- for(; i < concat->mtd.numeraseregions && +- (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) +- ; ++ for (; i < concat->mtd.numeraseregions && ++ (instr->addr + instr->len) >= erase_regions[i].offset; ++ ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ +- if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) ++ if ((instr->addr + instr->len) & (erase_regions[i].erasesize - ++ 1)) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ +- erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); ++ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; +@@ -258,39 +463,40 @@ + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; +- if(subdev->size <= erase->addr) ++ if (subdev->size <= erase->addr) + erase->addr -= subdev->size; + else + break; + } +- if(i >= concat->num_subdev) /* must never happen since size */ +- BUG(); /* limit has been verified above */ ++ ++ /* must never happen since size limit has been verified above */ ++ if (i >= concat->num_subdev) ++ BUG(); + + /* now do the erase: */ + err = 0; +- for(;length > 0; i++) /* loop for all subevices affected by this request */ +- { ++ for (; length > 0; i++) { ++ /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ +- if(erase->addr + length > subdev->size) ++ if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + +- if (!(subdev->flags & MTD_WRITEABLE)) +- { ++ if (!(subdev->flags & MTD_WRITEABLE)) { + err = -EROFS; + break; + } + length -= erase->len; +- if ((err = concat_dev_erase(subdev, erase))) +- { +- if(err == -EINVAL) /* sanity check: must never happen since */ +- BUG(); /* block alignment has been checked above */ ++ if ((err = concat_dev_erase(subdev, erase))) { ++ /* sanity check: should never happen since ++ * block alignment has been checked above */ ++ if (err == -EINVAL) ++ BUG(); + break; + } + /* +@@ -313,7 +519,7 @@ + return 0; + } + +-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; +@@ -321,18 +527,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -340,21 +543,21 @@ + + err = subdev->lock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; +@@ -362,18 +565,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -381,17 +581,17 @@ + + err = subdev->unlock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +@@ -400,8 +600,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +@@ -412,10 +611,9 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; +- if((rc = subdev->suspend(subdev)) < 0) ++ if ((rc = subdev->suspend(subdev)) < 0) + return rc; + } + return rc; +@@ -426,8 +624,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->resume(subdev); + } +@@ -439,11 +636,10 @@ + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +-struct mtd_info *mtd_concat_create( +- struct mtd_info *subdev[], /* subdevices to concatenate */ ++struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ +- char *name) /* name for the new device */ +-{ ++ char *name) ++{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; +@@ -451,21 +647,21 @@ + int num_erase_region; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); +- for(i = 0; i < num_devs; i++) ++ for (i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); +- concat = kmalloc (size, GFP_KERNEL); +- if(!concat) +- { +- printk ("memory allocation error while creating concatenated device \"%s\"\n", ++ concat = kmalloc(size, GFP_KERNEL); ++ if (!concat) { ++ printk ++ ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + memset(concat, 0, size); +- concat->subdev = (struct mtd_info **)(concat + 1); ++ concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for +@@ -479,39 +675,53 @@ + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; ++ if (subdev[0]->read_ecc) ++ concat->mtd.read_ecc = concat_read_ecc; ++ if (subdev[0]->write_ecc) ++ concat->mtd.write_ecc = concat_write_ecc; ++ if (subdev[0]->read_oob) ++ concat->mtd.read_oob = concat_read_oob; ++ if (subdev[0]->write_oob) ++ concat->mtd.write_oob = concat_write_oob; + + concat->subdev[0] = subdev[0]; + +- for(i = 1; i < num_devs; i++) +- { +- if(concat->mtd.type != subdev[i]->type) +- { ++ for (i = 1; i < num_devs; i++) { ++ if (concat->mtd.type != subdev[i]->type) { + kfree(concat); +- printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device type on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } +- if(concat->mtd.flags != subdev[i]->flags) +- { /* +- * Expect all flags except MTD_WRITEABLE to be equal on +- * all subdevices. ++ if (concat->mtd.flags != subdev[i]->flags) { ++ /* ++ * Expect all flags except MTD_WRITEABLE to be ++ * equal on all subdevices. + */ +- if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) +- { ++ if ((concat->mtd.flags ^ subdev[i]-> ++ flags) & ~MTD_WRITEABLE) { + kfree(concat); +- printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device flags on \"%s\"\n", ++ subdev[i]->name); + return NULL; +- } +- else /* if writeable attribute differs, make super device writeable */ +- concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; ++ } else ++ /* if writeable attribute differs, ++ make super device writeable */ ++ concat->mtd.flags |= ++ subdev[i]->flags & MTD_WRITEABLE; + } + concat->mtd.size += subdev[i]->size; +- if(concat->mtd.oobblock != subdev[i]->oobblock || ++ if (concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || +- concat->mtd.eccsize != subdev[i]->eccsize) +- { ++ concat->mtd.eccsize != subdev[i]->eccsize || ++ !concat->mtd.read_ecc != !subdev[i]->read_ecc || ++ !concat->mtd.write_ecc != !subdev[i]->write_ecc || ++ !concat->mtd.read_oob != !subdev[i]->read_oob || ++ !concat->mtd.write_oob != !subdev[i]->write_oob) { + kfree(concat); +- printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible OOB or ECC data on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; +@@ -535,7 +745,6 @@ + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; + +- + /* + * Combine the erase block size info of the subdevices: + * +@@ -544,44 +753,44 @@ + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* if it differs from the last subdevice's erase size, count it */ ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; +- if(curr_erasesize > max_erasesize) ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j].erasesize != ++ curr_erasesize) { + ++num_erase_region; +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; +- if(curr_erasesize > max_erasesize) ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + +- if(num_erase_region == 1) +- { /* ++ if (num_erase_region == 1) { ++ /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; +- } +- else +- { /* ++ } else { ++ /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ +@@ -590,12 +799,13 @@ + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; +- concat->mtd.eraseregions = erase_region_p = kmalloc ( +- num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); +- if(!erase_region_p) +- { ++ concat->mtd.eraseregions = erase_region_p = ++ kmalloc(num_erase_region * ++ sizeof (struct mtd_erase_region_info), GFP_KERNEL); ++ if (!erase_region_p) { + kfree(concat); +- printk ("memory allocation error while creating erase region list" ++ printk ++ ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } +@@ -606,41 +816,48 @@ + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j]. ++ erasesize != curr_erasesize) { + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - ++ begin) / curr_erasesize; + begin = position; + +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; + ++erase_region_p; + } +- position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; ++ position += ++ subdev[i]->eraseregions[j]. ++ numblocks * curr_erasesize; + } + } + } +@@ -660,16 +877,14 @@ + void mtd_concat_destroy(struct mtd_info *mtd) + { + struct mtd_concat *concat = CONCAT(mtd); +- if(concat->mtd.numeraseregions) ++ if (concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); + } + +- + EXPORT_SYMBOL(mtd_concat_create); + EXPORT_SYMBOL(mtd_concat_destroy); + +- + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Robert Kaiser "); + MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c +--- linux-mips-2.4.27/drivers/mtd/mtdcore.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdcore.c 2004-11-19 10:25:11.650237640 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $ ++ * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $ + * + * Core registration and callback routines for MTD + * drivers and users. +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #ifdef CONFIG_PROC_FS + #include +@@ -24,9 +25,15 @@ + + #include + +-static DECLARE_MUTEX(mtd_table_mutex); +-static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +-static struct mtd_notifier *mtd_notifiers = NULL; ++/* These are exported solely for the purpose of mtd_blkdevs.c. You ++ should not use them for _anything_ else */ ++DECLARE_MUTEX(mtd_table_mutex); ++struct mtd_info *mtd_table[MAX_MTD_DEVICES]; ++ ++EXPORT_SYMBOL_GPL(mtd_table_mutex); ++EXPORT_SYMBOL_GPL(mtd_table); ++ ++static LIST_HEAD(mtd_notifiers); + + /** + * add_mtd_device - register an MTD device +@@ -44,21 +51,28 @@ + + down(&mtd_table_mutex); + +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (!mtd_table[i]) +- { +- struct mtd_notifier *not=mtd_notifiers; ++ for (i=0; i < MAX_MTD_DEVICES; i++) ++ if (!mtd_table[i]) { ++ struct list_head *this; + + mtd_table[i] = mtd; + mtd->index = i; ++ mtd->usecount = 0; ++ + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); +- while (not) +- { +- (*(not->add))(mtd); +- not = not->next; ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->add(mtd); + } ++ + up(&mtd_table_mutex); +- MOD_INC_USE_COUNT; ++ /* We _know_ we aren't being removed, because ++ our caller is still holding us here. So none ++ of this try_ nonsense, and no bitching about it ++ either. :) */ ++ __module_get(THIS_MODULE); + return 0; + } + +@@ -78,29 +92,34 @@ + + int del_mtd_device (struct mtd_info *mtd) + { +- struct mtd_notifier *not=mtd_notifiers; +- int i; ++ int ret; + + down(&mtd_table_mutex); + +- for (i=0; i < MAX_MTD_DEVICES; i++) +- { +- if (mtd_table[i] == mtd) +- { +- while (not) +- { +- (*(not->remove))(mtd); +- not = not->next; +- } +- mtd_table[i] = NULL; +- up (&mtd_table_mutex); +- MOD_DEC_USE_COUNT; +- return 0; ++ if (mtd_table[mtd->index] != mtd) { ++ ret = -ENODEV; ++ } else if (mtd->usecount) { ++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", ++ mtd->index, mtd->name, mtd->usecount); ++ ret = -EBUSY; ++ } else { ++ struct list_head *this; ++ ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->remove(mtd); + } ++ ++ mtd_table[mtd->index] = NULL; ++ ++ module_put(THIS_MODULE); ++ ret = 0; + } + + up(&mtd_table_mutex); +- return 1; ++ return ret; + } + + /** +@@ -118,10 +137,9 @@ + + down(&mtd_table_mutex); + +- new->next = mtd_notifiers; +- mtd_notifiers = new; ++ list_add(&new->list, &mtd_notifiers); + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) +@@ -142,34 +160,24 @@ + + int unregister_mtd_user (struct mtd_notifier *old) + { +- struct mtd_notifier **prev = &mtd_notifiers; +- struct mtd_notifier *cur; + int i; + + down(&mtd_table_mutex); + +- while ((cur = *prev)) { +- if (cur == old) { +- *prev = cur->next; +- +- MOD_DEC_USE_COUNT; ++ module_put(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + old->remove(mtd_table[i]); + ++ list_del(&old->list); + up(&mtd_table_mutex); + return 0; +- } +- prev = &cur->next; +- } +- up(&mtd_table_mutex); +- return 1; + } + + + /** +- * __get_mtd_device - obtain a validated handle for an MTD device ++ * get_mtd_device - obtain a validated handle for an MTD device + * @mtd: last known address of the required MTD device + * @num: internal device number of the required MTD device + * +@@ -177,11 +185,10 @@ + * table, if any. Given an address and num == -1, search the device table + * for a device with that address and return if it's still present. Given + * both, return the num'th driver only if its address matches. Return NULL +- * if not. get_mtd_device() increases the use count, but +- * __get_mtd_device() doesn't - you should generally use get_mtd_device(). ++ * if not. + */ + +-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) ++struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) + { + struct mtd_info *ret = NULL; + int i; +@@ -198,10 +205,27 @@ + ret = NULL; + } + ++ if (ret && !try_module_get(ret->owner)) ++ ret = NULL; ++ ++ if (ret) ++ ret->usecount++; ++ + up(&mtd_table_mutex); + return ret; + } + ++void put_mtd_device(struct mtd_info *mtd) ++{ ++ int c; ++ ++ down(&mtd_table_mutex); ++ c = --mtd->usecount; ++ up(&mtd_table_mutex); ++ BUG_ON(c < 0); ++ ++ module_put(mtd->owner); ++} + + /* default_mtd_writev - default mtd writev method for MTD devices that + * dont implement their own +@@ -265,7 +289,8 @@ + + EXPORT_SYMBOL(add_mtd_device); + EXPORT_SYMBOL(del_mtd_device); +-EXPORT_SYMBOL(__get_mtd_device); ++EXPORT_SYMBOL(get_mtd_device); ++EXPORT_SYMBOL(put_mtd_device); + EXPORT_SYMBOL(register_mtd_user); + EXPORT_SYMBOL(unregister_mtd_user); + EXPORT_SYMBOL(default_mtd_writev); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c +--- linux-mips-2.4.27/drivers/mtd/mtdpart.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdpart.c 2004-11-19 10:25:11.652237336 +0100 +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id: mtdpart.c,v 1.32 2002/10/21 13:40:05 jocke Exp $ ++ * $Id: mtdpart.c,v 1.42 2003/07/09 11:19:01 dwmw2 Exp $ + * + * 02-21-2002 Thomas Gleixner + * added support for read_oob, write_oob +@@ -16,10 +16,11 @@ + #include + #include + #include +- ++#include ++#include + #include + #include +- ++#include + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -54,8 +55,12 @@ + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; ++ if (part->master->read_ecc == NULL) + return part->master->read (part->master, from + part->offset, + len, retlen, buf); ++ else ++ return part->master->read_ecc (part->master, from + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); + } + + static int part_point (struct mtd_info *mtd, loff_t from, size_t len, +@@ -78,9 +83,11 @@ + + + static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) ++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) +@@ -113,7 +120,7 @@ + size_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); +- return part->master->read_user_prot_reg (part->master, from, ++ return part->master->read_fact_prot_reg (part->master, from, + len, retlen, buf); + } + +@@ -127,17 +134,24 @@ + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; ++ if (part->master->write_ecc == NULL) + return part->master->write (part->master, to + part->offset, + len, retlen, buf); ++ else ++ return part->master->write_ecc (part->master, to + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); ++ + } + + static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) +@@ -174,25 +188,37 @@ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (part->master->writev_ecc == NULL) + return part->master->writev (part->master, vecs, count, + to + part->offset, retlen); ++ else ++ return part->master->writev_ecc (part->master, vecs, count, ++ to + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + + static int part_readv (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen) + { + struct mtd_part *part = PART(mtd); ++ if (part->master->readv_ecc == NULL) + return part->master->readv (part->master, vecs, count, + from + part->offset, retlen); ++ else ++ return part->master->readv_ecc (part->master, vecs, count, ++ from + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + + static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->writev_ecc (part->master, vecs, count, + to + part->offset, retlen, + eccbuf, oobsel); +@@ -200,9 +226,11 @@ + + static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->readv_ecc (part->master, vecs, count, + from + part->offset, retlen, + eccbuf, oobsel); +@@ -288,7 +316,7 @@ + */ + + int add_mtd_partitions(struct mtd_info *master, +- struct mtd_partition *parts, ++ const struct mtd_partition *parts, + int nbparts) + { + struct mtd_part *slave; +@@ -321,7 +349,7 @@ + + slave->mtd.name = parts[i].name; + slave->mtd.bank_size = master->bank_size; +- slave->mtd.module = master->module; ++ slave->mtd.owner = master->owner; + + slave->mtd.read = part_read; + slave->mtd.write = part_write; +@@ -452,6 +480,75 @@ + EXPORT_SYMBOL(add_mtd_partitions); + EXPORT_SYMBOL(del_mtd_partitions); + ++static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED; ++static LIST_HEAD(part_parsers); ++ ++struct mtd_part_parser *get_partition_parser(const char *name) ++{ ++ struct list_head *this; ++ void *ret = NULL; ++ spin_lock(&part_parser_lock); ++ ++ list_for_each(this, &part_parsers) { ++ struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); ++ ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++int register_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_add(&p->list, &part_parsers); ++ spin_unlock(&part_parser_lock); ++ ++ return 0; ++} ++ ++int deregister_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_del(&p->list); ++ spin_unlock(&part_parser_lock); ++ return 0; ++} ++ ++int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin) ++{ ++ struct mtd_part_parser *parser; ++ int ret = 0; ++ ++ for ( ; ret <= 0 && *types; types++) { ++ parser = get_partition_parser(*types); ++#ifdef CONFIG_KMOD ++ if (!parser && !request_module("%s", *types)) ++ parser = get_partition_parser(*types); ++#endif ++ if (!parser) { ++ printk(KERN_NOTICE "%s partition parsing not available\n", ++ *types); ++ continue; ++ } ++ ret = (*parser->parse_fn)(master, pparts, origin); ++ if (ret > 0) { ++ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ } ++ put_partition_parser(parser); ++ } ++ return ret; ++} ++ ++EXPORT_SYMBOL_GPL(parse_mtd_partitions); ++EXPORT_SYMBOL_GPL(register_mtd_parser); ++EXPORT_SYMBOL_GPL(deregister_mtd_parser); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Nicolas Pitre "); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Config.in linux/drivers/mtd/nand/Config.in +--- linux-mips-2.4.27/drivers/mtd/nand/Config.in 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/Config.in 2004-11-19 10:25:11.987186416 +0100 +@@ -1,6 +1,6 @@ + # drivers/mtd/nand/Config.in + +-# $Id: Config.in,v 1.11 2002/12/01 13:23:05 gleixner Exp $ ++# $Id: Config.in,v 1.14 2003/11/04 22:59:11 ahennessy Exp $ + + mainmenu_option next_comment + +@@ -11,26 +11,27 @@ + bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE + fi + +-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then +- dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND ++if [ "$CONFIG_ARM" = "y" ]; then ++ dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T ++ dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP ++ dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12 ++ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312 + fi + +-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then +- dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND +-fi +- +-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then +- dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND +-fi +- +-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then ++if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then + define_bool CONFIG_MTD_NAND_IDS y ++else ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then ++ define_bool CONFIG_MTD_NAND_IDS m ++ fi + fi + +-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then +-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then +- define_bool CONFIG_MTD_NAND_IDS m ++if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then ++ dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND + fi ++ ++if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then ++ dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND + fi + + endmenu +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Makefile linux/drivers/mtd/nand/Makefile +--- linux-mips-2.4.27/drivers/mtd/nand/Makefile 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/Makefile 2004-11-19 10:25:11.989186112 +0100 +@@ -1,16 +1,20 @@ + # + # linux/drivers/nand/Makefile + # +-# $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $ ++# $Id: Makefile.common,v 1.4 2003/11/04 22:59:11 ahennessy Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := nandlink.o +- + export-objs := nand.o nand_ecc.o nand_ids.o ++endif + + obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o + obj-$(CONFIG_MTD_NAND_SPIA) += spia.o ++obj-$(CONFIG_MTD_NAND_TOTO) += toto.o + obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o + obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o ++obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o ++obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o + obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c linux/drivers/mtd/nand/autcpu12.c +--- linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/autcpu12.c 2004-11-19 10:25:11.990185960 +0100 +@@ -4,9 +4,9 @@ + * Copyright (c) 2002 Thomas Gleixner + * + * Derived from drivers/mtd/spia.c +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * +- * $Id: autcpu12.c,v 1.6 2002/11/11 15:47:56 gleixner Exp $ ++ * $Id: autcpu12.c,v 1.13 2003/07/11 15:12:29 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -25,10 +25,10 @@ + * added page_cache + * + * 10-06-2002 TG 128K card support added +- * + */ + + #include ++#include + #include + #include + #include +@@ -70,6 +70,7 @@ + /* + * Define partitions for flash devices + */ ++extern struct nand_oobinfo jffs2_oobinfo; + + static struct mtd_partition partition_info16k[] = { + { name: "AUTCPU12 flash partition 1", +@@ -95,7 +96,7 @@ + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, +- size: 48 * SZ_1M}, ++ size: 48 * SZ_1M }, + }; + + static struct mtd_partition partition_info128k[] = { +@@ -104,7 +105,7 @@ + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, +- size: 112 * SZ_1M}, ++ size: 112 * SZ_1M }, + }; + + #define NUM_PARTITIONS16K 2 +@@ -114,7 +115,7 @@ + /* + * hardware specific access to control-lines + */ +-void autcpu12_hwcontrol(int cmd) ++static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) + { + + switch(cmd){ +@@ -133,7 +134,7 @@ + /* + * read device ready pin + */ +-int autcpu12_device_ready(void) ++int autcpu12_device_ready(struct mtd_info *mtd) + { + + return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; +@@ -184,7 +185,7 @@ + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ +- if (nand_scan (autcpu12_mtd)) { ++ if (nand_scan (autcpu12_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } +@@ -197,15 +198,6 @@ + goto out_ior; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for AUTCPU12.\n"); +- err = -ENOMEM; +- goto out_buf; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + switch(autcpu12_mtd->size){ + case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; +@@ -215,13 +207,11 @@ + default: { + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; +- goto out_cac; ++ goto out_buf; + } + } + goto out; + +-out_cac: +- kfree (this->data_cache); + out_buf: + kfree (this->data_buf); + out_ior: +@@ -250,7 +240,6 @@ + + /* Free internal data buffers */ + kfree (this->data_buf); +- kfree (this->data_cache); + + /* unmap physical adress */ + iounmap((void *)autcpu12_fio_base); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c linux/drivers/mtd/nand/diskonchip.c +--- linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/diskonchip.c 2004-11-19 10:25:11.992185656 +0100 +@@ -0,0 +1,534 @@ ++/* ++ * drivers/mtd/nand/diskonchip.c ++ * ++ * (C) 2003 Red Hat, Inc. ++ * ++ * Author: David Woodhouse ++ * ++ * Interface to generic NAND code for M-Systems DiskOnChip devices ++ * ++ * $Id: diskonchip.c,v 1.8 2003/11/05 16:52:34 dwmw2 Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++struct doc_priv { ++ unsigned long virtadr; ++ unsigned long physadr; ++ u_char ChipID; ++ u_char CDSNControl; ++ int chips_per_floor; /* The number of chips detected on each floor */ ++ int curfloor; ++ int curchip; ++}; ++ ++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) ++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); ++static void doc200x_select_chip(struct mtd_info *mtd, int chip); ++ ++static int debug=0; ++MODULE_PARM(debug, "i"); ++ ++static int try_dword=1; ++MODULE_PARM(try_dword, "i"); ++ ++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) ++{ ++ volatile char dummy; ++ int i; ++ ++ for (i = 0; i < cycles; i++) { ++ if (DoC_is_Millennium(doc)) ++ dummy = ReadDOC(doc->virtadr, NOP); ++ else ++ dummy = ReadDOC(doc->virtadr, DOCStatus); ++ } ++ ++} ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(struct doc_priv *doc) ++{ ++ unsigned long docptr = doc->virtadr; ++ unsigned long timeo = jiffies + (HZ * 10); ++ ++ if(debug) printk("_DoC_WaitReady...\n"); ++ /* Out-of-line routine to wait for chip response */ ++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ ++ return 0; ++} ++ ++static inline int DoC_WaitReady(struct doc_priv *doc) ++{ ++ unsigned long docptr = doc->virtadr; ++ int ret = 0; ++ ++ DoC_Delay(doc, 4); ++ ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ ++ DoC_Delay(doc, 2); ++ if(debug) printk("DoC_WaitReady OK\n"); ++ return ret; ++} ++ ++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ if(debug)printk("write_byte %02x\n", datum); ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, 2k_CDSN_IO); ++} ++ ++static u_char doc2000_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ u_char ret = ReadDOC(docptr, 2k_CDSN_IO); ++ if (debug) printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++static void doc2000_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ if (debug)printk("writebuf of %d bytes: ", len); ++ for (i=0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) printk("\n"); ++} ++ ++static void doc2000_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("readbuf of %d bytes: ", len); ++ ++ for (i=0; i < len; i++) { ++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++} ++ ++static void doc2000_readbuf_dword(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ if (debug) printk("readbuf_dword of %d bytes: ", len); ++ ++ if (unlikely((((unsigned long)buf)|len) & 3)) { ++ for (i=0; i < len; i++) { ++ *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++ } else { ++ for (i=0; i < len; i+=4) { ++ *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); ++ } ++ } ++} ++ ++static int doc2000_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) ++ return i; ++ return 0; ++} ++ ++static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ uint16_t ret; ++ ++ doc200x_select_chip(mtd, nr); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ this->write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ ret = this->read_byte(mtd) << 8; ++ ret |= this->read_byte(mtd); ++ ++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { ++ /* First chip probe. See if we get same results by 32-bit access */ ++ union { ++ uint32_t dword; ++ uint8_t byte[4]; ++ } ident; ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ doc2000_write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ doc2000_write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ ident.dword = readl(docptr + DoC_2k_CDSN_IO); ++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { ++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); ++ this->read_buf = &doc2000_readbuf_dword; ++ } ++ } ++ ++ return ret; ++} ++ ++static void doc2000_count_chips(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ uint16_t mfrid; ++ int i; ++ ++ /* Max 4 chips per floor on DiskOnChip 2000 */ ++ doc->chips_per_floor = 4; ++ ++ /* Find out what the first chip is */ ++ mfrid = doc200x_ident_chip(mtd, 0); ++ ++ /* Find how many chips in each floor. */ ++ for (i = 1; i < 4; i++) { ++ if (doc200x_ident_chip(mtd, i) != mfrid) ++ break; ++ } ++ doc->chips_per_floor = i; ++} ++ ++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) ++{ ++ struct doc_priv *doc = (void *)this->priv; ++ ++ int status; ++ ++ DoC_WaitReady(doc); ++ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); ++ DoC_WaitReady(doc); ++ status = (int)this->read_byte(mtd); ++ ++ return status; ++} ++ ++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, Mil_CDSN_IO); ++ WriteDOC(datum, docptr, WritePipeTerm); ++} ++ ++static u_char doc2001_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ /* 11.4.5 -- delay twice to allow extended length cycle */ ++ DoC_Delay(doc, 2); ++ ReadDOC(docptr, ReadPipeInit); ++ return ReadDOC(docptr, Mil_CDSN_IO); ++} ++ ++static void doc2001_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ /* Terminate write pipeline */ ++ WriteDOC(0x00, docptr, WritePipeTerm); ++} ++ ++static void doc2001_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO); ++ ++ /* Terminate read pipeline */ ++ buf[i] = ReadDOC(docptr, LastDataRead); ++} ++static int doc2001_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ++ ReadDOC(docptr, LastDataRead); ++ return i; ++ } ++ if (buf[i] != ReadDOC(docptr, LastDataRead)) ++ return i; ++ return 0; ++} ++ ++static void doc200x_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int floor = 0; ++ ++ /* 11.4.4 -- deassert CE before changing chip */ ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); ++ ++ if(debug)printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) ++ return; ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ WriteDOC(floor, docptr, FloorSelect); ++ WriteDOC(chip, docptr, CDSNDeviceSelect); ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ switch(cmd) { ++ case NAND_CTL_SETNCE: ++ doc->CDSNControl |= CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ doc->CDSNControl &= ~CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_SETCLE: ++ doc->CDSNControl |= CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ doc->CDSNControl &= ~CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ doc->CDSNControl |= CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ doc->CDSNControl &= ~CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_SETWP: ++ doc->CDSNControl |= CDSN_CTRL_WP; ++ break; ++ case NAND_CTL_CLRWP: ++ doc->CDSNControl &= ~CDSN_CTRL_WP; ++ break; ++ } ++ if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ /* 11.4.3 -- 4 NOPs after CSDNControl write */ ++ DoC_Delay(doc, 4); ++} ++ ++static int doc200x_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if(debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ /* 11.4.2 -- Must NOP twice if it's ready */ ++ DoC_Delay(doc, 2); ++ if (debug)printk("was ready\n"); ++ return 1; ++} ++ ++static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block) ++{ ++ /* FIXME: Look it up in the BBT */ ++ return 0; ++} ++ ++struct doc_priv mydoc = { ++ .physadr = 0xd4000, ++ .curfloor = -1, ++ .curchip = -1, ++}; ++ ++u_char mydatabuf[528]; ++ ++struct nand_chip mynand = { ++ .priv = (void *)&mydoc, ++ .select_chip = doc200x_select_chip, ++ .hwcontrol = doc200x_hwcontrol, ++ .dev_ready = doc200x_dev_ready, ++ .waitfunc = doc200x_wait, ++ .block_bad = doc200x_block_bad, ++ .eccmode = NAND_ECC_SOFT, ++ .data_buf = mydatabuf, ++}; ++ ++struct mtd_info mymtd = { ++ .priv = (void *)&mynand, ++ .owner = THIS_MODULE, ++}; ++ ++int __init init_nanddoc(void) ++{ ++ mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN); ++ int nrchips = 1; ++ char *name; ++ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ mydoc.virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ mydoc.virtadr, DOCControl); ++ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ mydoc.virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ mydoc.virtadr, DOCControl); ++ ++ mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID); ++ ++ switch(mydoc.ChipID) { ++ case DOC_ChipID_DocMil: ++ mynand.write_byte = doc2001_write_byte; ++ mynand.read_byte = doc2001_read_byte; ++ mynand.write_buf = doc2001_writebuf; ++ mynand.read_buf = doc2001_readbuf; ++ mynand.verify_buf = doc2001_verifybuf; ++ ++ ReadDOC(mydoc.virtadr, ChipID); ++ ReadDOC(mydoc.virtadr, ChipID); ++ if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) { ++ /* It's not a Millennium; it's one of the newer ++ DiskOnChip 2000 units with a similar ASIC. ++ Treat it like a Millennium, except that it ++ can have multiple chips. */ ++ doc2000_count_chips(&mymtd); ++ nrchips = 4 * mydoc.chips_per_floor; ++ name = "DiskOnChip 2000 (INFTL Model)"; ++ } else { ++ /* Bog-standard Millennium */ ++ mydoc.chips_per_floor = 1; ++ nrchips = 1; ++ name = "DiskOnChip Millennium"; ++ } ++ break; ++ ++ case DOC_ChipID_Doc2k: ++ mynand.write_byte = doc2000_write_byte; ++ mynand.read_byte = doc2000_read_byte; ++ mynand.write_buf = doc2000_writebuf; ++ mynand.read_buf = doc2000_readbuf; ++ mynand.verify_buf = doc2000_verifybuf; ++ ++ doc2000_count_chips(&mymtd); ++ nrchips = 4 * mydoc.chips_per_floor; ++ name = "DiskOnChip 2000 (NFTL Model)"; ++ mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO; ++ ++ break; ++ ++ default: ++ return -EIO; ++ } ++ if (nand_scan(&mymtd, nrchips)) { ++ iounmap((void *)mydoc.virtadr); ++ return -EIO; ++ } ++ mymtd.name = name; ++ add_mtd_device(&mymtd); ++ ++ return 0; ++} ++ ++void __exit cleanup_nanddoc(void) ++{ ++ del_mtd_device(&mymtd); ++ iounmap((void *)mydoc.virtadr); ++} ++ ++module_init(init_nanddoc); ++module_exit(cleanup_nanddoc); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/edb7312.c linux/drivers/mtd/nand/edb7312.c +--- linux-mips-2.4.27/drivers/mtd/nand/edb7312.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/edb7312.c 2004-11-19 10:25:11.997184896 +0100 +@@ -6,7 +6,7 @@ + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id: edb7312.c,v 1.3 2002/06/06 12:58:16 mag Exp $ ++ * $Id: edb7312.c,v 1.7 2003/07/11 15:12:29 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -20,6 +20,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -77,16 +78,13 @@ + }; + #define NUM_PARTITIONS 1 + +-extern int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); + #endif + + + /* + * hardware specific access to control-lines + */ +-static void ep7312_hwcontrol(int cmd) ++static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) + { + switch(cmd) { + +@@ -116,10 +114,13 @@ + /* + * read device ready pin + */ +-static int ep7312_device_ready(void) ++static int ep7312_device_ready(struct mtd_info *mtd) + { + return 1; + } ++#ifdef CONFIG_MTD_PARTITIONS ++const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif + + /* + * Main initialization routine +@@ -174,7 +175,7 @@ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (ep7312_mtd)) { ++ if (nand_scan (ep7312_mtd, 1)) { + iounmap((void *)ep7312_fio_base); + kfree (ep7312_mtd); + return -ENXIO; +@@ -189,27 +190,16 @@ + return -ENOMEM; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk("Unable to allocate NAND data cache for EDB7312.\n"); +- kfree (this->data_buf); +- iounmap((void *)ep7312_fio_base); +- kfree (ep7312_mtd); +- return -ENOMEM; +- } +- this->cache_page = -1; +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, +- "edb7312-nand"); ++#ifdef CONFIG_PARTITIONS ++ ep7312_mtd->name = "edb7312-nand"; ++ mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, ++ &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; + #endif +- if (mtd_parts_nb == 0) +- { ++ if (mtd_parts_nb == 0) { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; +@@ -236,7 +226,6 @@ + + /* Free internal data buffer */ + kfree (this->data_buf); +- kfree (this->data_cache); + + /* Free the MTD device structure */ + kfree (ep7312_mtd); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand.c linux/drivers/mtd/nand/nand.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand.c 2004-11-19 10:25:12.010182920 +0100 +@@ -8,7 +8,7 @@ + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 10-29-2001 Thomas Gleixner (tglx@linutronix.de) +@@ -112,10 +112,27 @@ + * for mtd->read_ecc / mtd->write_ecc + * some minor cleanups + * +- * 12-05-2000 tglx: Dave Ellis (DGE@sixnetio) provided the fix for ++ * 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for + * WRITE_VERIFY long time ago. Thanks for remembering me. + * +- * $Id: nand.c,v 1.36 2002/12/05 20:59:11 gleixner Exp $ ++ * 02-14-2003 tglx: Reject non page aligned writes ++ * Fixed ecc select in nand_write_page to match semantics. ++ * ++ * 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector ++ * ++ * 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to ++ + a structure, which will be supplied by a filesystem driver ++ * If NULL is given, then the defaults (none or defaults ++ * supplied by ioctl (MEMSETOOBSEL) are used. ++ * For partitions the partition defaults are used (mtdpart.c) ++ * ++ * 06-04-2003 tglx: fix compile errors and fix write verify problem for ++ * some chips, which need either a delay between the readback ++ * and the next write command or have the CE removed. The ++ * CE disable/enable is much faster than a 20us delay and ++ * it should work on all available chips. ++ * ++ * $Id: nand.c,v 1.60 2003/10/23 08:28:43 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -130,102 +147,151 @@ + #include + #include + #include ++#include + #include + #include + + /* +- * Macros for low-level register control +- */ +-#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); +- +-#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE); +- +-/* +- * out of band configuration for different filesystems +- */ +-static int oobconfigs[][6] = { +- { 0,0,0,0,0,0}, +- +- { NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2, +- NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 }, +- +- { NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2, +- NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 } +-}; +- +-/* + * NAND low-level MTD interface functions + */ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); ++ + static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); + static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel); ++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); + static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); + static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); + static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, +- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel); ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); + static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); + static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t * retlen); + static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, +- unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, int oobsel); ++ unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); + static void nand_sync (struct mtd_info *mtd); +-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col, +- int last, u_char *oob_buf, int oobsel); ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel); ++ ++static u_char nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return readb(this->IO_ADDR_R); ++} ++ ++static void nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writeb(byte, this->IO_ADDR_W); ++} ++ ++static void nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ switch(chip) { ++ case -1: ++ this->hwcontrol(mtd, NAND_CTL_CLRNCE); ++ break; ++ case 0: ++ this->hwcontrol(mtd, NAND_CTL_SETNCE); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* Appropriate chip should already be selected */ ++static int nand_block_bad(struct mtd_info *mtd, unsigned long page) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); ++ if (this->read_byte(mtd) != 0xff) ++ return 1; ++ ++ return 0; ++} ++ + /* + * Send command to NAND device + */ + static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) + { + register struct nand_chip *this = mtd->priv; +- register unsigned long NAND_IO_ADDR = this->IO_ADDR_W; + + /* Begin command latch cycle */ +- this->hwcontrol (NAND_CTL_SETCLE); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ +- if (command != NAND_CMD_SEQIN) +- writeb (command, NAND_IO_ADDR); +- else { +- if (mtd->oobblock == 256 && column >= 256) { +- column -= 256; +- writeb (NAND_CMD_READOOB, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } else if (mtd->oobblock == 512 && column >= 256) { +- if (column < 512) { +- column -= 256; +- writeb (NAND_CMD_READ1, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } else { +- column -= 512; +- writeb (NAND_CMD_READOOB, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; + } else { +- writeb (NAND_CMD_READ0, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ column -= 256; ++ readcmd = NAND_CMD_READ1; + } ++ this->write_byte(mtd, readcmd); + } ++ this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ +- this->hwcontrol (NAND_CTL_CLRCLE); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { +- this->hwcontrol (NAND_CTL_SETALE); ++ this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) +- writeb (column, NAND_IO_ADDR); ++ this->write_byte(mtd, column); + if (page_addr != -1) { +- writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); +- writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) +- writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ +- this->hwcontrol (NAND_CTL_CLRALE); ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* +@@ -244,10 +310,11 @@ + case NAND_CMD_RESET: + if (this->dev_ready) + break; +- this->hwcontrol (NAND_CTL_SETCLE); +- writeb (NAND_CMD_STATUS, NAND_IO_ADDR); +- this->hwcontrol (NAND_CTL_CLRCLE); +- while ( !(readb (this->IO_ADDR_R) & 0x40)); ++ udelay(this->chip_delay); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ +@@ -263,7 +330,7 @@ + } + + /* wait until command is processed */ +- while (!this->dev_ready()); ++ while (!this->dev_ready(mtd)); + } + + /* +@@ -288,17 +355,17 @@ + spin_unlock_bh (&this->chip_lock); + return; + } +- ++#if 0 /* This was broken. And of dubious utility */ + if (this->state == FL_ERASING) { + if (new_state != FL_ERASING) { + this->state = new_state; + spin_unlock_bh (&this->chip_lock); +- nand_select (); /* select in any case */ ++ this->select_chip(mtd, 0); /* select in any case */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + return; + } + } +- ++#endif + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); +@@ -334,17 +401,17 @@ + return 0; + } + if (this->dev_ready) { +- if (this->dev_ready ()) ++ if (this->dev_ready(mtd)) + break; + } +- if (readb (this->IO_ADDR_R) & 0x40) ++ if (this->read_byte(mtd) & 0x40) + break; + + spin_unlock_bh (&this->chip_lock); + yield (); + spin_lock_bh (&this->chip_lock); + } +- status = (int) readb (this->IO_ADDR_R); ++ status = (int) this->read_byte(mtd); + spin_unlock_bh (&this->chip_lock); + + return status; +@@ -352,14 +419,15 @@ + + /* + * Nand_page_program function is used for write and writev ! ++ * This function will always program a full page of data ++ * If you call it with a non page aligned buffer, you're lost :) + */ +-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, +- int page, int col, int last, u_char *oob_buf, int oobsel) ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel) + { + int i, status; + u_char ecc_code[6], *oob_data; +- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE; +- int *oob_config = oobconfigs[oobsel]; ++ int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ int *oob_config = oobsel->eccpos; + + /* pad oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf) { +@@ -369,66 +437,42 @@ + } else + oob_data = oob_buf; + +- /* software ecc 3 Bytes ECC / 256 Byte Data ? */ +- if (eccmode == NAND_ECC_SOFT) { +- /* Read back previous written data, if col > 0 */ +- if (col) { +- this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); +- for (i = 0; i < col; i++) +- this->data_poi[i] = readb (this->IO_ADDR_R); +- } +- if ((col < this->eccsize) && (last >= this->eccsize)) { +- this->calculate_ecc (&this->data_poi[0], &(ecc_code[0])); +- for (i = 0; i < 3; i++) +- oob_data[oob_config[i]] = ecc_code[i]; +- } +- /* Calculate and write the second ECC if we have enough data */ +- if ((mtd->oobblock == 512) && (last == 512)) { +- this->calculate_ecc (&this->data_poi[256], &(ecc_code[3])); +- for (i = 3; i < 6; i++) +- oob_data[oob_config[i]] = ecc_code[i]; +- } +- } else { +- /* For hardware ECC skip ECC, if we have no full page write */ +- if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock)) +- eccmode = NAND_ECC_NONE; +- } +- +- /* Prepad for partial page programming !!! */ +- for (i = 0; i < col; i++) +- this->data_poi[i] = 0xff; +- +- /* Postpad for partial page programming !!! oob is already padded */ +- for (i = last; i < mtd->oobblock; i++) +- this->data_poi[i] = 0xff; +- + /* Send command to begin auto page programming */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data, take care of eccmode */ +- switch (this->eccmode) { ++ switch (eccmode) { + /* No ecc and software ecc 3/256, write all */ + case NAND_ECC_NONE: ++ printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ break; + case NAND_ECC_SOFT: +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); ++ this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0])); ++ for (i = 0; i < 3; i++) ++ oob_data[oob_config[i]] = ecc_code[i]; ++ /* Calculate and write the second ECC for 512 Byte page size */ ++ if (mtd->oobblock == 512) { ++ this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3])); ++ for (i = 3; i < 6; i++) ++ oob_data[oob_config[i]] = ecc_code[i]; ++ } ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + + /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */ + case NAND_ECC_HW3_256: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */ +- for (i = 0; i < mtd->eccsize; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write */ ++ this->write_buf(mtd, this->data_poi, mtd->eccsize); + +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 3; i++) + oob_data[oob_config[i]] = ecc_code[i]; + + if (mtd->oobblock == 512) { +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ +- for (i = mtd->eccsize; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[3])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ ++ this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[3])); + for (i = 3; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + } +@@ -436,20 +480,18 @@ + + /* Hardware ecc 3 byte / 512 byte data, write full page */ + case NAND_ECC_HW3_512: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 3; i++) + oob_data[oob_config[i]] = ecc_code[i]; + break; + + /* Hardware ecc 6 byte / 512 byte data, write full page */ + case NAND_ECC_HW6_512: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + break; +@@ -460,8 +502,7 @@ + } + + /* Write out OOB data */ +- for (i = 0; i < mtd->oobsize; i++) +- writeb ( oob_data[i] , this->IO_ADDR_W); ++ this->write_buf(mtd, oob_data, mtd->oobsize); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); +@@ -490,25 +531,21 @@ + */ + + /* Send command to read back the page */ +- this->cmdfunc (mtd, NAND_CMD_READ0, col, page); ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + /* Loop through and verify the data */ +- for (i = col; i < last; i++) { +- if (this->data_poi[i] != readb (this->IO_ADDR_R)) { ++ if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } +- } + + /* check, if we have a fs-supplied oob-buffer */ + if (oob_buf) { +- for (i = 0; i < mtd->oobsize; i++) { +- if (oob_data[i] != readb (this->IO_ADDR_R)) { ++ if (this->verify_buf(mtd, oob_data, mtd->oobsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } +- } + } else { +- if (eccmode != NAND_ECC_NONE && !col && last == mtd->oobblock) { ++ if (eccmode != NAND_ECC_NONE) { + int ecc_bytes = 0; + + switch (this->eccmode) { +@@ -518,8 +555,7 @@ + case NAND_ECC_HW6_512: ecc_bytes = 6; break; + } + +- for (i = 0; i < mtd->oobsize; i++) +- oob_data[i] = readb (this->IO_ADDR_R); ++ this->read_buf(mtd, oob_data, mtd->oobsize); + + for (i = 0; i < ecc_bytes; i++) { + if (oob_data[oob_config[i]] != ecc_code[i]) { +@@ -531,6 +567,13 @@ + } + } + } ++ /* ++ * Terminate the read command. This is faster than sending a reset command or ++ * applying a 20us delay before issuing the next programm sequence. ++ * This is not a problem for all chips, but I have found a bunch of them. ++ */ ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, 0); + #endif + return 0; + } +@@ -540,7 +583,7 @@ + */ + static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) + { +- return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, 0)); ++ return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + } + + +@@ -548,7 +591,7 @@ + * NAND read with ECC + */ + static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, u_char * buf, u_char * oob_buf, int oobsel) ++ size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) + { + int j, col, page, end, ecc; + int erase_state = 0; +@@ -557,9 +600,15 @@ + u_char *data_poi, *oob_data = oob_buf; + u_char ecc_calc[6]; + u_char ecc_code[6]; +- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE; ++ int eccmode; ++ int *oob_config; ++ ++ // use chip default if zero ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + +- int *oob_config = oobconfigs[oobsel]; ++ eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ oob_config = oobsel->eccpos; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + +@@ -574,7 +623,7 @@ + nand_get_chip (this, mtd ,FL_READING, &erase_state); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* First we calculate the starting page */ + page = from >> this->page_shift; +@@ -596,7 +645,7 @@ + if (!this->dev_ready) + udelay (this->chip_delay); + else +- while (!this->dev_ready()); ++ while (!this->dev_ready(mtd)); + } + + /* +@@ -616,39 +665,40 @@ + + j = 0; + switch (eccmode) { +- case NAND_ECC_NONE: /* No ECC, Read in a page */ +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); ++ case NAND_ECC_NONE: { /* No ECC, Read in a page */ ++ static unsigned long lastwhinge = 0; ++ if ((lastwhinge / HZ) != (jiffies / HZ)) { ++ printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); ++ lastwhinge = jiffies; ++ } ++ this->read_buf(mtd, data_poi, end); + break; ++ } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); ++ this->read_buf(mtd, data_poi, end); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); + if (mtd->oobblock == 512) +- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); ++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); + break; + + case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < ecc) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, data_poi, ecc); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ + + if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, &data_poi[ecc], end-ecc); ++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */ + } + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, data_poi, end); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ + break; + + default: +@@ -658,7 +708,7 @@ + + /* read oobdata */ + for (j = 0; j < mtd->oobsize; j++) +- oob_data[oob + j] = readb (this->IO_ADDR_R); ++ oob_data[oob + j] = this->read_byte(mtd); + + /* Skip ECC, if not active */ + if (eccmode == NAND_ECC_NONE) +@@ -669,7 +719,7 @@ + ecc_code[j] = oob_data[oob + oob_config[j]]; + + /* correct data, if neccecary */ +- ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]); ++ ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]); + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + oob += mtd->oobsize; +@@ -682,7 +732,7 @@ + } + + if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) { +- ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]); ++ ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]); + if (oob_buf) { + *((int *)&oob_data[oob]) = ecc_status; + oob += sizeof(int); +@@ -705,7 +755,7 @@ + } + + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -753,7 +803,7 @@ + nand_get_chip (this, mtd , FL_READING, &erase_state); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page); +@@ -761,13 +811,20 @@ + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ +- for (i = 0; i < len; i++) { +- buf[i] = readb (this->IO_ADDR_R); +- if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1)) ++ i = 0; ++ while (i < len) { ++ int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1); ++ if (!thislen) ++ thislen = mtd->oobsize; ++ thislen = min_t(int, thislen, len); ++ this->read_buf(mtd, &buf[i], thislen); ++ i += thislen; ++ col += thislen; ++ /* Delay between pages */ + udelay (this->chip_delay); + } + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -780,45 +837,54 @@ + return 0; + } + ++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 ++ + /* + * Use NAND write ECC + */ + static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + { +- return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 0)); ++ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); + } + /* + * NAND write with ECC + */ + static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, +- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel) ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) + { +- int i, page, col, cnt, ret = 0, oob = 0, written = 0; ++ int page, ret = 0, oob = 0, written = 0; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { +- DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); ++ return -EINVAL; ++ } ++ ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + ++ // if oobsel is NULL, use chip defaults ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + +- /* Get the starting column */ +- col = to & (mtd->oobblock - 1); +- + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); + ret = -EIO; + goto out; +@@ -826,42 +892,27 @@ + + /* Loop until all data is written */ + while (written < len) { +- /* +- * Check, if we have a full page write, then we can +- * use the given buffer, else we have to copy +- */ +- if (!col && (len - written) >= mtd->oobblock) { ++ int cnt = mtd->oobblock; + this->data_poi = (u_char*) &buf[written]; +- cnt = mtd->oobblock; +- } else { +- cnt = 0; +- for (i = col; i < len && i < mtd->oobblock; i++) { +- this->data_buf[i] = buf[written + i]; +- cnt++; +- } +- this->data_poi = this->data_buf; +- } +- /* We use the same function for write and writev !) */ ++ /* We use the same function for write and writev */ + if (eccbuf) { +- ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel); ++ ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel); + oob += mtd->oobsize; + } else +- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); ++ ret = nand_write_page (mtd, this, page, NULL, oobsel); + + if (ret) + goto out; + + /* Update written bytes count */ + written += cnt; +- /* Next write is aligned */ +- col = 0; + /* Increment page address */ + page++; + } + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -873,13 +924,21 @@ + return ret; + } + ++static u_char ffchars[] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ++}; ++ + /* + * NAND write out-of-band + */ + static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + { +- int i, column, page, status, ret = 0; ++ int column, page, status, ret = 0; + struct nand_chip *this = mtd->priv; ++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE ++ int i; ++#endif + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + +@@ -902,27 +961,31 @@ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); ++ ++ /* Reset the chip. Some chips (like the Toshiba TC5832DC found ++ in one of my DiskOnChip 2000 test units) will clear the whole ++ data page too if we don't do this. I have no clue why, but ++ I seem to have 'fixed' it in the doc2000 driver in ++ August 1999. dwmw2. */ ++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } +- + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page); ++ + /* prepad 0xff for partial programming */ +- for (i = 0; i < column; i++) +- writeb (0xff, this->IO_ADDR_W); ++ this->write_buf(mtd, ffchars, column); + /* write data */ +- for (i = 0; i < len; i++) +- writeb (buf[i], this->IO_ADDR_W); ++ this->write_buf(mtd, buf, len); + /* postpad 0xff for partial programming */ +- for (i = len + column; i < mtd->oobsize; i++) +- writeb (0xff, this->IO_ADDR_W); ++ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); + + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); +@@ -944,7 +1007,7 @@ + + /* Loop through and verify the data */ + for (i = 0; i < len; i++) { +- if (buf[i] != readb (this->IO_ADDR_R)) { ++ if (buf[i] != this->read_byte(mtd)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; +@@ -954,7 +1017,7 @@ + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -976,9 +1039,9 @@ + } + + static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, +- loff_t to, size_t * retlen, u_char *eccbuf, int oobsel) ++ loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) + { +- int i, page, col, cnt, len, total_len, ret = 0, written = 0; ++ int i, page, len, total_len, ret = 0, written = 0; + struct nand_chip *this = mtd->priv; + + /* Calculate total length of data */ +@@ -995,39 +1058,42 @@ + return -EINVAL; + } + ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(total_len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); ++ return -EINVAL; ++ } ++ ++ // if oobsel is NULL, use chip defaults ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + +- /* Get the starting column */ +- col = to & (mtd->oobblock - 1); +- + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } + + /* Loop until all iovecs' data has been written */ +- cnt = col; + len = 0; +- + while (count) { + /* +- * Check, if we write from offset 0 and if the tuple +- * gives us not enough data for a full page write. Then we +- * can use the iov direct, else we have to copy into +- * data_buf. ++ * Check, if the tuple gives us not enough data for a ++ * full page write. Then we can use the iov direct, ++ * else we have to copy into data_buf. + */ +- if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) { +- cnt = mtd->oobblock; ++ if ((vecs->iov_len - len) >= mtd->oobblock) { + this->data_poi = (u_char *) vecs->iov_base; + this->data_poi += len; + len += mtd->oobblock; +@@ -1042,6 +1108,7 @@ + * Read data out of each tuple until we have a full page + * to write or we've read all the tuples. + */ ++ int cnt = 0; + while ((cnt < mtd->oobblock) && count) { + if (vecs->iov_base != NULL && vecs->iov_len) { + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; +@@ -1057,15 +1124,12 @@ + } + + /* We use the same function for write and writev !) */ +- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); ++ ret = nand_write_page (mtd, this, page, NULL, oobsel); + if (ret) + goto out; + + /* Update written bytes count */ +- written += (cnt - col); +- +- /* Reset written byte counter and column */ +- col = cnt = 0; ++ written += mtd->oobblock;; + + /* Increment page address */ + page++; +@@ -1073,7 +1137,7 @@ + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -1125,11 +1189,11 @@ + pages_per_block = mtd->erasesize / mtd->oobblock; + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; +@@ -1142,8 +1206,7 @@ + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ +- this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); +- if (readb (this->IO_ADDR_R) != 0xff) { ++ if (this->block_bad(mtd, page)) { + printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; +@@ -1179,7 +1242,7 @@ + if (this->state == FL_ERASING || this->state == FL_READY) { + /* Select the NAND device again, if we were interrupted */ + this->state = FL_ERASING; +- nand_select (); ++ this->select_chip(mtd, 0); + continue; + } else { + set_current_state (TASK_UNINTERRUPTIBLE); +@@ -1194,7 +1257,7 @@ + + erase_exit: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + spin_unlock_bh (&this->chip_lock); + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; +@@ -1205,6 +1268,7 @@ + /* The device is ready */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; ++ wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return more or less happy */ +@@ -1259,7 +1323,7 @@ + /* + * Scan for the NAND device + */ +-int nand_scan (struct mtd_info *mtd) ++int nand_scan (struct mtd_info *mtd, int maxchips) + { + int i, nand_maf_id, nand_dev_id; + struct nand_chip *this = mtd->priv; +@@ -1276,23 +1340,38 @@ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + ++ if (!this->block_bad) ++ this->block_bad = nand_block_bad; ++ if (!this->select_chip) ++ this->select_chip = nand_select_chip; ++ if (!this->write_byte) ++ this->write_byte = nand_write_byte; ++ if (!this->read_byte) ++ this->read_byte = nand_read_byte; ++ if (!this->write_buf) ++ this->write_buf = nand_write_buf; ++ if (!this->read_buf) ++ this->read_buf = nand_read_buf; ++ if (!this->verify_buf) ++ this->verify_buf = nand_verify_buf; ++ + /* Select the device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ +- nand_maf_id = readb (this->IO_ADDR_R); +- nand_dev_id = readb (this->IO_ADDR_R); ++ nand_maf_id = this->read_byte(mtd); ++ nand_dev_id = this->read_byte(mtd); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) { + mtd->name = nand_flash_ids[i].name; + mtd->erasesize = nand_flash_ids[i].erasesize; +- mtd->size = (1 << nand_flash_ids[i].chipshift); + mtd->eccsize = 256; ++ this->chipshift = nand_flash_ids[i].chipshift; + if (nand_flash_ids[i].page256) { + mtd->oobblock = 256; + mtd->oobsize = 8; +@@ -1307,13 +1386,34 @@ + if (nand_manuf_ids[i].id == nand_maf_id) + break; + } +- printk (KERN_INFO "NAND device: Manufacture ID:" ++ printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[i].name , mtd->name); + break; + } + } + ++ if (!mtd->name) { ++ printk (KERN_WARNING "No NAND device found!!!\n"); ++ return 1; ++ } ++ ++ for (i=1; i < maxchips; i++) { ++ this->select_chip(mtd, i); ++ ++ /* Send the command for reading device ID */ ++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); ++ ++ /* Read manufacturer and device IDs */ ++ if (nand_maf_id != this->read_byte(mtd) || ++ nand_dev_id != this->read_byte(mtd)) ++ break; ++ } ++ if (i > 1) ++ printk(KERN_INFO "%d NAND chips detected\n", i); ++ ++ mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */; ++ + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize +@@ -1324,6 +1424,7 @@ + switch (this->eccmode) { + + case NAND_ECC_HW3_512: ++ case NAND_ECC_HW6_512: + if (mtd->oobblock == 256) { + printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); + this->eccmode = NAND_ECC_SOFT; +@@ -1340,6 +1441,7 @@ + BUG(); + + case NAND_ECC_NONE: ++ printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); + this->eccmode = NAND_ECC_NONE; + break; + +@@ -1359,18 +1461,11 @@ + spin_lock_init (&this->chip_lock); + + /* De-select the device */ +- nand_deselect (); +- +- /* Print warning message for no device */ +- if (!mtd->size) { +- printk (KERN_WARNING "No NAND device found!!!\n"); +- return 1; +- } ++ this->select_chip(mtd, -1); + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; +- mtd->module = THIS_MODULE; + mtd->ecctype = MTD_ECC_SW; + mtd->erase = nand_erase; + mtd->point = NULL; +@@ -1389,6 +1484,7 @@ + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; ++ mtd->owner = THIS_MODULE; + + /* Return happy */ + return 0; +@@ -1397,5 +1493,5 @@ + EXPORT_SYMBOL (nand_scan); + + MODULE_LICENSE ("GPL"); +-MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); ++MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); + MODULE_DESCRIPTION ("Generic NAND flash driver code"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c linux/drivers/mtd/nand/nand_ecc.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand_ecc.c 2004-11-19 10:25:12.011182768 +0100 +@@ -1,10 +1,10 @@ + /* + * drivers/mtd/nand_ecc.c + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * +- * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $ ++ * $Id: nand_ecc.c,v 1.10 2003/07/01 23:31:15 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + /* + * Pre-calculated 256-way 1 byte column parity +@@ -84,7 +85,7 @@ + /* + * Calculate 3 byte ECC code for 256 byte block + */ +-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) ++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) + { + u_char idx, reg1, reg2, reg3; + int j; +@@ -119,7 +120,7 @@ + /* + * Detect and correct a 1 bit error for 256 byte block + */ +-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) ++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) + { + u_char a, b, c, d1, d2, d3, add, bit, i; + +@@ -209,5 +210,5 @@ + EXPORT_SYMBOL(nand_correct_data); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill "); ++MODULE_AUTHOR("Steven J. Hill "); + MODULE_DESCRIPTION("Generic NAND ECC support"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c linux/drivers/mtd/nand/nand_ids.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand_ids.c 2004-11-19 10:25:12.013182464 +0100 +@@ -4,7 +4,7 @@ + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * +- * $Id: nand_ids.c,v 1.1 2002/12/02 22:06:04 gleixner Exp $ ++ * $Id: nand_ids.c,v 1.4 2003/05/21 15:15:08 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -18,21 +18,21 @@ + * Chip ID list + */ + struct nand_flash_dev nand_flash_ids[] = { +- {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V +- {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V +- {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V +- {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V +- {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V +- {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V +- {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V +- {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V ++ {"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1}, ++ {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1}, ++ {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0}, ++ {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1}, ++ {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1}, ++ {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1}, ++ {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0}, ++ {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0}, ++ {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0}, ++ {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0}, ++ {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0}, ++ {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0}, ++ {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0}, ++ {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0}, ++ {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0}, + {NULL,} + }; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/spia.c linux/drivers/mtd/nand/spia.c +--- linux-mips-2.4.27/drivers/mtd/nand/spia.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/spia.c 2004-11-19 10:25:12.014182312 +0100 +@@ -1,14 +1,14 @@ + /* + * drivers/mtd/nand/spia.c + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * + * 10-29-2001 TG change to support hardwarespecific access + * to controllines (due to change in nand.c) + * page_cache added + * +- * $Id: spia.c,v 1.16 2002/03/05 13:50:47 dwmw2 Exp $ ++ * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -20,6 +20,8 @@ + * a 64Mibit (8MiB x 8 bits) NAND flash device. + */ + ++#include ++#include + #include + #include + #include +@@ -35,14 +37,14 @@ + /* + * Values specific to the SPIA board (used with EP7212 processor) + */ +-#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ +-#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ +-#define SPIA_PEDR = 0x0080 /* ++#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ ++#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ ++#define SPIA_PEDR 0x0080 /* + * IO offset to Port E data register + * where the CLE, ALE and NCE pins + * are wired to. + */ +-#define SPIA_PEDDR = 0x00c0 /* ++#define SPIA_PEDDR 0x00c0 /* + * IO offset to Port E data direction + * register so we can control the IO + * lines. +@@ -62,21 +64,20 @@ + MODULE_PARM(spia_pedr, "i"); + MODULE_PARM(spia_peddr, "i"); + +-__setup("spia_io_base=",spia_io_base); +-__setup("spia_fio_base=",spia_fio_base); +-__setup("spia_pedr=",spia_pedr); +-__setup("spia_peddr=",spia_peddr); +- + /* + * Define partitions for flash device + */ + const static struct mtd_partition partition_info[] = { +- { name: "SPIA flash partition 1", +- offset: 0, +- size: 2*1024*1024 }, +- { name: "SPIA flash partition 2", +- offset: 2*1024*1024, +- size: 6*1024*1024 } ++ { ++ .name = "SPIA flash partition 1", ++ .offset = 0, ++ .size = 2*1024*1024 ++ }, ++ { ++ .name = "SPIA flash partition 2", ++ .offset = 2*1024*1024, ++ .size = 6*1024*1024 ++ } + }; + #define NUM_PARTITIONS 2 + +@@ -84,7 +85,7 @@ + /* + * hardware specific access to control-lines + */ +-void spia_hwcontrol(int cmd){ ++static void spia_hwcontrol(struct mtd_info *mtd, int cmd){ + + switch(cmd){ + +@@ -139,7 +140,7 @@ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (spia_mtd)) { ++ if (nand_scan (spia_mtd, 1)) { + kfree (spia_mtd); + return -ENXIO; + } +@@ -152,16 +153,6 @@ + return -ENOMEM; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for SPIA.\n"); +- kfree (this->data_buf); +- kfree (spia_mtd); +- return = -ENOMEM; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); + +@@ -183,7 +174,6 @@ + + /* Free internal data buffer */ + kfree (this->data_buf); +- kfree (this->page_cache); + + /* Free the MTD device structure */ + kfree (spia_mtd); +@@ -192,5 +182,5 @@ + #endif + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * TI fido board. It supports 32MiB and 64MiB cards ++ * ++ * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * MTD structure for TOTO board ++ */ ++static struct mtd_info *toto_mtd = NULL; ++ ++static int toto_io_base = OMAP_FLASH_1_BASE; ++ ++#define CONFIG_NAND_WORKAROUND 1 ++ ++#define NAND_NCE 0x4000 ++#define NAND_CLE 0x1000 ++#define NAND_ALE 0x0002 ++#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE) ++ ++#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0) ++#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE) ++#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */ ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0) ++#else ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE) ++#endif ++#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0) ++#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE) ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info64M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++ { .name = "toto devboard extra partition 4", ++ .offset = 32 * SZ_1M, ++ .size = 32 * SZ_1M }, ++}; ++ ++static struct mtd_partition partition_info32M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++}; ++ ++#define NUM_PARTITIONS32M 3 ++#define NUM_PARTITIONS64M 4 ++/* ++ * hardware specific access to control-lines ++*/ ++ ++static void toto_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ udelay(1); /* hopefully enough time for tc make proceding write to clear */ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; ++ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; ++ ++ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; ++ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; ++ ++ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; ++ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; ++ } ++ udelay(1); /* allow time to ensure gpio state to over take memory write */ ++} ++ ++/* ++ * Main initialization routine ++ */ ++int __init toto_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!toto_mtd) { ++ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&toto_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) toto_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ toto_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = toto_io_base; ++ this->IO_ADDR_W = toto_io_base; ++ this->hwcontrol = toto_hwcontrol; ++ this->dev_ready = NULL; ++ /* 25 us command delay time */ ++ this->chip_delay = 30; ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (toto_mtd, 1)) { ++ err = -ENXIO; ++ goto out_mtd; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n"); ++ err = -ENOMEM; ++ goto out_mtd; ++ } ++ ++ /* Register the partitions */ ++ switch(toto_mtd->size){ ++ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; ++ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break; ++ default: { ++ printk (KERN_WARNING "Unsupported Nand device\n"); ++ err = -ENXIO; ++ goto out_buf; ++ } ++ } ++ ++ gpioreserve(NAND_MASK); /* claim our gpios */ ++ archflashwp(0,0); /* open up flash for writing */ ++ ++ goto out; ++ ++out_buf: ++ kfree (this->data_buf); ++out_mtd: ++ kfree (toto_mtd); ++out: ++ return err; ++} ++ ++module_init(toto_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit toto_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &toto_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(toto_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (toto_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ kfree (toto_mtd); ++ ++ /* stop flash writes */ ++ archflashwp(0,1); ++ ++ /* release gpios to system */ ++ gpiorelease(NAND_MASK); ++} ++module_exit(toto_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Richard Woodruff "); ++MODULE_DESCRIPTION("Glue layer for NAND flash on toto board"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c linux/drivers/mtd/nand/tx4925ndfmc.c +--- linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/tx4925ndfmc.c 2004-11-19 10:25:12.017181856 +0100 +@@ -0,0 +1,442 @@ ++/* ++ * drivers/mtd/tx4925ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports ++ * 16MiB, 32MiB and 64MiB cards. ++ * ++ * Author: MontaVista Software, Inc. source@mvista.com ++ * ++ * Derived from drivers/mtd/autcpu12.c ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * $Id: tx4925ndfmc.c,v 1.1 2003/11/04 22:59:11 ahennessy Exp $ ++ * ++ * Copyright (C) 2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for RBTX4925 board ++ */ ++static struct mtd_info *tx4925ndfmc_mtd = NULL; ++ ++/* ++ * Module stuff ++ */ ++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) ++#define tx4925ndfmc_init init_module ++#define tx4925ndfmc_cleanup cleanup_module ++#endif ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info16k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 8 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info32k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 24 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info64k[] = { ++ { .name = "User FS", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 16 * 0x00100000, ++ .size = 48 * 0x00100000}, ++}; ++ ++static struct mtd_partition partition_info128k[] = { ++ { .name = "Skip bad section", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "User FS", ++ .offset = 16 * 0x00100000, ++ .size = 112 * 0x00100000 }, ++}; ++#define NUM_PARTITIONS16K 2 ++#define NUM_PARTITIONS32K 2 ++#define NUM_PARTITIONS64K 2 ++#define NUM_PARTITIONS128K 2 ++ ++/* ++ * hardware specific access to control-lines ++*/ ++static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_SETNCE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE; ++ break; ++ } ++} ++ ++/* ++* read device ready pin ++*/ ++static int tx4925ndfmc_device_ready(struct mtd_info *mtd) ++{ ++ int ready; ++ ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1; ++ return ready; ++} ++void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ /* reset first */ ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB; ++} ++static void tx4925ndfmc_disable_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++} ++static void tx4925ndfmc_enable_read_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ; ++} ++void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ ++ int i; ++ u_char *ecc = ecc_code; ++ tx4925ndfmc_enable_read_ecc(); ++ for (i = 0;i < 6;i++,ecc++) ++ *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr)); ++ tx4925ndfmc_disable_ecc(); ++} ++void tx4925ndfmc_device_setup(void) ++{ ++ ++ *(unsigned char *)0xbb005000 &= ~0x08; ++ ++ /* reset NDFMC */ ++ tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST; ++ while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST); ++ ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0; ++ tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW; ++} ++static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4925_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4925_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio ++n **pparts, char *); ++#endif ++ ++/* ++ * Main initialization routine ++ */ ++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++int __init tx4925ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4925ndfmc_mtd) { ++ printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ tx4925ndfmc_device_setup(); ++ ++ /* io is indirect via a register so don't need to ioremap address */ ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4925ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr); ++ this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr); ++ this->hwcontrol = tx4925ndfmc_hwcontrol; ++ this->enable_hwecc = tx4925ndfmc_enable_hwecc; ++ this->calculate_ecc = tx4925ndfmc_readecc; ++ this->correct_data = nand_correct_data; ++ this->eccmode = NAND_ECC_HW6_512; ++ this->dev_ready = tx4925ndfmc_device_ready; ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ this->read_byte = tx4925ndfmc_nand_read_byte; ++ this->write_byte = tx4925ndfmc_nand_write_byte; ++ this->cmdfunc = tx4925ndfmc_nand_command; ++ this->write_buf = tx4925ndfmc_nand_write_buf; ++ this->read_buf = tx4925ndfmc_nand_read_buf; ++ this->verify_buf = tx4925ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4925ndfmc_mtd, 1)) { ++ err = -ENXIO; ++ goto out_ior; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk ("Unable to allocate NAND data buffer for RBTX4925.\n"); ++ err = -ENOMEM; ++ goto out_ior; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4925ndfmc_mtd); ++ } ++#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ switch(tx4925ndfmc_mtd->size){ ++ case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break; ++ case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break; ++ case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; ++ case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; ++ default: { ++ printk ("Unsupported SmartMedia device\n"); ++ err = -ENXIO; ++ goto out_buf; ++ } ++ } ++#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ goto out; ++ ++out_buf: ++ kfree (this->data_buf); ++out_ior: ++out: ++ return err; ++} ++ ++module_init(tx4925ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit tx4925ndfmc_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(tx4925ndfmc_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (tx4925ndfmc_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4925ndfmc_mtd); ++} ++module_exit(tx4925ndfmc_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy "); ++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c linux/drivers/mtd/nand/tx4938ndfmc.c +--- linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/tx4938ndfmc.c 2004-11-19 10:25:12.019181552 +0100 +@@ -0,0 +1,422 @@ ++/* ++ * drivers/mtd/nand/tx4938ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device connected to ++ * TX4938 internal NAND Memory Controller. ++ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit. ++ * ++ * Author: source@mvista.com ++ * ++ * Based on spia.c by Steven J. Hill ++ * ++ * $Id: tx4938ndfmc.c,v 1.1 2003/11/04 22:59:11 ahennessy Exp $ ++ * ++ * Copyright (C) 2000-2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under the ++ * terms of the GNU General Public License version 2. This program is ++ * licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for TX4938 NDFMC ++ */ ++static struct mtd_info *tx4938ndfmc_mtd; ++ ++/* ++ * Define partitions for flash device ++ */ ++#define flush_wb() (void)tx4938_ndfmcptr->mcr; ++ ++#define NUM_PARTITIONS 3 ++#define NUMBER_OF_CIS_BLOCKS 24 ++#define SIZE_OF_BLOCK 0x00004000 ++#define NUMBER_OF_BLOCK_PER_ZONE 1024 ++#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK) ++#ifndef CONFIG_MTD_CMDLINE_PARTS ++/* ++ * You can use the following sample of MTD partitions ++ * on the NAND Flash Memory 32MB or more. ++ * ++ * The following figure shows the image of the sample partition on ++ * the 32MB NAND Flash Memory. ++ * ++ * Block No. ++ * 0 +-----------------------------+ ------ ++ * | CIS | ^ ++ * 24 +-----------------------------+ | ++ * | kernel image | | Zone 0 ++ * | | | ++ * +-----------------------------+ | ++ * 1023 | unused area | v ++ * +-----------------------------+ ------ ++ * 1024 | JFFS2 | ^ ++ * | | | ++ * | | | Zone 1 ++ * | | | ++ * | | | ++ * | | v ++ * 2047 +-----------------------------+ ------ ++ * ++ */ ++static struct mtd_partition partition_info[NUM_PARTITIONS] = { ++ { ++ .name = "RBTX4938 CIS Area", ++ .offset = 0, ++ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK), ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "RBTX4938 kernel image", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */ ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "Root FS (JFFS2)", ++ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */ ++ .size = MTDPART_SIZ_FULL ++ }, ++}; ++#endif ++ ++static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ switch (cmd) { ++ case NAND_CTL_SETCLE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE; ++ break; ++ /* TX4938_NDFMCR_CE bit is 0:high 1:low */ ++ case NAND_CTL_SETNCE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE; ++ break; ++ } ++} ++static int tx4938ndfmc_dev_ready(struct mtd_info *mtd) ++{ ++ flush_wb(); ++ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY); ++} ++static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ; ++ ecc_code[1] = tx4938_ndfmcptr->dtr; ++ ecc_code[0] = tx4938_ndfmcptr->dtr; ++ ecc_code[2] = tx4938_ndfmcptr->dtr; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++} ++static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON; ++} ++ ++static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4938_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4938_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R); ++} ++ ++static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; iIO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); ++#endif ++/* ++ * Main initialization routine ++ */ ++int __init tx4938ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int bsprt = 0, hold = 0xf, spw = 0xf; ++ int protected = 0; ++ ++ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) { ++ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n"); ++ return -ENODEV; ++ } ++ bsprt = 1; ++ hold = 2; ++ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */ ++ ++ if ((tx4938_ccfgptr->pcfg & ++ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) ++ != TX4938_PCFG_NDF_SEL) { ++ printk("TX4938 NDFMC: disabled by PCFG.\n"); ++ return -ENODEV; ++ } ++ ++ /* reset NDFMC */ ++ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST; ++ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST) ++ ; ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0; ++ tx4938_ndfmcptr->spr = hold << 4 | spw; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4938ndfmc_mtd) { ++ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4938ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->hwcontrol = tx4938ndfmc_hwcontrol; ++ this->dev_ready = tx4938ndfmc_dev_ready; ++ this->calculate_ecc = tx4938ndfmc_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ this->enable_hwecc = tx4938ndfmc_enable_hwecc; ++ this->eccmode = NAND_ECC_HW3_256; ++ this->chip_delay = 100; ++ this->read_byte = tx4938ndfmc_nand_read_byte; ++ this->write_byte = tx4938ndfmc_nand_write_byte; ++ this->cmdfunc = tx4938ndfmc_nand_command; ++ this->write_buf = tx4938ndfmc_nand_write_buf; ++ this->read_buf = tx4938ndfmc_nand_read_buf; ++ this->verify_buf = tx4938ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4938ndfmc_mtd, 1)) { ++ kfree (tx4938ndfmc_mtd); ++ return -ENXIO; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk ("Unable to allocate NAND data buffer for TX4938.\n"); ++ kfree (tx4938ndfmc_mtd); ++ return -ENOMEM; ++ } ++ ++ if (protected) { ++ printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); ++ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); ++ } ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4938ndfmc_mtd); ++ } ++#else ++ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS ); ++#endif ++ ++ return 0; ++} ++module_init(tx4938ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit tx4938ndfmc_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv; ++ ++ /* Unregister the device */ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ del_mtd_partitions(tx4938ndfmc_mtd); ++#endif ++ del_mtd_device (tx4938ndfmc_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4938ndfmc_mtd); ++ ++ /* Free internal data buffer */ ++ kfree (this->data_buf); ++} ++module_exit(tx4938ndfmc_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy "); ++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlcore.c linux/drivers/mtd/nftlcore.c +--- linux-mips-2.4.27/drivers/mtd/nftlcore.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/nftlcore.c 2004-11-19 10:25:11.653237184 +0100 +@@ -1,7 +1,7 @@ + /* Linux driver for NAND Flash Translation Layer */ + /* (c) 1999 Machine Vision Holdings, Inc. */ + /* Author: David Woodhouse */ +-/* $Id: nftlcore.c,v 1.87 2002/09/13 14:35:33 dwmw2 Exp $ */ ++/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */ + + /* + The contents of this file are distributed under the GNU General +@@ -23,15 +23,13 @@ + #include + #include + #include +-#include ++#include + +-#ifdef CONFIG_KMOD + #include +-#endif + #include + #include + #include +-#include ++#include + + /* maximum number of loops while examining next block, to have a + chance to detect consistency problems (they should never happen +@@ -39,187 +37,95 @@ + + #define MAX_LOOPS 10000 + +-/* NFTL block device stuff */ +-#define MAJOR_NR NFTL_MAJOR +-#define DEVICE_REQUEST nftl_request +-#define DEVICE_OFF(device) +- +- +-#include +-#include +- +-/* Linux-specific block device functions */ +- +-/* I _HATE_ the Linux block device setup more than anything else I've ever +- * encountered, except ... +- */ +- +-static int nftl_sizes[256]; +-static int nftl_blocksizes[256]; +- +-/* .. for the Linux partition table handling. */ +-struct hd_struct part_table[256]; +- +-#if LINUX_VERSION_CODE < 0x20328 +-static void dummy_init (struct gendisk *crap) +-{} +-#endif +- +-static struct gendisk nftl_gendisk = { +- major: MAJOR_NR, +- major_name: "nftl", +- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */ +- max_p: (1<mtd == mtd) { +- /* This is a Spare Media Header for an NFTL we've already found */ +- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n"); ++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) + return; +- } +- } +- if (firstfree == -1) { +- printk(KERN_WARNING "No more NFTL slot available\n"); +- return; +- } ++ ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); + + nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); ++ + if (!nftl) { +- printk(KERN_WARNING "Out of memory for NFTL data structures\n"); ++ printk(KERN_WARNING "NFTL: out of memory for data structures\n"); + return; + } ++ memset(nftl, 0, sizeof(*nftl)); + +- init_MUTEX(&nftl->mutex); +- +- nftl->mtd = mtd; ++ nftl->mbd.mtd = mtd; ++ nftl->mbd.devnum = -1; ++ nftl->mbd.blksize = 512; ++ nftl->mbd.tr = tr; + + if (NFTL_mount(nftl) < 0) { +- printk(KERN_WARNING "Could not mount NFTL device\n"); ++ printk(KERN_WARNING "NFTL: could not mount device\n"); + kfree(nftl); + return; + } + + /* OK, it's a new one. Set up all the data structures. */ +-#ifdef PSYCHO_DEBUG +- printk("Found new NFTL nftl%c\n", firstfree + 'a'); +-#endif + +- /* linux stuff */ +- nftl->usecount = 0; ++ /* Calculate geometry */ + nftl->cylinders = 1024; + nftl->heads = 16; + + temp = nftl->cylinders * nftl->heads; +- nftl->sectors = nftl->nr_sects / temp; +- if (nftl->nr_sects % temp) { ++ nftl->sectors = nftl->mbd.size / temp; ++ if (nftl->mbd.size % temp) { + nftl->sectors++; + temp = nftl->cylinders * nftl->sectors; +- nftl->heads = nftl->nr_sects / temp; ++ nftl->heads = nftl->mbd.size / temp; + +- if (nftl->nr_sects % temp) { ++ if (nftl->mbd.size % temp) { + nftl->heads++; + temp = nftl->heads * nftl->sectors; +- nftl->cylinders = nftl->nr_sects / temp; ++ nftl->cylinders = nftl->mbd.size / temp; + } + } + +- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) { +- printk(KERN_WARNING "Cannot calculate an NFTL geometry to " +- "match size of 0x%x.\n", nftl->nr_sects); +- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", ++ if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "NFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", nftl->mbd.size); ++ printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", + nftl->cylinders, nftl->heads , nftl->sectors, +- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors ); +- +- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */ ++ (long)nftl->cylinders * (long)nftl->heads * ++ (long)nftl->sectors ); + } +- NFTLs[firstfree] = nftl; +- /* Finally, set up the block device sizes */ +- nftl_sizes[firstfree * 16] = nftl->nr_sects; +- //nftl_blocksizes[firstfree*16] = 512; +- part_table[firstfree * 16].nr_sects = nftl->nr_sects; +- +- nftl_gendisk.nr_real++; +- +- /* partition check ... */ +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, firstfree); +-#else +- grok_partitions(&nftl_gendisk, firstfree, 1<nr_sects); +-#endif +-} +- +-static void NFTL_unsetup(int i) +-{ +- struct NFTLrecord *nftl = NFTLs[i]; +- +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i); +- +- NFTLs[i] = NULL; + ++ if (add_mtd_blktrans_dev(&nftl->mbd)) { + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); +- +- nftl_gendisk.nr_real--; + kfree(nftl); +-} +- +-/* Search the MTD device for NFTL partitions */ +-static void NFTL_notify_add(struct mtd_info *mtd) +-{ +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name); +- +- if (mtd) { +- if (!mtd->read_oob) { +- /* If this MTD doesn't have out-of-band data, +- then there's no point continuing */ +- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n"); + return; + } +- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n", +- mtd->read, mtd->size, mtd->erasesize); +- +- NFTL_setup(mtd); +- } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif + } + +-static void NFTL_notify_remove(struct mtd_info *mtd) ++static void nftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i; ++ struct NFTLrecord *nftl = (void *)dev; + +- for (i = 0; i < MAX_NFTLS; i++) { +- if (NFTLs[i] && NFTLs[i]->mtd == mtd) +- NFTL_unsetup(i); +- } ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ if (nftl->ReplUnitTable) ++ kfree(nftl->ReplUnitTable); ++ if (nftl->EUNtable) ++ kfree(nftl->EUNtable); ++ kfree(nftl); + } + + #ifdef CONFIG_NFTL_RW +@@ -303,7 +209,7 @@ + + targetEUN = thisEUN; + for (block = 0; block < nftl->EraseSize / 512; block ++) { +- MTD_READOOB(nftl->mtd, ++ MTD_READOOB(nftl->mbd.mtd, + (thisEUN * nftl->EraseSize) + (block * 512), + 16 , &retlen, (char *)&oob); + if (block == 2) { +@@ -420,7 +326,7 @@ + chain by selecting the longer one */ + oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); + oob.u.c.unused = 0xffffffff; +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -444,16 +350,16 @@ + if (BlockMap[block] == BLOCK_NIL) + continue; + +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), ++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + if (ret < 0) { +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) ++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, + movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + if (ret != -EIO) + printk("Error went away on retry.\n"); + } +- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512), ++ MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + } + +@@ -462,7 +368,7 @@ + = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, + 8, &retlen, (char *)&oob.u); + + /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ +@@ -582,7 +488,7 @@ + + lastEUN = writeEUN; + +- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci); + + DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", +@@ -670,12 +576,12 @@ + nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; + + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + +- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + /* we link the new block to the chain only after the +@@ -685,13 +591,13 @@ + /* Both in our cache... */ + nftl->ReplUnitTable[lastEUN] = writeEUN; + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum + = cpu_to_le16(writeEUN); + +- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -704,8 +610,10 @@ + return 0xffff; + } + +-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 writeEUN; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); + size_t retlen; +@@ -720,7 +628,7 @@ + return 1; + } + +- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); + /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ + +@@ -728,8 +636,10 @@ + } + #endif /* CONFIG_NFTL_RW */ + +-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 lastgoodEUN; + u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); +@@ -742,7 +652,7 @@ + + if (thisEUN != BLOCK_NIL) { + while (thisEUN < nftl->nb_blocks) { +- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs, ++ if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -761,13 +671,13 @@ + case SECTOR_IGNORE: + break; + default: +- printk("Unknown status for block %d in EUN %d: %x\n", ++ printk("Unknown status for block %ld in EUN %d: %x\n", + block, thisEUN, status); + break; + } + + if (!silly--) { +- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", ++ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", + block / (nftl->EraseSize / 512)); + return 1; + } +@@ -783,264 +693,22 @@ + loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; + size_t retlen; + u_char eccbuf[6]; +- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) ++ if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) + return -EIO; + } + return 0; + } + +-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) +-{ +- struct NFTLrecord *nftl; +- int p; +- +- nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS]; +- +- if (!nftl) return -EINVAL; +- +- switch (cmd) { +- case HDIO_GETGEO: { +- struct hd_geometry g; +- +- g.heads = nftl->heads; +- g.sectors = nftl->sectors; +- g.cylinders = nftl->cylinders; +- g.start = part_table[MINOR(inode->i_rdev)].start_sect; +- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; +- } +- case BLKGETSIZE: /* Return device size */ +- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, +- (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9, +- (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (nftl->mtd->sync) +- nftl->mtd->sync(nftl->mtd); +- return 0; +- +- case BLKRRPART: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- if (nftl->usecount > 1) return -EBUSY; +- /* +- * We have to flush all buffers and invalidate caches, +- * or we won't be able to re-use the partitions, +- * if there was a change and we don't want to reboot +- */ +- p = (1< 0) { +- kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p); +- if (part_table[p].nr_sects > 0) +- invalidate_device (devp, 1); +- +- part_table[MINOR(inode->i_dev)+p].start_sect = 0; +- part_table[MINOR(inode->i_dev)+p].nr_sects = 0; +- } +- +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS); +-#else +- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS, +- 1<nr_sects); +-#endif +- return 0; +- +-#if (LINUX_VERSION_CODE < 0x20303) +- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKSSZGET: +- return blk_ioctl(inode->i_rdev, cmd, arg); +-#endif +- +- default: +- return -EINVAL; +- } +-} +- +-void nftl_request(RQFUNC_ARG) +-{ +- unsigned int dev, block, nsect; +- struct NFTLrecord *nftl; +- char *buffer; +- struct request *req; +- int res; +- +- while (1) { +- INIT_REQUEST; /* blk.h */ +- req = CURRENT; +- +- /* We can do this because the generic code knows not to +- touch the request at the head of the queue */ +- spin_unlock_irq(&io_request_lock); +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n"); +- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n", +- (req->cmd == READ) ? "Read " : "Write", +- req->sector, req->current_nr_sectors); +- +- dev = MINOR(req->rq_dev); +- block = req->sector; +- nsect = req->current_nr_sectors; +- buffer = req->buffer; +- res = 1; /* succeed */ +- +- if (dev >= MAX_NFTLS * (1<rq_dev)); +- res = 0; /* fail */ +- goto repeat; +- } +- +- nftl = NFTLs[dev / (1<mutex); +- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); +- +- if (block + nsect > part_table[dev].nr_sects) { +- /* access past the end of device */ +- printk("nftl%c%d: bad access: block = %d, count = %d\n", +- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); +- up(&nftl->mutex); +- res = 0; /* fail */ +- goto repeat; +- } +- +- block += part_table[dev].start_sect; +- +- if (req->cmd == READ) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors); +- +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_readblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n"); +- up(&nftl->mutex); +- goto repeat; +- } else if (req->cmd == WRITE) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, +- req->nr_sectors); +-#ifdef CONFIG_NFTL_RW +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_writeblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n"); +-#else +- res = 0; /* Writes always fail */ +-#endif /* CONFIG_NFTL_RW */ +- up(&nftl->mutex); +- goto repeat; +- } else { +- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- repeat: +- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res); +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static int nftl_open(struct inode *ip, struct file *fp) +-{ +- int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS; +- struct NFTLrecord *thisNFTL; +- thisNFTL = NFTLs[nftlnum]; +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n"); +- +-#ifdef CONFIG_KMOD +- if (!thisNFTL && nftlnum == 0) { +- request_module("docprobe"); +- thisNFTL = NFTLs[nftlnum]; +- } +-#endif +- if (!thisNFTL) { +- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", +- nftlnum, ip->i_rdev, ip, fp); +- return -ENODEV; +- } +- +-#ifndef CONFIG_NFTL_RW +- if (fp->f_mode & FMODE_WRITE) +- return -EROFS; +-#endif /* !CONFIG_NFTL_RW */ +- +- thisNFTL->usecount++; +- BLK_INC_USE_COUNT; +- if (!get_mtd_device(thisNFTL->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- return 0; +-} +- +-static int nftl_release(struct inode *inode, struct file *fp) ++static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct NFTLrecord *thisNFTL; +- +- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); +- +- if (thisNFTL->mtd->sync) +- thisNFTL->mtd->sync(thisNFTL->mtd); +- thisNFTL->usecount--; +- BLK_DEC_USE_COUNT; ++ struct NFTLrecord *nftl = (void *)dev; + +- put_mtd_device(thisNFTL->mtd); ++ geo->heads = nftl->heads; ++ geo->sectors = nftl->sectors; ++ geo->cylinders = nftl->cylinders; + + return 0; + } +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations nftl_fops = { +- read: block_read, +- write: block_write, +- ioctl: nftl_ioctl, +- open: nftl_open, +- release: nftl_release, +- fsync: block_fsync, +-}; +-#else +-static struct block_device_operations nftl_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: nftl_open, +- release: nftl_release, +- ioctl: nftl_ioctl +-}; +-#endif +- +- + + /**************************************************************************** + * +@@ -1048,49 +716,33 @@ + * + ****************************************************************************/ + +-static struct mtd_notifier nftl_notifier = { +- add: NFTL_notify_add, +- remove: NFTL_notify_remove ++ ++struct mtd_blktrans_ops nftl_tr = { ++ .name = "nftl", ++ .major = NFTL_MAJOR, ++ .part_bits = NFTL_PARTN_BITS, ++ .getgeo = nftl_getgeo, ++ .readsect = nftl_readblock, ++#ifdef CONFIG_NFTL_RW ++ .writesect = nftl_writeblock, ++#endif ++ .add_mtd = nftl_add_mtd, ++ .remove_dev = nftl_remove_dev, ++ .owner = THIS_MODULE, + }; + + extern char nftlmountrev[]; + + int __init init_nftl(void) + { +- int i; +- +-#ifdef PRERELEASE +- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.87 $, nftlmount.c %s\n", nftlmountrev); +-#endif +- +- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ +- printk("unable to register NFTL block device on major %d\n", MAJOR_NR); +- return -EBUSY; +- } else { +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); ++ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev); + +- /* set block size to 1kB each */ +- for (i = 0; i < 256; i++) { +- nftl_blocksizes[i] = 1024; +- } +- blksize_size[MAJOR_NR] = nftl_blocksizes; +- +- add_gendisk(&nftl_gendisk); +- } +- +- register_mtd_user(&nftl_notifier); +- +- return 0; ++ return register_mtd_blktrans(&nftl_tr); + } + + static void __exit cleanup_nftl(void) + { +- unregister_mtd_user(&nftl_notifier); +- unregister_blkdev(MAJOR_NR, "nftl"); +- +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- +- del_gendisk(&nftl_gendisk); ++ deregister_mtd_blktrans(&nftl_tr); + } + + module_init(init_nftl); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c +--- linux-mips-2.4.27/drivers/mtd/nftlmount.c 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/nftlmount.c 2004-11-19 10:25:11.655236880 +0100 +@@ -4,7 +4,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id: nftlmount.c,v 1.31 2002/11/15 16:34:43 dwmw2 Exp $ ++ * $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -21,26 +21,17 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-#define __NO_VERSION__ + #include +-#include + #include +-#include +-#include +-#include +-#include + #include + #include +-#include +-#include + #include + #include + #include +-#include + + #define SECTORSIZE 512 + +-char nftlmountrev[]="$Revision: 1.31 $"; ++char nftlmountrev[]="$Revision: 1.34 $"; + + /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the + * various device information of the NFTL partition and Bad Unit Table. Update +@@ -59,8 +50,8 @@ + + /* Assume logical EraseSize == physical erasesize for starting the scan. + We'll sort it out later if we find a MediaHeader which says otherwise */ +- nftl->EraseSize = nftl->mtd->erasesize; +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ nftl->EraseSize = nftl->mbd.mtd->erasesize; ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + + nftl->MediaUnit = BLOCK_NIL; + nftl->SpareMediaUnit = BLOCK_NIL; +@@ -71,12 +62,12 @@ + + /* Check for ANAND header first. Then can whinge if it's found but later + checks fail */ +- if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { ++ if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { + static int warncount = 5; + + if (warncount) { + printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + if (!--warncount) + printk(KERN_WARNING "Further failures for this block will not be printed\n"); + } +@@ -87,16 +78,16 @@ + /* ANAND\0 not found. Continue */ + #if 0 + printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + #endif + continue; + } + + /* To be safer with BIOS, also use erase mark as discriminant */ +- if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, +- 8, &retlen, (char *)&h1)) < 0) { ++ if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ 8, &retlen, (char *)&h1) < 0)) { + printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + +@@ -106,23 +97,23 @@ + */ + if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n", +- block * nftl->EraseSize, nftl->mtd->index, ++ block * nftl->EraseSize, nftl->mbd.mtd->index, + le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1)); + continue; + } + + /* Finally reread to check ECC */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, +- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, ++ &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + + /* Paranoia. Check the ANAND header is still there after the ECC read */ + if (memcmp(buf, "ANAND", 6)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + continue; +@@ -137,8 +128,12 @@ + printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n", + nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); + /* if (debug) Print both side by side */ ++ if (boot_record_count < 2) { ++ /* We haven't yet seen two real ones */ + return -1; + } ++ continue; ++ } + if (boot_record_count == 1) + nftl->SpareMediaUnit = block; + +@@ -163,8 +158,8 @@ + } else if (mh->UnitSizeFactor != 0xff) { + printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", + mh->UnitSizeFactor); +- nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor); +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + } + nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { +@@ -182,7 +177,7 @@ + return -1; + } + +- nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); ++ nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + + /* If we're not using the last sectors in the device for some reason, + reduce nb_blocks accordingly so we forget they're there */ +@@ -220,7 +215,7 @@ + for (i = 0; i < nftl->nb_blocks; i++) { + if ((i & (SECTORSIZE - 1)) == 0) { + /* read one sector for every SECTORSIZE of blocks */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize + ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, &retlen, buf, + (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { + printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", +@@ -263,16 +258,16 @@ + for (i = 0; i < len; i += SECTORSIZE) { + /* we want to read the sector without ECC check here since a free + sector does not have ECC syndrome on it yet */ +- if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0) ++ if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + return -1; + if (memcmpb(buf, 0xff, SECTORSIZE) != 0) + return -1; + + if (check_oob) { +- if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize, ++ if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize, + &retlen, buf) < 0) + return -1; +- if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0) ++ if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0) + return -1; + } + address += SECTORSIZE; +@@ -297,7 +292,7 @@ + struct erase_info *instr = &nftl->instr; + + /* Read the Unit Control Information #1 for Wear-Leveling */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + goto default_uci1; + +@@ -314,7 +309,7 @@ + /* XXX: use async erase interface, XXX: test return code */ + instr->addr = block * nftl->EraseSize; + instr->len = nftl->EraseSize; +- MTD_ERASE(nftl->mtd, instr); ++ MTD_ERASE(nftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + /* could not format, FixMe: We should update the BadUnitTable +@@ -337,7 +332,7 @@ + return -1; + + uci.WearInfo = le32_to_cpu(nb_erases); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&uci) < 0) + return -1; + return 0; +@@ -363,7 +358,7 @@ + block = first_block; + for (;;) { + for (i = 0; i < sectors_per_block; i++) { +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -383,7 +378,7 @@ + /* sector not free actually : mark it as SECTOR_IGNORE */ + bci.Status = SECTOR_IGNORE; + bci.Status1 = SECTOR_IGNORE; +- MTD_WRITEOOB(nftl->mtd, ++ MTD_WRITEOOB(nftl->mbd.mtd, + block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci); + } +@@ -476,7 +471,7 @@ + size_t retlen; + + /* check erase mark. */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + +@@ -491,7 +486,7 @@ + h1.EraseMark = cpu_to_le16(ERASE_MARK); + h1.EraseMark1 = cpu_to_le16(ERASE_MARK); + h1.WearInfo = cpu_to_le32(0); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + } else { +@@ -503,7 +498,7 @@ + SECTORSIZE, 0) != 0) + return -1; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i, + 16, &retlen, buf) < 0) + return -1; + if (i == SECTORSIZE) { +@@ -533,7 +528,7 @@ + struct nftl_uci2 uci; + size_t retlen; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + return 0; + +@@ -572,9 +567,9 @@ + + for (;;) { + /* read the block header. If error, we format the chain */ +- if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, + &retlen, (char *)&h0) < 0 || +- MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) { + s->ReplUnitTable[block] = BLOCK_NIL; + do_format_chain = 1; +diff -Nurb linux-mips-2.4.27/drivers/mtd/redboot.c linux/drivers/mtd/redboot.c +--- linux-mips-2.4.27/drivers/mtd/redboot.c 2001-12-02 12:34:42.000000000 +0100 ++++ linux/drivers/mtd/redboot.c 2004-11-19 10:25:11.656236728 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $ ++ * $Id: redboot.c,v 1.12 2003/06/25 16:08:10 dwmw2 Exp $ + * + * Parse RedBoot-style Flash Image System (FIS) tables and + * produce a Linux partition array to match. +@@ -7,6 +7,7 @@ + + #include + #include ++#include + + #include + #include +@@ -34,7 +35,9 @@ + return 1; + } + +-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts) ++static int parse_redboot_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, ++ unsigned long fis_origin) + { + int nrparts = 0; + struct fis_image_desc *buf; +@@ -43,7 +46,9 @@ + int ret, i; + size_t retlen; + char *names; ++ char *nullname; + int namelen = 0; ++ static char nullstring[] = "unallocated"; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + +@@ -90,7 +95,11 @@ + goto out; + } + new_fl->img = &buf[i]; ++ if (fis_origin) { ++ buf[i].flash_base -= fis_origin; ++ } else { + buf[i].flash_base &= master->size-1; ++ } + + /* I'm sure the JFFS2 code has done me permanent damage. + * I now think the following is _normal_ +@@ -110,18 +119,24 @@ + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) + nrparts++; + } +- parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL); ++ parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } +- names = (char *)&parts[nrparts]; ++ + memset(parts, 0, sizeof(*parts)*nrparts + namelen); ++ ++ /* FIXME: Include nullname only if it's used */ ++ nullname = (char *)&parts[nrparts]; ++ sprintf(nullname, nullstring); ++ names = nullname + sizeof(nullstring); ++ + i=0; + + if (fl->img->flash_base) { +- parts[0].name = "unallocated space"; ++ parts[0].name = nullname; + parts[0].size = fl->img->flash_base; + parts[0].offset = 0; + } +@@ -133,11 +148,11 @@ + strcpy(names, fl->img->name); + names += strlen(names)+1; + +- if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { ++ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { + i++; + parts[i].offset = parts[i-1].size + parts[i-1].offset; + parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = "unallocated space"; ++ parts[i].name = nullname; + } + tmp_fl = fl; + fl = fl->next; +@@ -155,7 +170,24 @@ + return ret; + } + +-EXPORT_SYMBOL(parse_redboot_partitions); ++static struct mtd_part_parser redboot_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_redboot_partitions, ++ .name = "RedBoot", ++}; ++ ++static int __init redboot_parser_init(void) ++{ ++ return register_mtd_parser(&redboot_parser); ++} ++ ++static void __exit redboot_parser_exit(void) ++{ ++ deregister_mtd_parser(&redboot_parser); ++} ++ ++module_init(redboot_parser_init); ++module_exit(redboot_parser_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse "); +diff -Nurb linux-mips-2.4.27/fs/Config.in linux/fs/Config.in +--- linux-mips-2.4.27/fs/Config.in 2004-02-20 02:22:19.000000000 +0100 ++++ linux/fs/Config.in 2004-11-19 10:25:12.229149632 +0100 +@@ -49,6 +49,7 @@ + dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD + if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then + int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0 ++ bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND + fi + tristate 'Compressed ROM file system support' CONFIG_CRAMFS + bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS +diff -Nurb linux-mips-2.4.27/fs/jffs2/Makefile linux/fs/jffs2/Makefile +--- linux-mips-2.4.27/fs/jffs2/Makefile 2003-08-13 19:19:25.000000000 +0200 ++++ linux/fs/jffs2/Makefile 2004-11-19 10:25:12.071173648 +0100 +@@ -1,7 +1,7 @@ + # + # Makefile for the linux Journalling Flash FileSystem (JFFS) routines. + # +-# $Id: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $ ++# $Id: Makefile.common,v 1.1 2003/05/27 09:34:41 dwmw2 Exp $ + # + # Note! Dependencies are done automagically by 'make dep', which also + # removes any old dependencies. DON'T put your own dependencies here +@@ -10,16 +10,31 @@ + # Note 2! The CFLAGS definitions are now in the main makefile... + + +-COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \ +- compr_zlib.o ++obj-$(CONFIG_JFFS2_FS) := jffs2.o ++ ++COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o compr_zlib.o + JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \ +- read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \ +- symlink.o build.o erase.o background.o ++ read.o nodemgmt.o readinode.o write.o scan.o gc.o \ ++ symlink.o build.o erase.o background.o fs.o writev.o + +-O_TARGET := jffs2.o ++BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) ++ ++ifeq ($(BELOW25),y) ++LINUX_OBJS := super-v24.o crc32.o rbtree.o ++else ++LINUX_OBJS := super.o ++endif + +-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS) +-obj-m := $(O_TARGET) ++NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) := wbuf.o + ++jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \ ++ $(LINUX_OBJS) ++ ++ ++# 2.4 build compatibility ++ifeq ($(BELOW25),y) ++obj-y := $(jffs2-objs) ++O_TARGET := jffs2.o + include $(TOPDIR)/Rules.make ++endif + +diff -Nurb linux-mips-2.4.27/fs/jffs2/background.c linux/fs/jffs2/background.c +--- linux-mips-2.4.27/fs/jffs2/background.c 2001-11-06 08:56:10.000000000 +0100 ++++ linux/fs/jffs2/background.c 2004-11-19 10:25:12.072173496 +0100 +@@ -1,61 +1,36 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $ ++ * $Id: background.c,v 1.47 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + + #define __KERNEL_SYSCALLS__ + + #include +-#include +-#include + #include + #include +-#include + #include ++#include ++#include ++#include + #include "nodelist.h" + + + static int jffs2_garbage_collect_thread(void *); +-static int thread_should_wake(struct jffs2_sb_info *c); + + void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) + { +- spin_lock_bh(&c->erase_completion_lock); +- if (c->gc_task && thread_should_wake(c)) ++ spin_lock(&c->erase_completion_lock); ++ if (c->gc_task && jffs2_thread_should_wake(c)) + send_sig(SIGHUP, c->gc_task, 1); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + } + + /* This must only ever be called when no GC thread is currently running */ +@@ -86,12 +61,12 @@ + + void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) + { +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + if (c->gc_task) { + D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); + send_sig(SIGKILL, c->gc_task, 1); + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + wait_for_completion(&c->gc_thread_exit); + } + +@@ -99,34 +74,37 @@ + { + struct jffs2_sb_info *c = _c; + +- daemonize(); +- current->tty = NULL; ++ daemonize("jffs2_gcd_mtd%d", c->mtd->index); ++ allow_signal(SIGKILL); ++ allow_signal(SIGSTOP); ++ allow_signal(SIGCONT); ++ + c->gc_task = current; + up(&c->gc_thread_start); + +- sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index); +- +- /* FIXME in the 2.2 backport */ +- current->nice = 10; ++ set_user_nice(current, 10); + + for (;;) { +- spin_lock_irq(¤t->sigmask_lock); +- siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); +- recalc_sigpending(current); +- spin_unlock_irq(¤t->sigmask_lock); ++ allow_signal(SIGHUP); + +- if (!thread_should_wake(c)) { ++ if (!jffs2_thread_should_wake(c)) { + set_current_state (TASK_INTERRUPTIBLE); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); +- /* Yes, there's a race here; we checked thread_should_wake() before +- setting current->state to TASK_INTERRUPTIBLE. But it doesn't ++ /* Yes, there's a race here; we checked jffs2_thread_should_wake() ++ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't + matter - We don't care if we miss a wakeup, because the GC thread + is only an optimisation anyway. */ + schedule(); + } + +- if (current->need_resched) +- schedule(); ++ if (current->flags & PF_FREEZE) { ++ refrigerator(0); ++ /* refrigerator() should recalc sigpending for us ++ but doesn't. No matter - allow_signal() will. */ ++ continue; ++ } ++ ++ cond_resched(); + + /* Put_super will send a SIGKILL and then wait on the sem. + */ +@@ -134,9 +112,7 @@ + siginfo_t info; + unsigned long signr; + +- spin_lock_irq(¤t->sigmask_lock); +- signr = dequeue_signal(¤t->blocked, &info); +- spin_unlock_irq(¤t->sigmask_lock); ++ signr = dequeue_signal_lock(current, ¤t->blocked, &info); + + switch(signr) { + case SIGSTOP: +@@ -147,9 +123,10 @@ + + case SIGKILL: + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); +- spin_lock_bh(&c->erase_completion_lock); ++ die: ++ spin_lock(&c->erase_completion_lock); + c->gc_task = NULL; +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + complete_and_exit(&c->gc_thread_exit, 0); + + case SIGHUP: +@@ -157,27 +134,15 @@ + break; + default: + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); +- + } + } + /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ +- spin_lock_irq(¤t->sigmask_lock); +- siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); +- recalc_sigpending(current); +- spin_unlock_irq(¤t->sigmask_lock); ++ disallow_signal(SIGHUP); + + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); +- jffs2_garbage_collect_pass(c); ++ if (jffs2_garbage_collect_pass(c) == -ENOSPC) { ++ printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); ++ goto die; ++ } + } +-} +- +-static int thread_should_wake(struct jffs2_sb_info *c) +-{ +- D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", +- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size)); +- if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER && +- c->dirty_size > c->sector_size) +- return 1; +- else +- return 0; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/build.c linux/fs/jffs2/build.c +--- linux-mips-2.4.27/fs/jffs2/build.c 2003-07-05 05:23:44.000000000 +0200 ++++ linux/fs/jffs2/build.c 2004-11-19 10:25:12.073173344 +0100 +@@ -1,47 +1,22 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: build.c,v 1.16.2.3 2003/04/30 09:43:32 dwmw2 Exp $ ++ * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * + */ + + #include +-#include ++#include + #include + #include "nodelist.h" + +-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); +-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *); ++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); + + static inline struct jffs2_inode_cache * + first_inode_chain(int *i, struct jffs2_sb_info *c) +@@ -68,16 +43,52 @@ + ic; \ + ic = next_inode(&i, ic, (c))) + ++ ++static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++{ ++ struct jffs2_full_dirent *fd; ++ ++ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino)); ++ ++ /* For each child, increase nlink */ ++ for(fd = ic->scan_dents; fd; fd = fd->next) { ++ struct jffs2_inode_cache *child_ic; ++ if (!fd->ino) ++ continue; ++ ++ /* XXX: Can get high latency here with huge directories */ ++ ++ child_ic = jffs2_get_ino_cache(c, fd->ino); ++ if (!child_ic) { ++ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", ++ fd->name, fd->ino, ic->ino); ++ continue; ++ } ++ ++ if (child_ic->nlink++ && fd->type == DT_DIR) { ++ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); ++ if (fd->ino == 1 && ic->ino == 1) { ++ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); ++ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); ++ } ++ /* What do we do about it? */ ++ } ++ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); ++ /* Can't free them. We might need them in pass 2 */ ++ } ++} ++ + /* Scan plan: + - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go + - Scan directory tree from top down, setting nlink in inocaches + - Scan inocaches for inodes with nlink==0 + */ +-int jffs2_build_filesystem(struct jffs2_sb_info *c) ++static int jffs2_build_filesystem(struct jffs2_sb_info *c) + { + int ret; + int i; + struct jffs2_inode_cache *ic; ++ struct jffs2_full_dirent *dead_fds = NULL; + + /* First, scan the medium and build all the inode caches with + lists of physical nodes */ +@@ -90,14 +101,17 @@ + return ret; + + D1(printk(KERN_DEBUG "Scanned flash completely\n")); +- /* Now build the data map for each inode, marking obsoleted nodes +- as such, and also increase nlink of any children. */ ++ D1(jffs2_dump_block_lists(c)); ++ ++ /* Now scan the directory tree, increasing nlink according to every dirent found. */ + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); +- ret = jffs2_build_inode_pass1(c, ic); +- if (ret) { +- D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret)); +- return ret; ++ ++ D1(BUG_ON(ic->ino > c->highest_ino)); ++ ++ if (ic->scan_dents) { ++ jffs2_build_inode_pass1(c, ic); ++ cond_resched(); + } + } + D1(printk(KERN_DEBUG "Pass 1 complete\n")); +@@ -107,181 +121,226 @@ + children too, and repeat the scan. As that's going to be + a fairly uncommon occurrence, it's not so evil to do it this + way. Recursion bad. */ +- do { +- D1(printk(KERN_DEBUG "Pass 2 (re)starting\n")); +- ret = 0; ++ D1(printk(KERN_DEBUG "Pass 2 starting\n")); ++ + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); + if (ic->nlink) + continue; + +- ret = jffs2_build_remove_unlinked_inode(c, ic); +- if (ret) +- break; +- /* -EAGAIN means the inode's nlink was zero, so we deleted it, +- and furthermore that it had children and their nlink has now +- gone to zero too. So we have to restart the scan. */ ++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); ++ cond_resched(); ++ } ++ ++ D1(printk(KERN_DEBUG "Pass 2a starting\n")); ++ ++ while (dead_fds) { ++ struct jffs2_inode_cache *ic; ++ struct jffs2_full_dirent *fd = dead_fds; ++ ++ dead_fds = fd->next; ++ ++ ic = jffs2_get_ino_cache(c, fd->ino); ++ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); ++ ++ if (ic) ++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); ++ jffs2_free_full_dirent(fd); + } +- } while(ret == -EAGAIN); + + D1(printk(KERN_DEBUG "Pass 2 complete\n")); + +- /* Finally, we can scan again and free the dirent nodes and scan_info structs */ ++ /* Finally, we can scan again and free the dirent structs */ + for_each_inode(i, c, ic) { +- struct jffs2_scan_info *scan = ic->scan; + struct jffs2_full_dirent *fd; + D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); +- if (!scan) { +- if (ic->nlink) { +- D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink)); +- } +- continue; +- } +- ic->scan = NULL; +- while(scan->dents) { +- fd = scan->dents; +- scan->dents = fd->next; ++ ++ while(ic->scan_dents) { ++ fd = ic->scan_dents; ++ ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } +- kfree(scan); ++ ic->scan_dents = NULL; ++ cond_resched(); + } + D1(printk(KERN_DEBUG "Pass 3 complete\n")); ++ D1(jffs2_dump_block_lists(c)); ++ ++ /* Rotate the lists by some number to ensure wear levelling */ ++ jffs2_rotate_lists(c); + + return ret; + } + +-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) + { +- struct jffs2_tmp_dnode_info *tn; ++ struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; +- struct jffs2_node_frag *fraglist = NULL; +- struct jffs2_tmp_dnode_info *metadata = NULL; +- +- D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino)); +- if (ic->ino > c->highest_ino) +- c->highest_ino = ic->ino; + +- if (!ic->scan->tmpnodes && ic->ino != 1) { +- D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino)); +- } +- /* Build the list to make sure any obsolete nodes are marked as such */ +- while(ic->scan->tmpnodes) { +- tn = ic->scan->tmpnodes; +- ic->scan->tmpnodes = tn->next; +- +- if (metadata && tn->version > metadata->version) { +- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n", +- metadata->fn->raw->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); + +- jffs2_free_full_dnode(metadata->fn); +- jffs2_free_tmp_dnode_info(metadata); +- metadata = NULL; ++ for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { ++ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); ++ jffs2_mark_node_obsolete(c, raw); + } + +- if (tn->fn->size) { +- jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn); +- jffs2_free_tmp_dnode_info(tn); +- } else { +- if (!metadata) { +- metadata = tn; +- } else { +- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n", +- tn->fn->raw->flash_offset &~3)); +- +- jffs2_free_full_dnode(tn->fn); +- jffs2_free_tmp_dnode_info(tn); +- } +- } +- } ++ if (ic->scan_dents) { ++ int whinged = 0; ++ D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino)); + +- /* OK. Now clear up */ +- if (metadata) { +- jffs2_free_full_dnode(metadata->fn); +- jffs2_free_tmp_dnode_info(metadata); +- } +- metadata = NULL; ++ while(ic->scan_dents) { ++ struct jffs2_inode_cache *child_ic; + +- while (fraglist) { +- struct jffs2_node_frag *frag; +- frag = fraglist; +- fraglist = fraglist->next; ++ fd = ic->scan_dents; ++ ic->scan_dents = fd->next; + +- if (frag->node && !(--frag->node->frags)) { +- jffs2_free_full_dnode(frag->node); ++ if (!fd->ino) { ++ /* It's a deletion dirent. Ignore it */ ++ D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name)); ++ jffs2_free_full_dirent(fd); ++ continue; + } +- jffs2_free_node_frag(frag); ++ if (!whinged) { ++ whinged = 1; ++ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); + } + +- /* Now for each child, increase nlink */ +- for(fd=ic->scan->dents; fd; fd = fd->next) { +- struct jffs2_inode_cache *child_ic; +- if (!fd->ino) +- continue; ++ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", ++ fd->name, fd->ino)); + + child_ic = jffs2_get_ino_cache(c, fd->ino); + if (!child_ic) { +- printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", +- fd->name, fd->ino, ic->ino); ++ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); ++ jffs2_free_full_dirent(fd); + continue; + } + +- if (child_ic->nlink++ && fd->type == DT_DIR) { +- printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); +- if (fd->ino == 1 && ic->ino == 1) { +- printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); +- printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); ++ /* Reduce nlink of the child. If it's now zero, stick it on the ++ dead_fds list to be cleaned up later. Else just free the fd */ ++ ++ child_ic->nlink--; ++ ++ if (!child_ic->nlink) { ++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", ++ fd->ino, fd->name)); ++ fd->next = *dead_fds; ++ *dead_fds = fd; ++ } else { ++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", ++ fd->ino, fd->name, child_ic->nlink)); ++ jffs2_free_full_dirent(fd); + } +- /* What do we do about it? */ + } +- D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); +- /* Can't free them. We might need them in pass 2 */ + } +- return 0; ++ ++ /* ++ We don't delete the inocache from the hash list and free it yet. ++ The erase code will do that, when all the nodes are completely gone. ++ */ + } + +-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) + { +- struct jffs2_raw_node_ref *raw; +- struct jffs2_full_dirent *fd; +- int ret = 0; ++ uint32_t size; + +- if(!ic->scan) { +- D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino)); +- return 0; +- } ++ /* Deletion should almost _always_ be allowed. We're fairly ++ buggered once we stop allowing people to delete stuff ++ because there's not enough free space... */ ++ c->resv_blocks_deletion = 2; ++ ++ /* Be conservative about how much space we need before we allow writes. ++ On top of that which is required for deletia, require an extra 2% ++ of the medium to be available, for overhead caused by nodes being ++ split across blocks, etc. */ ++ ++ size = c->flash_size / 50; /* 2% of flash size */ ++ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ ++ size += c->sector_size - 1; /* ... and round up */ ++ ++ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); ++ ++ /* When do we let the GC thread run in the background */ ++ ++ c->resv_blocks_gctrigger = c->resv_blocks_write + 1; ++ ++ /* When do we allow garbage collection to merge nodes to make ++ long-term progress at the expense of short-term space exhaustion? */ ++ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; ++ ++ /* When do we allow garbage collection to eat from bad blocks rather ++ than actually making progress? */ ++ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; ++ ++ /* If there's less than this amount of dirty space, don't bother ++ trying to GC to make more space. It'll be a fruitless task */ ++ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); ++ ++ D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", ++ c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks)); ++ D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n", ++ c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n", ++ c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n", ++ c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n", ++ c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n", ++ c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n", ++ c->nospc_dirty_size)); ++} + +- D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); ++int jffs2_do_mount_fs(struct jffs2_sb_info *c) ++{ ++ int i; + +- for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { +- D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3)); +- jffs2_mark_node_obsolete(c, raw); ++ c->free_size = c->flash_size; ++ c->nr_blocks = c->flash_size / c->sector_size; ++ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); ++ if (!c->blocks) ++ return -ENOMEM; ++ for (i=0; inr_blocks; i++) { ++ INIT_LIST_HEAD(&c->blocks[i].list); ++ c->blocks[i].offset = i * c->sector_size; ++ c->blocks[i].free_size = c->sector_size; ++ c->blocks[i].dirty_size = 0; ++ c->blocks[i].wasted_size = 0; ++ c->blocks[i].unchecked_size = 0; ++ c->blocks[i].used_size = 0; ++ c->blocks[i].first_node = NULL; ++ c->blocks[i].last_node = NULL; ++ } ++ ++ init_MUTEX(&c->alloc_sem); ++ init_MUTEX(&c->erase_free_sem); ++ init_waitqueue_head(&c->erase_wait); ++ init_waitqueue_head(&c->inocache_wq); ++ spin_lock_init(&c->erase_completion_lock); ++ spin_lock_init(&c->inocache_lock); ++ ++ INIT_LIST_HEAD(&c->clean_list); ++ INIT_LIST_HEAD(&c->very_dirty_list); ++ INIT_LIST_HEAD(&c->dirty_list); ++ INIT_LIST_HEAD(&c->erasable_list); ++ INIT_LIST_HEAD(&c->erasing_list); ++ INIT_LIST_HEAD(&c->erase_pending_list); ++ INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); ++ INIT_LIST_HEAD(&c->erase_complete_list); ++ INIT_LIST_HEAD(&c->free_list); ++ INIT_LIST_HEAD(&c->bad_list); ++ INIT_LIST_HEAD(&c->bad_used_list); ++ c->highest_ino = 1; ++ ++ if (jffs2_build_filesystem(c)) { ++ D1(printk(KERN_DEBUG "build_fs failed\n")); ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ return -EIO; + } + +- if (ic->scan->dents) { +- printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); +- +- while(ic->scan->dents) { +- struct jffs2_inode_cache *child_ic; ++ jffs2_calc_trigger_levels(c); + +- fd = ic->scan->dents; +- ic->scan->dents = fd->next; +- +- D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", +- fd->name, fd->ino)); +- +- child_ic = jffs2_get_ino_cache(c, fd->ino); +- if (!child_ic) { +- printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); +- continue; +- } +- jffs2_free_full_dirent(fd); +- child_ic->nlink--; +- } +- ret = -EAGAIN; +- } +- kfree(ic->scan); +- ic->scan = NULL; +- // jffs2_del_ino_cache(c, ic); +- // jffs2_free_inode_cache(ic); +- return ret; ++ return 0; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr.c linux/fs/jffs2/compr.c +--- linux-mips-2.4.27/fs/jffs2/compr.c 2001-11-05 21:16:18.000000000 +0100 ++++ linux/fs/jffs2/compr.c 2004-11-19 10:25:12.085171520 +0100 +@@ -1,59 +1,37 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $ ++ * $Id: compr.c,v 1.33 2003/11/28 17:22:54 dwmw2 Exp $ + * + */ + + #include + #include +-#include + #include ++#include ++#include + #include ++#include "nodelist.h" + +-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); ++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); + + + /* jffs2_compress: + * @data: Pointer to uncompressed data +- * @cdata: Pointer to buffer for compressed data ++ * @cdata: Pointer to returned pointer to buffer for compressed data + * @datalen: On entry, holds the amount of data available for compression. + * On exit, expected to hold the amount of data actually compressed. + * @cdatalen: On entry, holds the amount of space available for compressed +@@ -68,47 +46,59 @@ + * jffs2_compress should compress as much as will fit, and should set + * *datalen accordingly to show the amount of data which were compressed. + */ +-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen) ++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen) + { ++#ifdef JFFS2_COMPRESSION + int ret; + +- ret = zlib_compress(data_in, cpage_out, datalen, cdatalen); ++ *cpage_out = kmalloc(*cdatalen, GFP_KERNEL); ++ if (!*cpage_out) { ++ printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n"); ++ goto out; ++ } ++ ++#ifdef JFFS2_USE_ZLIB ++ ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_ZLIB; + } +-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ +- ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen); ++#endif ++#ifdef JFFS2_USE_DYNRUBIN ++ ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_DYNRUBIN; + } + #endif +-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */ +- ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen); ++#ifdef JFFS2_USE_RUBINMIPS ++ ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_RUBINMIPS; + } + #endif ++#ifdef JFFS2_USE_RTIME + /* rtime does manage to recompress already-compressed data */ +- ret = rtime_compress(data_in, cpage_out, datalen, cdatalen); ++ ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_RTIME; + } +-#if 0 +- /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */ +- /* If we get here, no compression is going to work */ +- /* But we might want to use the fragmentation part -- Arjan */ +- memcpy(cpage_out,data_in,min(*datalen,*cdatalen)); +- if (*datalen > *cdatalen) +- *datalen = *cdatalen; + #endif ++ kfree(*cpage_out); ++#endif /* Compression */ ++ out: ++ *cpage_out = data_in; ++ *datalen = *cdatalen; + return JFFS2_COMPR_NONE; /* We failed to compress */ +- + } + ++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) ++{ ++ if (orig != comprbuf) ++ kfree(comprbuf); ++} + + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen) ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) + { + switch (comprtype) { + case JFFS2_COMPR_NONE: +@@ -119,30 +109,27 @@ + case JFFS2_COMPR_ZERO: + memset(data_out, 0, datalen); + break; +- ++#ifdef JFFS2_USE_ZLIB + case JFFS2_COMPR_ZLIB: +- zlib_decompress(cdata_in, data_out, cdatalen, datalen); ++ jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- ++#endif ++#ifdef JFFS2_USE_RTIME + case JFFS2_COMPR_RTIME: +- rtime_decompress(cdata_in, data_out, cdatalen, datalen); ++ jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- +- case JFFS2_COMPR_RUBINMIPS: +-#if 0 /* Disabled 23/9/1 */ +- rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); +-#else +- printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n"); + #endif ++#ifdef JFFS2_USE_RUBINMIPS ++ case JFFS2_COMPR_RUBINMIPS: ++ jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- case JFFS2_COMPR_DYNRUBIN: +-#if 1 /* Phase this one out */ +- dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); +-#else +- printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n"); + #endif +- break; ++#ifdef JFFS2_USE_DYNRUBIN ++ case JFFS2_COMPR_DYNRUBIN: + ++ jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); ++ break; ++#endif + default: + printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype); + return -EIO; +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rtime.c linux/fs/jffs2/compr_rtime.c +--- linux-mips-2.4.27/fs/jffs2/compr_rtime.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/compr_rtime.c 2004-11-19 10:25:12.087171216 +0100 +@@ -1,43 +1,19 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $ ++ * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $ + * + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last +- * occurances" for every possible source-value; after sending the ++ * occurrences" for every possible source-value; after sending the + * first source-byte, the second byte indicated the "run" length of + * matches + * +@@ -51,10 +27,10 @@ + #include + + /* _compress returns the compressed size, -1 if bigger */ +-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { +- int positions[256]; ++ short positions[256]; + int outpos = 0; + int pos=0; + +@@ -91,10 +67,10 @@ + } + + +-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 srclen, __u32 destlen) ++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) + { +- int positions[256]; ++ short positions[256]; + int outpos = 0; + int pos=0; + +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.c linux/fs/jffs2/compr_rubin.c +--- linux-mips-2.4.27/fs/jffs2/compr_rubin.c 2001-11-05 21:16:18.000000000 +0100 ++++ linux/fs/jffs2/compr_rubin.c 2004-11-19 10:25:12.088171064 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by Arjan van de Ven + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $ ++ * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $ + * + */ + +@@ -43,7 +19,7 @@ + + + +-void init_rubin(struct rubin_state *rs, int div, int *bits) ++static void init_rubin(struct rubin_state *rs, int div, int *bits) + { + int c; + +@@ -56,7 +32,7 @@ + } + + +-int encode(struct rubin_state *rs, long A, long B, int symbol) ++static int encode(struct rubin_state *rs, long A, long B, int symbol) + { + + long i0, i1; +@@ -91,7 +67,7 @@ + } + + +-void end_rubin(struct rubin_state *rs) ++static void end_rubin(struct rubin_state *rs) + { + + int i; +@@ -104,7 +80,7 @@ + } + + +-void init_decode(struct rubin_state *rs, int div, int *bits) ++static void init_decode(struct rubin_state *rs, int div, int *bits) + { + init_rubin(rs, div, bits); + +@@ -151,7 +127,7 @@ + rs->rec_q = rec_q; + } + +-int decode(struct rubin_state *rs, long A, long B) ++static int decode(struct rubin_state *rs, long A, long B) + { + unsigned long p = rs->p, q = rs->q; + long i0, threshold; +@@ -212,8 +188,8 @@ + + + +-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, +- unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen) ++static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, ++ unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) + { + int outpos = 0; + int pos=0; +@@ -246,20 +222,20 @@ + } + #if 0 + /* _compress returns the compressed size, -1 if bigger */ +-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { + return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + } + #endif +-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { + int bits[8]; + unsigned char histo[256]; + int i; + int ret; +- __u32 mysrclen, mydstlen; ++ uint32_t mysrclen, mydstlen; + + mysrclen = *sourcelen; + mydstlen = *dstlen - 8; +@@ -315,8 +291,8 @@ + return 0; + } + +-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, +- unsigned char *page_out, __u32 srclen, __u32 destlen) ++static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, ++ unsigned char *page_out, uint32_t srclen, uint32_t destlen) + { + int outpos = 0; + struct rubin_state rs; +@@ -330,14 +306,14 @@ + } + + +-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 sourcelen, __u32 dstlen) ++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t sourcelen, uint32_t dstlen) + { + rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + } + +-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 sourcelen, __u32 dstlen) ++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t sourcelen, uint32_t dstlen) + { + int bits[8]; + int c; +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.h linux/fs/jffs2/compr_rubin.h +--- linux-mips-2.4.27/fs/jffs2/compr_rubin.h 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/compr_rubin.h 2004-11-19 10:25:12.090170760 +0100 +@@ -1,7 +1,7 @@ + /* Rubin encoder/decoder header */ + /* work started at : aug 3, 1994 */ + /* last modification : aug 15, 1994 */ +-/* $Id: compr_rubin.h,v 1.5 2001/02/26 13:50:01 dwmw2 Exp $ */ ++/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */ + + #include "pushpull.h" + +@@ -19,10 +19,3 @@ + int bit_divider; + int bits[8]; + }; +- +- +-void init_rubin (struct rubin_state *rs, int div, int *bits); +-int encode (struct rubin_state *, long, long, int); +-void end_rubin (struct rubin_state *); +-void init_decode (struct rubin_state *, int div, int *bits); +-int decode (struct rubin_state *, long, long); +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_zlib.c linux/fs/jffs2/compr_zlib.c +--- linux-mips-2.4.27/fs/jffs2/compr_zlib.c 2003-01-11 18:53:17.000000000 +0100 ++++ linux/fs/jffs2/compr_zlib.c 2004-11-19 10:25:12.091170608 +0100 +@@ -1,50 +1,26 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_zlib.c,v 1.8.2.1 2002/10/11 09:04:44 dwmw2 Exp $ ++ * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $ + * + */ + +-#ifndef __KERNEL__ ++#if !defined(__KERNEL__) && !defined(__ECOS) + #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" + #endif + + #include + #include +-#include /* for min() */ + #include +-#include + #include ++#include ++#include + #include "nodelist.h" + + /* Plan: call deflate() with avail_in == *sourcelen, +@@ -58,21 +34,24 @@ + + static DECLARE_MUTEX(deflate_sem); + static DECLARE_MUTEX(inflate_sem); +-static void *deflate_workspace; +-static void *inflate_workspace; ++static z_stream inf_strm, def_strm; ++ ++#ifdef __KERNEL__ /* Linux-only */ ++#include ++#include + + int __init jffs2_zlib_init(void) + { +- deflate_workspace = vmalloc(zlib_deflate_workspacesize()); +- if (!deflate_workspace) { ++ def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); ++ if (!def_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); +- inflate_workspace = vmalloc(zlib_inflate_workspacesize()); +- if (!inflate_workspace) { ++ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); ++ if (!inf_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); +- vfree(deflate_workspace); ++ vfree(def_strm.workspace); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); +@@ -81,97 +60,120 @@ + + void jffs2_zlib_exit(void) + { +- vfree(deflate_workspace); +- vfree(inflate_workspace); ++ vfree(def_strm.workspace); ++ vfree(inf_strm.workspace); + } ++#endif /* __KERNEL__ */ + +-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { +- z_stream strm; + int ret; + + if (*dstlen <= STREAM_END_SPACE) + return -1; + + down(&deflate_sem); +- strm.workspace = deflate_workspace; + +- if (Z_OK != zlib_deflateInit(&strm, 3)) { ++ if (Z_OK != zlib_deflateInit(&def_strm, 3)) { + printk(KERN_WARNING "deflateInit failed\n"); + up(&deflate_sem); + return -1; + } + +- strm.next_in = data_in; +- strm.total_in = 0; ++ def_strm.next_in = data_in; ++ def_strm.total_in = 0; + +- strm.next_out = cpage_out; +- strm.total_out = 0; ++ def_strm.next_out = cpage_out; ++ def_strm.total_out = 0; + +- while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { +- strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); +- strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); ++ while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { ++ def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); ++ def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out); + D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n", +- strm.avail_in, strm.avail_out)); +- ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH); ++ def_strm.avail_in, def_strm.avail_out)); ++ ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); + D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", +- strm.avail_in, strm.avail_out, strm.total_in, strm.total_out)); ++ def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out)); + if (ret != Z_OK) { + D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); +- zlib_deflateEnd(&strm); ++ zlib_deflateEnd(&def_strm); + up(&deflate_sem); + return -1; + } + } +- strm.avail_out += STREAM_END_SPACE; +- strm.avail_in = 0; +- ret = zlib_deflate(&strm, Z_FINISH); +- zlib_deflateEnd(&strm); +- up(&deflate_sem); ++ def_strm.avail_out += STREAM_END_SPACE; ++ def_strm.avail_in = 0; ++ ret = zlib_deflate(&def_strm, Z_FINISH); ++ zlib_deflateEnd(&def_strm); ++ + if (ret != Z_STREAM_END) { + D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); +- return -1; ++ ret = -1; ++ goto out; + } + +- D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", +- strm.total_in, strm.total_out)); ++ if (def_strm.total_out >= def_strm.total_in) { ++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n", ++ def_strm.total_in, def_strm.total_out)); ++ ret = -1; ++ goto out; ++ } + +- if (strm.total_out >= strm.total_in) +- return -1; ++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", ++ def_strm.total_in, def_strm.total_out)); + +- *dstlen = strm.total_out; +- *sourcelen = strm.total_in; +- return 0; ++ *dstlen = def_strm.total_out; ++ *sourcelen = def_strm.total_in; ++ ret = 0; ++ out: ++ up(&deflate_sem); ++ return ret; + } + +-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 srclen, __u32 destlen) ++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) + { +- z_stream strm; + int ret; ++ int wbits = MAX_WBITS; + + down(&inflate_sem); +- strm.workspace = inflate_workspace; + +- if (Z_OK != zlib_inflateInit(&strm)) { ++ inf_strm.next_in = data_in; ++ inf_strm.avail_in = srclen; ++ inf_strm.total_in = 0; ++ ++ inf_strm.next_out = cpage_out; ++ inf_strm.avail_out = destlen; ++ inf_strm.total_out = 0; ++ ++ /* If it's deflate, and it's got no preset dictionary, then ++ we can tell zlib to skip the adler32 check. */ ++ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ++ ((data_in[0] & 0x0f) == Z_DEFLATED) && ++ !(((data_in[0]<<8) + data_in[1]) % 31)) { ++ ++ D2(printk(KERN_DEBUG "inflate skipping adler32\n")); ++ wbits = -((data_in[0] >> 4) + 8); ++ inf_strm.next_in += 2; ++ inf_strm.avail_in -= 2; ++ } else { ++ /* Let this remain D1 for now -- it should never happen */ ++ D1(printk(KERN_DEBUG "inflate not skipping adler32\n")); ++ } ++ ++ ++ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { + printk(KERN_WARNING "inflateInit failed\n"); + up(&inflate_sem); + return; + } +- strm.next_in = data_in; +- strm.avail_in = srclen; +- strm.total_in = 0; +- +- strm.next_out = cpage_out; +- strm.avail_out = destlen; +- strm.total_out = 0; + +- while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK) ++ while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) + ; + if (ret != Z_STREAM_END) { + printk(KERN_NOTICE "inflate returned %d\n", ret); + } +- zlib_inflateEnd(&strm); ++ zlib_inflateEnd(&inf_strm); + up(&inflate_sem); + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/comprtest.c linux/fs/jffs2/comprtest.c +--- linux-mips-2.4.27/fs/jffs2/comprtest.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/comprtest.c 2004-11-19 10:25:12.093170304 +0100 +@@ -1,4 +1,4 @@ +-/* $Id: comprtest.c,v 1.4 2001/02/21 14:03:20 dwmw2 Exp $ */ ++/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */ + + #include + #include +@@ -266,13 +266,13 @@ + static unsigned char decomprbuf[TESTDATA_LEN]; + + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen); ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen); ++ uint32_t *datalen, uint32_t *cdatalen); + + int init_module(void ) { + unsigned char comprtype; +- __u32 c, d; ++ uint32_t c, d; + int ret; + + printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", +diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.c linux/fs/jffs2/crc32.c +--- linux-mips-2.4.27/fs/jffs2/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/crc32.c 2004-11-19 10:25:12.095170000 +0100 +@@ -0,0 +1,97 @@ ++/* ++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or ++ * code or tables extracted from it, as desired without restriction. ++ * ++ * First, the polynomial itself and its table of feedback terms. The ++ * polynomial is ++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 ++ * ++ * Note that we take it "backwards" and put the highest-order term in ++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the ++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in ++ * the MSB being 1 ++ * ++ * Note that the usual hardware shift register implementation, which ++ * is what we're using (we're merely optimizing it by doing eight-bit ++ * chunks at a time) shifts bits into the lowest-order term. In our ++ * implementation, that means shifting towards the right. Why do we ++ * do it this way? Because the calculated CRC must be transmitted in ++ * order from highest-order term to lowest-order term. UARTs transmit ++ * characters in order from LSB to MSB. By storing the CRC this way ++ * we hand it to the UART in the order low-byte to high-byte; the UART ++ * sends each low-bit to hight-bit; and the result is transmission bit ++ * by bit from highest- to lowest-order term without requiring any bit ++ * shuffling on our part. Reception works similarly ++ * ++ * The feedback terms table consists of 256, 32-bit entries. Notes ++ * ++ * The table can be generated at runtime if desired; code to do so ++ * is shown later. It might not be obvious, but the feedback ++ * terms simply represent the results of eight shift/xor opera ++ * tions for all combinations of data and CRC register values ++ * ++ * The values must be right-shifted by eight bits by the "updcrc ++ * logic; the shift must be unsigned (bring in zeroes). On some ++ * hardware you could probably optimize the shift in assembler by ++ * using byte-swap instructions ++ * polynomial $edb88320 ++ */ ++ ++/* $Id: crc32.c,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */ ++ ++#include "crc32.h" ++ ++const uint32_t crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; +diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.h linux/fs/jffs2/crc32.h +--- linux-mips-2.4.27/fs/jffs2/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/crc32.h 2004-11-19 10:25:12.096169848 +0100 +@@ -0,0 +1,21 @@ ++#ifndef CRC32_H ++#define CRC32_H ++ ++/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */ ++ ++#include ++ ++extern const uint32_t crc32_table[256]; ++ ++/* Return a 32-bit CRC of the contents of the buffer. */ ++ ++static inline uint32_t ++crc32(uint32_t val, const void *ss, int len) ++{ ++ const unsigned char *s = ss; ++ while (--len >= 0) ++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); ++ return val; ++} ++ ++#endif +diff -Nurb linux-mips-2.4.27/fs/jffs2/dir.c linux/fs/jffs2/dir.c +--- linux-mips-2.4.27/fs/jffs2/dir.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/dir.c 2004-11-19 10:25:12.097169696 +0100 +@@ -1,84 +1,73 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: dir.c,v 1.45.2.8 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $ + * + */ + + #include + #include ++#include + #include +-#include /* For completion */ ++#include + #include + #include + #include ++#include + #include "nodelist.h" +-#include ++ ++/* Urgh. Please tell me there's a nicer way of doing these. */ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) ++typedef int mknod_arg_t; ++#define NAMEI_COMPAT(x) ((void *)x) ++#else ++typedef dev_t mknod_arg_t; ++#define NAMEI_COMPAT(x) (x) ++#endif + + static int jffs2_readdir (struct file *, void *, filldir_t); + +-static int jffs2_create (struct inode *,struct dentry *,int); +-static struct dentry *jffs2_lookup (struct inode *,struct dentry *); ++static int jffs2_create (struct inode *,struct dentry *,int, ++ struct nameidata *); ++static struct dentry *jffs2_lookup (struct inode *,struct dentry *, ++ struct nameidata *); + static int jffs2_link (struct dentry *,struct inode *,struct dentry *); + static int jffs2_unlink (struct inode *,struct dentry *); + static int jffs2_symlink (struct inode *,struct dentry *,const char *); + static int jffs2_mkdir (struct inode *,struct dentry *,int); + static int jffs2_rmdir (struct inode *,struct dentry *); +-static int jffs2_mknod (struct inode *,struct dentry *,int,int); ++static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t); + static int jffs2_rename (struct inode *, struct dentry *, + struct inode *, struct dentry *); + + struct file_operations jffs2_dir_operations = + { +- read: generic_read_dir, +- readdir: jffs2_readdir, +- ioctl: jffs2_ioctl, +- fsync: jffs2_null_fsync ++ .read = generic_read_dir, ++ .readdir = jffs2_readdir, ++ .ioctl = jffs2_ioctl, ++ .fsync = jffs2_fsync + }; + + + struct inode_operations jffs2_dir_inode_operations = + { +- create: jffs2_create, +- lookup: jffs2_lookup, +- link: jffs2_link, +- unlink: jffs2_unlink, +- symlink: jffs2_symlink, +- mkdir: jffs2_mkdir, +- rmdir: jffs2_rmdir, +- mknod: jffs2_mknod, +- rename: jffs2_rename, +- setattr: jffs2_setattr, ++ .create = NAMEI_COMPAT(jffs2_create), ++ .lookup = NAMEI_COMPAT(jffs2_lookup), ++ .link = jffs2_link, ++ .unlink = jffs2_unlink, ++ .symlink = jffs2_symlink, ++ .mkdir = jffs2_mkdir, ++ .rmdir = jffs2_rmdir, ++ .mknod = jffs2_mknod, ++ .rename = jffs2_rename, ++ .setattr = jffs2_setattr, + }; + + /***********************************************************************/ +@@ -88,12 +77,13 @@ + and we use the same hash function as the dentries. Makes this + nice and simple + */ +-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target) ++static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, ++ struct nameidata *nd) + { + struct jffs2_inode_info *dir_f; + struct jffs2_sb_info *c; + struct jffs2_full_dirent *fd = NULL, *fd_list; +- __u32 ino = 0; ++ uint32_t ino = 0; + struct inode *inode = NULL; + + D1(printk(KERN_DEBUG "jffs2_lookup()\n")); +@@ -153,8 +143,9 @@ + offset++; + } + if (offset == 1) { +- D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino)); +- if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) ++ unsigned long pino = parent_ino(filp->f_dentry); ++ D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino)); ++ if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0) + goto out; + offset++; + } +@@ -188,18 +179,14 @@ + + /***********************************************************************/ + +-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode) ++ ++static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, ++ struct nameidata *nd) + { ++ struct jffs2_raw_inode *ri; + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; + struct inode *inode; +- struct jffs2_raw_inode *ri; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dnode *fn; +- struct jffs2_full_dirent *fd; +- int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; + int ret; + + ri = jffs2_alloc_raw_inode(); +@@ -210,23 +197,11 @@ + + D1(printk(KERN_DEBUG "jffs2_create()\n")); + +- /* Try to reserve enough space for both node and dirent. +- * Just the node will do for now, though +- */ +- namelen = dentry->d_name.len; +- ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); +- D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen)); +- if (ret) { +- jffs2_free_raw_inode(ri); +- return ret; +- } +- + inode = jffs2_new_inode(dir_i, mode, ri); + + if (IS_ERR(inode)) { + D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); + jffs2_free_raw_inode(ri); +- jffs2_complete_reservation(c); + return PTR_ERR(inode); + } + +@@ -236,93 +211,22 @@ + inode->i_mapping->nrpages = 0; + + f = JFFS2_INODE_INFO(inode); ++ dir_f = JFFS2_INODE_INFO(dir_i); + +- ri->data_crc = 0; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- +- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen); +- D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode)); +- jffs2_free_raw_inode(ri); +- +- if (IS_ERR(fn)) { +- D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); +- /* Eeek. Wave bye bye */ +- up(&f->sem); +- jffs2_complete_reservation(c); +- jffs2_clear_inode(inode); +- return PTR_ERR(fn); +- } +- /* No data here. Only a metadata node, which will be +- obsoleted by the first data write +- */ +- f->metadata = fn; +- +- /* Work out where to put the dirent node now. */ +- writtenlen = PAD(writtenlen); +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- up(&f->sem); +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ +- jffs2_complete_reservation(c); +- ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ ret = jffs2_do_create(c, dir_f, f, ri, ++ dentry->d_name.name, dentry->d_name.len); + + if (ret) { +- /* Eep. */ +- D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); + jffs2_clear_inode(inode); ++ make_bad_inode(inode); ++ iput(inode); ++ jffs2_free_raw_inode(ri); + return ret; + } +- } + +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) { +- /* Argh. Now we treat it like a normal delete */ +- jffs2_complete_reservation(c); +- jffs2_clear_inode(inode); +- return -ENOMEM; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; +- rd->nsize = namelen; +- rd->type = DT_REG; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); +- +- if (IS_ERR(fd)) { +- /* dirent failed to write. Delete the inode normally +- as if it were the final unlink() */ +- jffs2_free_raw_dirent(rd); +- up(&dir_f->sem); +- jffs2_clear_inode(inode); +- return PTR_ERR(fd); +- } +- +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; +- +- jffs2_free_raw_dirent(rd); +- +- /* Link the fd into the inode's list, obsoleting an old +- one if necessary. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); + ++ jffs2_free_raw_inode(ri); + d_instantiate(dentry, inode); + + D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", +@@ -332,173 +236,48 @@ + + /***********************************************************************/ + +-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename) +-{ +- struct jffs2_inode_info *dir_f, *f; +- struct jffs2_sb_info *c; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dirent *fd; +- __u32 alloclen, phys_ofs; +- int ret; +- +- c = JFFS2_SB_INFO(dir_i->i_sb); +- +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) +- return -ENOMEM; +- +- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION); +- if (ret) { +- jffs2_free_raw_dirent(rd); +- return ret; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- /* Build a deletion node */ +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + dentry->d_name.len; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = 0; +- rd->mctime = CURRENT_TIME; +- rd->nsize = dentry->d_name.len; +- rd->type = DT_UNKNOWN; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL); +- +- jffs2_complete_reservation(c); +- jffs2_free_raw_dirent(rd); +- +- if (IS_ERR(fd)) { +- up(&dir_f->sem); +- return PTR_ERR(fd); +- } +- +- /* File it. This will mark the old one obsolete. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); +- +- if (!rename) { +- f = JFFS2_INODE_INFO(dentry->d_inode); +- down(&f->sem); +- +- while (f->dents) { +- /* There can be only deleted ones */ +- fd = f->dents; +- +- f->dents = fd->next; +- +- if (fd->ino) { +- printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", +- f->inocache->ino, fd->name, fd->ino); +- } else { +- D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino)); +- } +- jffs2_mark_node_obsolete(c, fd->raw); +- jffs2_free_full_dirent(fd); +- } +- /* Don't oops on unlinking a bad inode */ +- if (f->inocache) +- f->inocache->nlink--; +- dentry->d_inode->i_nlink--; +- up(&f->sem); +- } +- +- return 0; +-} + + static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry) + { +- return jffs2_do_unlink(dir_i, dentry, 0); +-} +-/***********************************************************************/ +- +-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename) +-{ +- struct jffs2_inode_info *dir_f, *f; +- struct jffs2_sb_info *c; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dirent *fd; +- __u32 alloclen, phys_ofs; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb); ++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); ++ struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode); + int ret; + +- c = JFFS2_SB_INFO(dir_i->i_sb); +- +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) +- return -ENOMEM; +- +- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- jffs2_free_raw_dirent(rd); ++ ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, ++ dentry->d_name.len, dead_f); ++ if (dead_f->inocache) ++ dentry->d_inode->i_nlink = dead_f->inocache->nlink; + return ret; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- /* Build a deletion node */ +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + dentry->d_name.len; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = old_dentry->d_inode->i_ino; +- rd->mctime = CURRENT_TIME; +- rd->nsize = dentry->d_name.len; +- +- /* XXX: This is ugly. */ +- rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; +- if (!rd->type) rd->type = DT_REG; +- +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL); +- +- jffs2_complete_reservation(c); +- jffs2_free_raw_dirent(rd); +- +- if (IS_ERR(fd)) { +- up(&dir_f->sem); +- return PTR_ERR(fd); +- } +- +- /* File it. This will mark the old one obsolete. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); +- +- if (!rename) { +- f = JFFS2_INODE_INFO(old_dentry->d_inode); +- down(&f->sem); +- old_dentry->d_inode->i_nlink = ++f->inocache->nlink; +- up(&f->sem); +- } +- return 0; + } ++/***********************************************************************/ ++ + + static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) + { ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb); ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); ++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); + int ret; ++ uint8_t type; + +- /* Can't link a bad inode. */ +- if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache) ++ /* Don't let people make hard links to bad inodes. */ ++ if (!f->inocache) + return -EIO; + + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + +- ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); ++ /* XXX: This is ugly */ ++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; ++ if (!type) type = DT_REG; ++ ++ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len); ++ + if (!ret) { ++ down(&f->sem); ++ old_dentry->d_inode->i_nlink = ++f->inocache->nlink; ++ up(&f->sem); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + } +@@ -517,8 +296,7 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + + /* FIXME: If you care. We'd need to use frags for the target +@@ -556,15 +334,16 @@ + + f = JFFS2_INODE_INFO(inode); + +- inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target); +- ri->totlen = sizeof(*ri) + ri->dsize; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); ++ inode->i_size = strlen(target); ++ ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->compr = JFFS2_COMPR_NONE; +- ri->data_crc = crc32(0, target, strlen(target)); +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target))); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -581,13 +360,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = (writtenlen+3)&~3; +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -595,7 +367,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -608,41 +379,42 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_LNK; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + return 0; +@@ -659,8 +431,7 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + + mode |= S_IFDIR; +@@ -692,13 +463,15 @@ + + inode->i_op = &jffs2_dir_inode_operations; + inode->i_fop = &jffs2_dir_operations; ++ /* Directories get nlink 2 at start */ ++ inode->i_nlink = 2; + + f = JFFS2_INODE_INFO(inode); + +- ri->data_crc = 0; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(0); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -715,13 +488,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = PAD(writtenlen); +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -729,7 +495,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -742,41 +507,43 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_DIR; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); ++ dir_i->i_nlink++; + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + return 0; +@@ -786,15 +553,19 @@ + { + struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); + struct jffs2_full_dirent *fd; ++ int ret; + + for (fd = f->dents ; fd; fd = fd->next) { + if (fd->ino) + return -ENOTEMPTY; + } +- return jffs2_unlink(dir_i, dentry); ++ ret = jffs2_unlink(dir_i, dentry); ++ if (!ret) ++ dir_i->i_nlink--; ++ return ret; + } + +-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev) ++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev) + { + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; +@@ -804,12 +575,14 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- unsigned short dev; ++ jint16_t dev; + int devlen = 0; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + ++ if (!old_valid_dev(rdev)) ++ return -EINVAL; ++ + ri = jffs2_alloc_raw_inode(); + if (!ri) + return -ENOMEM; +@@ -817,7 +590,7 @@ + c = JFFS2_SB_INFO(dir_i->i_sb); + + if (S_ISBLK(mode) || S_ISCHR(mode)) { +- dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev)); ++ dev = cpu_to_je16(old_encode_dev(rdev)); + devlen = sizeof(dev); + } + +@@ -844,15 +617,15 @@ + + f = JFFS2_INODE_INFO(inode); + +- ri->dsize = ri->csize = devlen; +- ri->totlen = sizeof(*ri) + ri->csize; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); ++ ri->dsize = ri->csize = cpu_to_je32(devlen); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + devlen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->compr = JFFS2_COMPR_NONE; +- ri->data_crc = crc32(0, &dev, devlen); +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -869,13 +642,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = (writtenlen+3)&~3; +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -883,7 +649,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -896,44 +661,45 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + + /* XXX: This is ugly. */ + rd->type = (mode & S_IFMT) >> 12; + +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + +@@ -944,7 +710,9 @@ + struct inode *new_dir_i, struct dentry *new_dentry) + { + int ret; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; ++ uint8_t type; + + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, +@@ -973,7 +741,15 @@ + */ + + /* Make a hard link */ +- ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1); ++ ++ /* XXX: This is ugly */ ++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; ++ if (!type) type = DT_REG; ++ ++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), ++ old_dentry->d_inode->i_ino, type, ++ new_dentry->d_name.name, new_dentry->d_name.len); ++ + if (ret) + return ret; + +@@ -989,22 +765,36 @@ + } + } + ++ /* If it was a directory we moved, and there was no victim, ++ increase i_nlink on its new parent */ ++ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f) ++ new_dir_i->i_nlink++; ++ + /* Unlink the original */ +- ret = jffs2_do_unlink(old_dir_i, old_dentry, 1); ++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), ++ old_dentry->d_name.name, old_dentry->d_name.len, NULL); ++ ++ /* We don't touch inode->i_nlink */ + + if (ret) { + /* Oh shit. We really ought to make a single node which can do both atomically */ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); + down(&f->sem); ++ old_dentry->d_inode->i_nlink++; + if (f->inocache) +- old_dentry->d_inode->i_nlink = f->inocache->nlink++; ++ f->inocache->nlink++; + up(&f->sem); + + printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); + /* Might as well let the VFS know */ + d_instantiate(new_dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); +- } + return ret; ++ } ++ ++ if (S_ISDIR(old_dentry->d_inode->i_mode)) ++ old_dir_i->i_nlink--; ++ ++ return 0; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/erase.c linux/fs/jffs2/erase.c +--- linux-mips-2.4.27/fs/jffs2/erase.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/erase.c 2004-11-19 10:25:12.099169392 +0100 +@@ -1,68 +1,60 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: erase.c,v 1.24.2.1 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $ + * + */ ++ + #include + #include + #include +-#include +-#include +-#include "nodelist.h" ++#include + #include ++#include ++#include ++#include "nodelist.h" + + struct erase_priv_struct { + struct jffs2_eraseblock *jeb; + struct jffs2_sb_info *c; + }; + ++#ifndef __ECOS + static void jffs2_erase_callback(struct erase_info *); ++#endif ++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); + static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); + + void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + { +- struct erase_info *instr; + int ret; ++#ifdef __ECOS ++ ret = jffs2_flash_erase(c, jeb); ++ if (!ret) { ++ jffs2_erase_succeeded(c, jeb); ++ return; ++ } ++#else /* Linux */ ++ struct erase_info *instr; + + instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); + if (!instr) { + printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); ++ c->dirty_size += c->sector_size; ++ jeb->dirty_size = c->sector_size; ++ spin_unlock(&c->erase_completion_lock); + return; + } + +@@ -77,19 +69,27 @@ + ((struct erase_priv_struct *)instr->priv)->jeb = jeb; + ((struct erase_priv_struct *)instr->priv)->c = c; + ++ /* NAND , read out the fail counter, if possible */ ++ if (!jffs2_can_mark_obsolete(c)) ++ jffs2_nand_read_failcnt(c,jeb); ++ + ret = c->mtd->erase(c->mtd, instr); +- if (!ret) { ++ if (!ret) + return; +- } ++ ++ kfree(instr); ++#endif /* __ECOS */ ++ + if (ret == -ENOMEM || ret == -EAGAIN) { + /* Erase failed immediately. Refile it on the list */ + D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); +- kfree(instr); ++ c->dirty_size += c->sector_size; ++ jeb->dirty_size = c->sector_size; ++ spin_unlock(&c->erase_completion_lock); + return; + } + +@@ -97,74 +97,101 @@ + printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset); + else + printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); +- spin_lock_bh(&c->erase_completion_lock); +- list_del(&jeb->list); +- list_add(&jeb->list, &c->bad_list); +- c->nr_erasing_blocks--; +- c->bad_size += c->sector_size; +- c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); +- wake_up(&c->erase_wait); +- kfree(instr); ++ ++ jffs2_erase_failed(c, jeb); + } + +-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) ++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) + { + struct jffs2_eraseblock *jeb; + +- spin_lock_bh(&c->erase_completion_lock); +- while (!list_empty(&c->erase_pending_list)) { ++ down(&c->erase_free_sem); + +- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); ++ spin_lock(&c->erase_completion_lock); + +- D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); ++ while (!list_empty(&c->erase_complete_list) || ++ !list_empty(&c->erase_pending_list)) { + ++ if (!list_empty(&c->erase_complete_list)) { ++ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); ++ list_del(&jeb->list); ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_mark_erased_block(c, jeb); ++ ++ if (!--count) { ++ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); ++ goto done; ++ } ++ ++ } else if (!list_empty(&c->erase_pending_list)) { ++ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); ++ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); + list_del(&jeb->list); + c->erasing_size += c->sector_size; ++ c->wasted_size -= jeb->wasted_size; + c->free_size -= jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; +- jeb->used_size = jeb->dirty_size = jeb->free_size = 0; ++ jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + jffs2_free_all_node_refs(c, jeb); + list_add(&jeb->list, &c->erasing_list); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + + jffs2_erase_block(c, jeb); ++ ++ } else { ++ BUG(); ++ } ++ + /* Be nice */ +- if (current->need_resched) +- schedule(); +- spin_lock_bh(&c->erase_completion_lock); ++ cond_resched(); ++ spin_lock(&c->erase_completion_lock); + } +- spin_unlock_bh(&c->erase_completion_lock); ++ ++ spin_unlock(&c->erase_completion_lock); ++ done: + D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); ++ ++ up(&c->erase_free_sem); ++} ++ ++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); ++ spin_lock(&c->erase_completion_lock); ++ list_del(&jeb->list); ++ list_add_tail(&jeb->list, &c->erase_complete_list); ++ spin_unlock(&c->erase_completion_lock); ++ /* Ensure that kupdated calls us again to mark them clean */ ++ jffs2_erase_pending_trigger(c); + } + ++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ spin_lock(&c->erase_completion_lock); ++ c->erasing_size -= c->sector_size; ++ c->bad_size += c->sector_size; ++ list_del(&jeb->list); ++ list_add(&jeb->list, &c->bad_list); ++ c->nr_erasing_blocks--; ++ spin_unlock(&c->erase_completion_lock); ++ wake_up(&c->erase_wait); ++} + ++#ifndef __ECOS + static void jffs2_erase_callback(struct erase_info *instr) + { + struct erase_priv_struct *priv = (void *)instr->priv; + + if(instr->state != MTD_ERASE_DONE) { + printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); +- spin_lock(&priv->c->erase_completion_lock); +- priv->c->erasing_size -= priv->c->sector_size; +- priv->c->bad_size += priv->c->sector_size; +- list_del(&priv->jeb->list); +- list_add(&priv->jeb->list, &priv->c->bad_list); +- priv->c->nr_erasing_blocks--; +- spin_unlock(&priv->c->erase_completion_lock); +- wake_up(&priv->c->erase_wait); ++ jffs2_erase_failed(priv->c, priv->jeb); + } else { +- D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr)); +- spin_lock(&priv->c->erase_completion_lock); +- list_del(&priv->jeb->list); +- list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list); +- spin_unlock(&priv->c->erase_completion_lock); ++ jffs2_erase_succeeded(priv->c, priv->jeb); + } +- /* Make sure someone picks up the block off the erase_complete list */ +- OFNI_BS_2SFFJ(priv->c)->s_dirt = 1; + kfree(instr); + } ++#endif /* !__ECOS */ + + /* Hmmm. Maybe we should accept the extra space it takes and make + this a standard doubly-linked list? */ +@@ -221,7 +248,7 @@ + this = ic->nodes; + + while(this) { +- printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3); ++ printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this)); + if (++i == 5) { + printk("\n" KERN_DEBUG); + i=0; +@@ -256,54 +283,43 @@ + jeb->last_node = NULL; + } + +-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) ++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + { +- OFNI_BS_2SFFJ(c)->s_dirt = 1; +-} +- +-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) +-{ +- static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)}; +- struct jffs2_eraseblock *jeb; +- struct jffs2_raw_node_ref *marker_ref; ++ struct jffs2_raw_node_ref *marker_ref = NULL; + unsigned char *ebuf; +- ssize_t retlen; ++ size_t retlen; + int ret; + +- marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4); +- +- spin_lock_bh(&c->erase_completion_lock); +- while (!list_empty(&c->erase_complete_list)) { +- jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); +- list_del(&jeb->list); +- spin_unlock_bh(&c->erase_completion_lock); +- ++ if (!jffs2_cleanmarker_oob(c)) { + marker_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { + printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n"); +- /* Come back later */ ++ /* Stick it back on the list from whence it came and come back later */ + jffs2_erase_pending_trigger(c); ++ spin_lock(&c->erase_completion_lock); ++ list_add(&jeb->list, &c->erase_complete_list); ++ spin_unlock(&c->erase_completion_lock); + return; + } +- ++ } + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset); + } else { +- __u32 ofs = jeb->offset; ++ uint32_t ofs = jeb->offset; + + D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); + while(ofs < jeb->offset + c->sector_size) { +- __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs); ++ uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; + +- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); +- if (ret < 0) { ++ ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); ++ if (ret) { + printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + goto bad; + } + if (retlen != readlen) { +- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen); ++ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto bad; + } + for (i=0; ierase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; + + list_add_tail(&jeb->list, &c->bad_list); + c->nr_erasing_blocks--; +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); + return; + } + } + ofs += readlen; ++ cond_resched(); + } + kfree(ebuf); + } + + /* Write the erase complete marker */ + D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); +- ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker); ++ if (jffs2_cleanmarker_oob(c)) { ++ ++ if (jffs2_write_nand_cleanmarker(c, jeb)) ++ goto bad2; ++ ++ jeb->first_node = jeb->last_node = NULL; ++ ++ jeb->free_size = c->sector_size; ++ jeb->used_size = 0; ++ jeb->dirty_size = 0; ++ jeb->wasted_size = 0; ++ } else { ++ struct jffs2_unknown_node marker = { ++ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), ++ .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), ++ .totlen = cpu_to_je32(c->cleanmarker_size) ++ }; ++ ++ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); ++ ++ /* We only write the header; the rest was noise or padding anyway */ ++ ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + if (ret) { + printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", + jeb->offset, ret); + goto bad2; + } + if (retlen != sizeof(marker)) { +- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n", ++ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", + jeb->offset, sizeof(marker), retlen); + goto bad2; + } + + marker_ref->next_in_ino = NULL; + marker_ref->next_phys = NULL; +- marker_ref->flash_offset = jeb->offset; +- marker_ref->totlen = PAD(sizeof(marker)); ++ marker_ref->flash_offset = jeb->offset | REF_NORMAL; ++ marker_ref->__totlen = c->cleanmarker_size; + + jeb->first_node = jeb->last_node = marker_ref; + +- jeb->free_size = c->sector_size - marker_ref->totlen; +- jeb->used_size = marker_ref->totlen; ++ jeb->free_size = c->sector_size - c->cleanmarker_size; ++ jeb->used_size = c->cleanmarker_size; + jeb->dirty_size = 0; ++ jeb->wasted_size = 0; ++ } + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->free_size += jeb->free_size; + c->used_size += jeb->used_size; + + ACCT_SANITY_CHECK(c,jeb); +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + + list_add_tail(&jeb->list, &c->free_list); + c->nr_erasing_blocks--; + c->nr_free_blocks++; ++ spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); +- } +- spin_unlock_bh(&c->erase_completion_lock); + } ++ +diff -Nurb linux-mips-2.4.27/fs/jffs2/file.c linux/fs/jffs2/file.c +--- linux-mips-2.4.27/fs/jffs2/file.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/file.c 2004-11-19 10:25:12.101169088 +0100 +@@ -1,319 +1,106 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: file.c,v 1.58.2.7 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: file.c,v 1.97 2003/11/02 08:52:35 dwmw2 Exp $ + * + */ + ++#include + #include +-#include /* for min() */ + #include + #include ++#include + #include ++#include ++#include + #include + #include "nodelist.h" +-#include + + extern int generic_file_open(struct inode *, struct file *) __attribute__((weak)); + extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak)); + + +-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync) ++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) + { +- /* Move along. Nothing to see here */ ++ struct inode *inode = dentry->d_inode; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ ++ /* Trigger GC to flush any pending writes for this inode */ ++ jffs2_flush_wbuf_gc(c, inode->i_ino); ++ + return 0; + } + + struct file_operations jffs2_file_operations = + { +- llseek: generic_file_llseek, +- open: generic_file_open, +- read: generic_file_read, +- write: generic_file_write, +- ioctl: jffs2_ioctl, +- mmap: generic_file_mmap, +- fsync: jffs2_null_fsync ++ .llseek = generic_file_llseek, ++ .open = generic_file_open, ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .ioctl = jffs2_ioctl, ++ .mmap = generic_file_readonly_mmap, ++ .fsync = jffs2_fsync, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29) ++ .sendfile = generic_file_sendfile ++#endif + }; + + /* jffs2_file_inode_operations */ + + struct inode_operations jffs2_file_inode_operations = + { +- setattr: jffs2_setattr ++ .setattr = jffs2_setattr + }; + + struct address_space_operations jffs2_file_address_operations = + { +- readpage: jffs2_readpage, +- prepare_write: jffs2_prepare_write, +- commit_write: jffs2_commit_write ++ .readpage = jffs2_readpage, ++ .prepare_write =jffs2_prepare_write, ++ .commit_write = jffs2_commit_write + }; + +-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) +-{ +- struct jffs2_full_dnode *old_metadata, *new_metadata; +- struct inode *inode = dentry->d_inode; +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_raw_inode *ri; +- unsigned short dev; +- unsigned char *mdata = NULL; +- int mdatalen = 0; +- unsigned int ivalid; +- __u32 phys_ofs, alloclen; +- int ret; +- D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); +- ret = inode_change_ok(inode, iattr); +- if (ret) +- return ret; +- +- /* Special cases - we don't want more than one data node +- for these types on the medium at any time. So setattr +- must read the original data associated with the node +- (i.e. the device numbers or the target name) and write +- it out again with the appropriate data attached */ +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { +- /* For these, we don't actually need to read the old node */ +- dev = (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) | +- MINOR(to_kdev_t(dentry->d_inode->i_rdev)); +- mdata = (char *)&dev; +- mdatalen = sizeof(dev); +- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); +- } else if (S_ISLNK(inode->i_mode)) { +- mdatalen = f->metadata->size; +- mdata = kmalloc(f->metadata->size, GFP_USER); +- if (!mdata) +- return -ENOMEM; +- ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); +- if (ret) { +- kfree(mdata); +- return ret; +- } +- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); +- } +- +- ri = jffs2_alloc_raw_inode(); +- if (!ri) { +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- return -ENOMEM; +- } +- +- ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- jffs2_free_raw_inode(ri); +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- return ret; +- } +- down(&f->sem); +- ivalid = iattr->ia_valid; +- +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = sizeof(*ri) + mdatalen; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri->ino = inode->i_ino; +- ri->version = ++f->highest_version; +- +- ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode; +- ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid; +- ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid; +- +- if (ivalid & ATTR_MODE && ri->mode & S_ISGID && +- !in_group_p(ri->gid) && !capable(CAP_FSETID)) +- ri->mode &= ~S_ISGID; +- +- ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size; +- ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime; +- ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime; +- ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime; +- +- ri->offset = 0; +- ri->csize = ri->dsize = mdatalen; +- ri->compr = JFFS2_COMPR_NONE; +- if (inode->i_size < ri->isize) { +- /* It's an extension. Make it a hole node */ +- ri->compr = JFFS2_COMPR_ZERO; +- ri->dsize = ri->isize - inode->i_size; +- ri->offset = inode->i_size; +- } +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- if (mdatalen) +- ri->data_crc = crc32(0, mdata, mdatalen); +- else +- ri->data_crc = 0; +- +- new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL); +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- +- jffs2_complete_reservation(c); +- +- if (IS_ERR(new_metadata)) { +- jffs2_free_raw_inode(ri); +- up(&f->sem); +- return PTR_ERR(new_metadata); +- } +- /* It worked. Update the inode */ +- inode->i_atime = ri->atime; +- inode->i_ctime = ri->ctime; +- inode->i_mtime = ri->mtime; +- inode->i_mode = ri->mode; +- inode->i_uid = ri->uid; +- inode->i_gid = ri->gid; +- +- +- old_metadata = f->metadata; +- +- if (inode->i_size > ri->isize) { +- vmtruncate(inode, ri->isize); +- jffs2_truncate_fraglist (c, &f->fraglist, ri->isize); +- } +- +- if (inode->i_size < ri->isize) { +- jffs2_add_full_dnode_to_inode(c, f, new_metadata); +- inode->i_size = ri->isize; +- f->metadata = NULL; +- } else { +- f->metadata = new_metadata; +- } +- if (old_metadata) { +- jffs2_mark_node_obsolete(c, old_metadata->raw); +- jffs2_free_full_dnode(old_metadata); +- } +- jffs2_free_raw_inode(ri); +- up(&f->sem); +- return 0; +-} +- + int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) + { + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_node_frag *frag = f->fraglist; +- __u32 offset = pg->index << PAGE_CACHE_SHIFT; +- __u32 end = offset + PAGE_CACHE_SIZE; + unsigned char *pg_buf; + int ret; + +- D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset)); ++ D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); + + if (!PageLocked(pg)) + PAGE_BUG(pg); + +- while(frag && frag->ofs + frag->size <= offset) { +- // D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size)); +- frag = frag->next; +- } +- + pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ + +- /* XXX FIXME: Where a single physical node actually shows up in two +- frags, we read it twice. Don't do that. */ +- /* Now we're pointing at the first frag which overlaps our page */ +- while(offset < end) { +- D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end)); +- if (!frag || frag->ofs > offset) { +- __u32 holesize = end - offset; +- if (frag) { +- D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset)); +- holesize = min(holesize, frag->ofs - offset); +- D1(jffs2_print_frag_list(f)); +- } +- D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); +- memset(pg_buf, 0, holesize); +- pg_buf += holesize; +- offset += holesize; +- continue; +- } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) { +- D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", +- inode->i_ino, frag->ofs, offset)); +- D1(jffs2_print_frag_list(f)); +- memset(pg_buf, 0, end - offset); +- ClearPageUptodate(pg); +- SetPageError(pg); +- kunmap(pg); +- return -EIO; +- } else if (!frag->node) { +- __u32 holeend = min(end, frag->ofs + frag->size); +- D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); +- memset(pg_buf, 0, holeend - offset); +- pg_buf += holeend - offset; +- offset = holeend; +- frag = frag->next; +- continue; +- } else { +- __u32 readlen; +- __u32 fragofs; /* offset within the frag to start reading */ ++ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); + +- fragofs = offset - frag->ofs; +- readlen = min(frag->size - fragofs, end - offset); +- D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs, +- fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3)); +- ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen); +- D2(printk(KERN_DEBUG "node read done\n")); + if (ret) { +- D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret)); +- memset(pg_buf, 0, readlen); + ClearPageUptodate(pg); + SetPageError(pg); +- kunmap(pg); +- return ret; +- } +- +- pg_buf += readlen; +- offset += readlen; +- frag = frag->next; +- D2(printk(KERN_DEBUG "node read was OK. Looping\n")); +- } +- } +- D2(printk(KERN_DEBUG "readpage finishing\n")); ++ } else { + SetPageUptodate(pg); + ClearPageError(pg); ++ } + + flush_dcache_page(pg); +- + kunmap(pg); +- D1(printk(KERN_DEBUG "readpage finished\n")); ++ ++ D2(printk(KERN_DEBUG "readpage finished\n")); + return 0; + } + + int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg) + { + int ret = jffs2_do_readpage_nolock(inode, pg); +- UnlockPage(pg); ++ unlock_page(pg); + return ret; + } + +@@ -333,17 +120,17 @@ + { + struct inode *inode = pg->mapping->host; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +- __u32 pageofs = pg->index << PAGE_CACHE_SHIFT; ++ uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; + int ret = 0; + +- D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages)); ++ D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); + + if (pageofs > inode->i_size) { + /* Make new hole frag from old EOF to new page */ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_raw_inode ri; + struct jffs2_full_dnode *fn; +- __u32 phys_ofs, alloc_len; ++ uint32_t phys_ofs, alloc_len; + + D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", + (unsigned int)inode->i_size, pageofs)); +@@ -355,29 +142,30 @@ + down(&f->sem); + memset(&ri, 0, sizeof(ri)); + +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri); +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = f->inocache->ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = max((__u32)inode->i_size, pageofs); +- ri.atime = ri.ctime = ri.mtime = CURRENT_TIME; +- ri.offset = inode->i_size; +- ri.dsize = pageofs - inode->i_size; +- ri.csize = 0; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(inode->i_mode); ++ ri.uid = cpu_to_je16(inode->i_uid); ++ ri.gid = cpu_to_je16(inode->i_gid); ++ ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); ++ ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); ++ ri.offset = cpu_to_je32(inode->i_size); ++ ri.dsize = cpu_to_je32(pageofs - inode->i_size); ++ ri.csize = cpu_to_je32(0); + ri.compr = JFFS2_COMPR_ZERO; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = 0; ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(0); ++ ++ fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + +- fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL); +- jffs2_complete_reservation(c); + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); ++ jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } +@@ -391,16 +179,17 @@ + D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); ++ jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } ++ jffs2_complete_reservation(c); + inode->i_size = pageofs; + up(&f->sem); + } + +- + /* Read in the page if it wasn't already present, unless it's a whole page */ +- if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { ++ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { + down(&f->sem); + ret = jffs2_do_readpage_nolock(inode, pg); + up(&f->sem); +@@ -417,14 +206,12 @@ + struct inode *inode = pg->mapping->host; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end); +- __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT); +- __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs); + struct jffs2_raw_inode *ri; + int ret = 0; +- ssize_t writtenlen = 0; ++ uint32_t writtenlen = 0; + +- D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); ++ D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", ++ inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); + + if (!start && end == PAGE_CACHE_SIZE) { + /* We need to avoid deadlock with page_cache_read() in +@@ -435,109 +222,47 @@ + } + + ri = jffs2_alloc_raw_inode(); +- if (!ri) +- return -ENOMEM; +- +- while(writelen) { +- struct jffs2_full_dnode *fn; +- unsigned char *comprbuf = NULL; +- unsigned char comprtype = JFFS2_COMPR_NONE; +- __u32 phys_ofs, alloclen; +- __u32 datalen, cdatalen; + +- D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs)); +- +- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- SetPageError(pg); +- D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); +- break; ++ if (!ri) { ++ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); ++ return -ENOMEM; + } +- down(&f->sem); +- datalen = writelen; +- cdatalen = min(alloclen - sizeof(*ri), writelen); +- +- comprbuf = kmalloc(cdatalen, GFP_KERNEL); +- if (comprbuf) { +- comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen); +- } +- if (comprtype == JFFS2_COMPR_NONE) { +- /* Either compression failed, or the allocation of comprbuf failed */ +- if (comprbuf) +- kfree(comprbuf); +- comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1)); +- datalen = cdatalen; +- } +- /* Now comprbuf points to the data to be written, be it compressed or not. +- comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means +- that the comprbuf doesn't need to be kfree()d. +- */ +- +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = sizeof(*ri) + cdatalen; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri->ino = inode->i_ino; +- ri->version = ++f->highest_version; +- ri->mode = inode->i_mode; +- ri->uid = inode->i_uid; +- ri->gid = inode->i_gid; +- ri->isize = max((__u32)inode->i_size, file_ofs + datalen); +- ri->atime = ri->ctime = ri->mtime = CURRENT_TIME; +- ri->offset = file_ofs; +- ri->csize = cdatalen; +- ri->dsize = datalen; +- ri->compr = comprtype; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- ri->data_crc = crc32(0, comprbuf, cdatalen); +- +- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL); + +- jffs2_complete_reservation(c); ++ /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ++ ri->ino = cpu_to_je32(inode->i_ino); ++ ri->mode = cpu_to_jemode(inode->i_mode); ++ ri->uid = cpu_to_je16(inode->i_uid); ++ ri->gid = cpu_to_je16(inode->i_gid); ++ ri->isize = cpu_to_je32((uint32_t)inode->i_size); ++ ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); ++ ++ /* In 2.4, it was already kmapped by generic_file_write(). Doesn't ++ hurt to do it again. The alternative is ifdefs, which are ugly. */ ++ kmap(pg); ++ ++ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start, ++ (pg->index << PAGE_CACHE_SHIFT) + start, ++ end - start, &writtenlen); + +- if (comprtype != JFFS2_COMPR_NONE) +- kfree(comprbuf); ++ kunmap(pg); + +- if (IS_ERR(fn)) { +- ret = PTR_ERR(fn); +- up(&f->sem); +- SetPageError(pg); +- break; +- } +- ret = jffs2_add_full_dnode_to_inode(c, f, fn); +- if (f->metadata) { +- jffs2_mark_node_obsolete(c, f->metadata->raw); +- jffs2_free_full_dnode(f->metadata); +- f->metadata = NULL; +- } +- up(&f->sem); + if (ret) { +- /* Eep */ +- D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); +- jffs2_mark_node_obsolete(c, fn->raw); +- jffs2_free_full_dnode(fn); ++ /* There was an error writing. */ + SetPageError(pg); +- break; + } +- inode->i_size = ri->isize; ++ ++ if (writtenlen) { ++ if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { ++ inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; + inode->i_blocks = (inode->i_size + 511) >> 9; +- inode->i_ctime = inode->i_mtime = ri->ctime; +- if (!datalen) { +- printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n"); +- ret = -EIO; +- SetPageError(pg); +- break; ++ ++ inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); + } +- D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); +- writtenlen += datalen; +- file_ofs += datalen; +- writelen -= datalen; + } + + jffs2_free_raw_inode(ri); + +- if (writtenlen < end) { ++ if (start+writtenlen < end) { + /* generic_file_write has written more to the page cache than we've + actually written to the medium. Mark the page !Uptodate so that + it gets reread */ +@@ -545,13 +270,7 @@ + SetPageError(pg); + ClearPageUptodate(pg); + } +- if (writtenlen <= start) { +- /* We didn't even get to the start of the affected part */ +- ret = ret?ret:-ENOSPC; +- D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret)); +- } +- writtenlen = min(end-start, writtenlen-start); + +- D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages)); ++ D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret)); + return writtenlen?writtenlen:ret; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/fs.c linux/fs/jffs2/fs.c +--- linux-mips-2.4.27/fs/jffs2/fs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/fs.c 2004-11-19 10:25:12.102168936 +0100 +@@ -0,0 +1,618 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: fs.c,v 1.37 2004/01/26 12:34:21 dwmw2 Exp $ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "nodelist.h" ++ ++ ++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ++{ ++ struct jffs2_full_dnode *old_metadata, *new_metadata; ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_raw_inode *ri; ++ unsigned short dev; ++ unsigned char *mdata = NULL; ++ int mdatalen = 0; ++ unsigned int ivalid; ++ uint32_t phys_ofs, alloclen; ++ int ret; ++ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ++ ret = inode_change_ok(inode, iattr); ++ if (ret) ++ return ret; ++ ++ /* Special cases - we don't want more than one data node ++ for these types on the medium at any time. So setattr ++ must read the original data associated with the node ++ (i.e. the device numbers or the target name) and write ++ it out again with the appropriate data attached */ ++ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { ++ /* For these, we don't actually need to read the old node */ ++ dev = old_encode_dev(inode->i_rdev); ++ mdata = (char *)&dev; ++ mdatalen = sizeof(dev); ++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); ++ } else if (S_ISLNK(inode->i_mode)) { ++ mdatalen = f->metadata->size; ++ mdata = kmalloc(f->metadata->size, GFP_USER); ++ if (!mdata) ++ return -ENOMEM; ++ ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); ++ if (ret) { ++ kfree(mdata); ++ return ret; ++ } ++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); ++ } ++ ++ ri = jffs2_alloc_raw_inode(); ++ if (!ri) { ++ if (S_ISLNK(inode->i_mode)) ++ kfree(mdata); ++ return -ENOMEM; ++ } ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ jffs2_free_raw_inode(ri); ++ if (S_ISLNK(inode->i_mode & S_IFMT)) ++ kfree(mdata); ++ return ret; ++ } ++ down(&f->sem); ++ ivalid = iattr->ia_valid; ++ ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri->ino = cpu_to_je32(inode->i_ino); ++ ri->version = cpu_to_je32(++f->highest_version); ++ ++ ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid); ++ ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); ++ ++ if (ivalid & ATTR_MODE) ++ if (iattr->ia_mode & S_ISGID && ++ !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) ++ ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); ++ else ++ ri->mode = cpu_to_jemode(iattr->ia_mode); ++ else ++ ri->mode = cpu_to_jemode(inode->i_mode); ++ ++ ++ ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); ++ ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); ++ ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); ++ ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); ++ ++ ri->offset = cpu_to_je32(0); ++ ri->csize = ri->dsize = cpu_to_je32(mdatalen); ++ ri->compr = JFFS2_COMPR_NONE; ++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { ++ /* It's an extension. Make it a hole node */ ++ ri->compr = JFFS2_COMPR_ZERO; ++ ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ++ ri->offset = cpu_to_je32(inode->i_size); ++ } ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ if (mdatalen) ++ ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); ++ else ++ ri->data_crc = cpu_to_je32(0); ++ ++ new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); ++ if (S_ISLNK(inode->i_mode)) ++ kfree(mdata); ++ ++ if (IS_ERR(new_metadata)) { ++ jffs2_complete_reservation(c); ++ jffs2_free_raw_inode(ri); ++ up(&f->sem); ++ return PTR_ERR(new_metadata); ++ } ++ /* It worked. Update the inode */ ++ inode->i_atime = ITIME(je32_to_cpu(ri->atime)); ++ inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); ++ inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); ++ inode->i_mode = jemode_to_cpu(ri->mode); ++ inode->i_uid = je16_to_cpu(ri->uid); ++ inode->i_gid = je16_to_cpu(ri->gid); ++ ++ ++ old_metadata = f->metadata; ++ ++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) ++ jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size); ++ ++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { ++ jffs2_add_full_dnode_to_inode(c, f, new_metadata); ++ inode->i_size = iattr->ia_size; ++ f->metadata = NULL; ++ } else { ++ f->metadata = new_metadata; ++ } ++ if (old_metadata) { ++ jffs2_mark_node_obsolete(c, old_metadata->raw); ++ jffs2_free_full_dnode(old_metadata); ++ } ++ jffs2_free_raw_inode(ri); ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ /* We have to do the vmtruncate() without f->sem held, since ++ some pages may be locked and waiting for it in readpage(). ++ We are protected from a simultaneous write() extending i_size ++ back past iattr->ia_size, because do_truncate() holds the ++ generic inode semaphore. */ ++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) ++ vmtruncate(inode, iattr->ia_size); ++ ++ return 0; ++} ++ ++int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) ++{ ++ return jffs2_do_setattr(dentry->d_inode, iattr); ++} ++ ++int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ unsigned long avail; ++ ++ buf->f_type = JFFS2_SUPER_MAGIC; ++ buf->f_bsize = 1 << PAGE_SHIFT; ++ buf->f_blocks = c->flash_size >> PAGE_SHIFT; ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_namelen = JFFS2_MAX_NAME_LEN; ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ avail = c->dirty_size + c->free_size; ++ if (avail > c->sector_size * c->resv_blocks_write) ++ avail -= c->sector_size * c->resv_blocks_write; ++ else ++ avail = 0; ++ ++ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; ++ ++ D1(jffs2_dump_block_lists(c)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ return 0; ++} ++ ++ ++void jffs2_clear_inode (struct inode *inode) ++{ ++ /* We can forget about this inode for now - drop all ++ * the nodelists associated with it, etc. ++ */ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); ++ ++ D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); ++ ++ jffs2_do_clear_inode(c, f); ++} ++ ++void jffs2_read_inode (struct inode *inode) ++{ ++ struct jffs2_inode_info *f; ++ struct jffs2_sb_info *c; ++ struct jffs2_raw_inode latest_node; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); ++ ++ f = JFFS2_INODE_INFO(inode); ++ c = JFFS2_SB_INFO(inode->i_sb); ++ ++ jffs2_init_inode_info(f); ++ ++ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); ++ ++ if (ret) { ++ make_bad_inode(inode); ++ up(&f->sem); ++ return; ++ } ++ inode->i_mode = jemode_to_cpu(latest_node.mode); ++ inode->i_uid = je16_to_cpu(latest_node.uid); ++ inode->i_gid = je16_to_cpu(latest_node.gid); ++ inode->i_size = je32_to_cpu(latest_node.isize); ++ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); ++ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); ++ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); ++ ++ inode->i_nlink = f->inocache->nlink; ++ ++ inode->i_blksize = PAGE_SIZE; ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ switch (inode->i_mode & S_IFMT) { ++ jint16_t rdev; ++ ++ case S_IFLNK: ++ inode->i_op = &jffs2_symlink_inode_operations; ++ break; ++ ++ case S_IFDIR: ++ { ++ struct jffs2_full_dirent *fd; ++ ++ for (fd=f->dents; fd; fd = fd->next) { ++ if (fd->type == DT_DIR && fd->ino) ++ inode->i_nlink++; ++ } ++ /* and '..' */ ++ inode->i_nlink++; ++ /* Root dir gets i_nlink 3 for some reason */ ++ if (inode->i_ino == 1) ++ inode->i_nlink++; ++ ++ inode->i_op = &jffs2_dir_inode_operations; ++ inode->i_fop = &jffs2_dir_operations; ++ break; ++ } ++ case S_IFREG: ++ inode->i_op = &jffs2_file_inode_operations; ++ inode->i_fop = &jffs2_file_operations; ++ inode->i_mapping->a_ops = &jffs2_file_address_operations; ++ inode->i_mapping->nrpages = 0; ++ break; ++ ++ case S_IFBLK: ++ case S_IFCHR: ++ /* Read the device numbers from the media */ ++ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); ++ if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { ++ /* Eep */ ++ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ make_bad_inode(inode); ++ return; ++ } ++ ++ case S_IFSOCK: ++ case S_IFIFO: ++ inode->i_op = &jffs2_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, ++ old_decode_dev((je16_to_cpu(rdev)))); ++ break; ++ ++ default: ++ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); ++ } ++ ++ up(&f->sem); ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); ++} ++ ++void jffs2_dirty_inode(struct inode *inode) ++{ ++ struct iattr iattr; ++ ++ if (!(inode->i_state & I_DIRTY_DATASYNC)) { ++ D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); ++ return; ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino)); ++ ++ iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME; ++ iattr.ia_mode = inode->i_mode; ++ iattr.ia_uid = inode->i_uid; ++ iattr.ia_gid = inode->i_gid; ++ iattr.ia_atime = inode->i_atime; ++ iattr.ia_mtime = inode->i_mtime; ++ iattr.ia_ctime = inode->i_ctime; ++ ++ jffs2_do_setattr(inode, &iattr); ++} ++ ++int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ ++ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) ++ return -EROFS; ++ ++ /* We stop if it was running, then restart if it needs to. ++ This also catches the case where it was stopped and this ++ is just a remount to restart it */ ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_stop_garbage_collect_thread(c); ++ ++ if (!(*flags & MS_RDONLY)) ++ jffs2_start_garbage_collect_thread(c); ++ ++ sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY); ++ ++ return 0; ++} ++ ++void jffs2_write_super (struct super_block *sb) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ sb->s_dirt = 0; ++ ++ if (sb->s_flags & MS_RDONLY) ++ return; ++ ++ D1(printk(KERN_DEBUG "jffs2_write_super()\n")); ++ jffs2_garbage_collect_trigger(c); ++ jffs2_erase_pending_blocks(c, 0); ++ jffs2_flush_wbuf_gc(c, 0); ++} ++ ++ ++/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, ++ fill in the raw_inode while you're at it. */ ++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) ++{ ++ struct inode *inode; ++ struct super_block *sb = dir_i->i_sb; ++ struct jffs2_sb_info *c; ++ struct jffs2_inode_info *f; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); ++ ++ c = JFFS2_SB_INFO(sb); ++ ++ inode = new_inode(sb); ++ ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ f = JFFS2_INODE_INFO(inode); ++ jffs2_init_inode_info(f); ++ ++ memset(ri, 0, sizeof(*ri)); ++ /* Set OS-specific defaults for new inodes */ ++ ri->uid = cpu_to_je16(current->fsuid); ++ ++ if (dir_i->i_mode & S_ISGID) { ++ ri->gid = cpu_to_je16(dir_i->i_gid); ++ if (S_ISDIR(mode)) ++ mode |= S_ISGID; ++ } else { ++ ri->gid = cpu_to_je16(current->fsgid); ++ } ++ ri->mode = cpu_to_jemode(mode); ++ ret = jffs2_do_new_inode (c, f, mode, ri); ++ if (ret) { ++ make_bad_inode(inode); ++ iput(inode); ++ return ERR_PTR(ret); ++ } ++ inode->i_nlink = 1; ++ inode->i_ino = je32_to_cpu(ri->ino); ++ inode->i_mode = jemode_to_cpu(ri->mode); ++ inode->i_gid = je16_to_cpu(ri->gid); ++ inode->i_uid = je16_to_cpu(ri->uid); ++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; ++ ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); ++ ++ inode->i_blksize = PAGE_SIZE; ++ inode->i_blocks = 0; ++ inode->i_size = 0; ++ ++ insert_inode_hash(inode); ++ ++ return inode; ++} ++ ++ ++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct jffs2_sb_info *c; ++ struct inode *root_i; ++ int ret; ++ size_t blocks; ++ ++ c = JFFS2_SB_INFO(sb); ++ ++ c->flash_size = c->mtd->size; ++ ++ /* ++ * Check, if we have to concatenate physical blocks to larger virtual blocks ++ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation) ++ */ ++ blocks = c->flash_size / c->mtd->erasesize; ++ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) ++ blocks >>= 1; ++ ++ c->sector_size = c->flash_size / blocks; ++ if (c->sector_size != c->mtd->erasesize) ++ printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", ++ c->mtd->erasesize / 1024, c->sector_size / 1024); ++ ++ if (c->flash_size < 5*c->sector_size) { ++ printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); ++ return -EINVAL; ++ } ++ ++ c->cleanmarker_size = sizeof(struct jffs2_unknown_node); ++ /* Joern -- stick alignment for weird 8-byte-page flash here */ ++ ++ if (jffs2_cleanmarker_oob(c)) { ++ /* NAND (or other bizarre) flash... do setup accordingly */ ++ ret = jffs2_nand_flash_setup(c); ++ if (ret) ++ return ret; ++ } ++ ++ c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); ++ if (!c->inocache_list) { ++ ret = -ENOMEM; ++ goto out_wbuf; ++ } ++ memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); ++ ++ if ((ret = jffs2_do_mount_fs(c))) ++ goto out_inohash; ++ ++ ret = -EINVAL; ++ ++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); ++ root_i = iget(sb, 1); ++ if (is_bad_inode(root_i)) { ++ D1(printk(KERN_WARNING "get root inode failed\n")); ++ goto out_nodes; ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); ++ sb->s_root = d_alloc_root(root_i); ++ if (!sb->s_root) ++ goto out_root_i; ++ ++#if LINUX_VERSION_CODE >= 0x20403 ++ sb->s_maxbytes = 0xFFFFFFFF; ++#endif ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = JFFS2_SUPER_MAGIC; ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_start_garbage_collect_thread(c); ++ return 0; ++ ++ out_root_i: ++ iput(root_i); ++ out_nodes: ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ out_inohash: ++ kfree(c->inocache_list); ++ out_wbuf: ++ jffs2_nand_flash_cleanup(c); ++ ++ return ret; ++} ++ ++void jffs2_gc_release_inode(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f) ++{ ++ iput(OFNI_EDONI_2SFFJ(f)); ++} ++ ++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, ++ int inum, int nlink) ++{ ++ struct inode *inode; ++ struct jffs2_inode_cache *ic; ++ if (!nlink) { ++ /* The inode has zero nlink but its nodes weren't yet marked ++ obsolete. This has to be because we're still waiting for ++ the final (close() and) iput() to happen. ++ ++ There's a possibility that the final iput() could have ++ happened while we were contemplating. In order to ensure ++ that we don't cause a new read_inode() (which would fail) ++ for the inode in question, we use ilookup() in this case ++ instead of iget(). ++ ++ The nlink can't _become_ zero at this point because we're ++ holding the alloc_sem, and jffs2_do_unlink() would also ++ need that while decrementing nlink on any inode. ++ */ ++ inode = ilookup(OFNI_BS_2SFFJ(c), inum); ++ if (!inode) { ++ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", ++ inum)); ++ ++ spin_lock(&c->inocache_lock); ++ ic = jffs2_get_ino_cache(c, inum); ++ if (!ic) { ++ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); ++ spin_unlock(&c->inocache_lock); ++ return NULL; ++ } ++ if (ic->state != INO_STATE_CHECKEDABSENT) { ++ /* Wait for progress. Don't just loop */ ++ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", ++ ic->ino, ic->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ } else { ++ spin_unlock(&c->inocache_lock); ++ } ++ ++ return NULL; ++ } ++ } else { ++ /* Inode has links to it still; they're not going away because ++ jffs2_do_unlink() would need the alloc_sem and we have it. ++ Just iget() it, and if read_inode() is necessary that's OK. ++ */ ++ inode = iget(OFNI_BS_2SFFJ(c), inum); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ } ++ if (is_bad_inode(inode)) { ++ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", ++ inum, nlink); ++ /* NB. This will happen again. We need to do something appropriate here. */ ++ iput(inode); ++ return ERR_PTR(-EIO); ++ } ++ ++ return JFFS2_INODE_INFO(inode); ++} ++ ++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ unsigned long offset, ++ unsigned long *priv) ++{ ++ struct inode *inode = OFNI_EDONI_2SFFJ(f); ++ struct page *pg; ++ ++ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, ++ (void *)jffs2_do_readpage_unlock, inode); ++ if (IS_ERR(pg)) ++ return (void *)pg; ++ ++ *priv = (unsigned long)pg; ++ return kmap(pg); ++} ++ ++void jffs2_gc_release_page(struct jffs2_sb_info *c, ++ unsigned char *ptr, ++ unsigned long *priv) ++{ ++ struct page *pg = (void *)*priv; ++ ++ kunmap(pg); ++ page_cache_release(pg); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/gc.c linux/fs/jffs2/gc.c +--- linux-mips-2.4.27/fs/jffs2/gc.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/gc.c 2004-11-19 10:25:12.104168632 +0100 +@@ -1,76 +1,67 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: gc.c,v 1.52.2.7 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: gc.c,v 1.132 2003/12/01 11:32:11 dwmw2 Exp $ + * + */ + + #include + #include + #include +-#include +-#include +-#include + #include +-#include "nodelist.h" + #include ++#include ++#include ++#include "nodelist.h" + ++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ++ struct jffs2_inode_cache *ic, ++ struct jffs2_raw_node_ref *raw); + static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fd); + static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); + static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); + static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *indeo, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end); + static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end); ++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); + + /* Called with erase_completion_lock held */ + static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) + { + struct jffs2_eraseblock *ret; + struct list_head *nextlist = NULL; ++ int n = jiffies % 128; + + /* Pick an eraseblock to garbage collect next. This is where we'll + put the clever wear-levelling algorithms. Eventually. */ +- if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) { ++ /* We possibly want to favour the dirtier blocks more when the ++ number of free blocks is low. */ ++ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { + D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); + nextlist = &c->bad_used_list; +- } else if (jiffies % 100 && !list_empty(&c->dirty_list)) { +- /* Most of the time, pick one off the dirty list */ ++ } else if (n < 50 && !list_empty(&c->erasable_list)) { ++ /* Note that most of them will have gone directly to be erased. ++ So don't favour the erasable_list _too_ much. */ ++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); ++ nextlist = &c->erasable_list; ++ } else if (n < 110 && !list_empty(&c->very_dirty_list)) { ++ /* Most of the time, pick one off the very_dirty list */ ++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); ++ nextlist = &c->very_dirty_list; ++ } else if (n < 126 && !list_empty(&c->dirty_list)) { + D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); + nextlist = &c->dirty_list; + } else if (!list_empty(&c->clean_list)) { +@@ -80,9 +71,16 @@ + D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); + + nextlist = &c->dirty_list; ++ } else if (!list_empty(&c->very_dirty_list)) { ++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); ++ nextlist = &c->very_dirty_list; ++ } else if (!list_empty(&c->erasable_list)) { ++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); ++ ++ nextlist = &c->erasable_list; + } else { +- /* Eep. Both were empty */ +- printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n"); ++ /* Eep. All were empty */ ++ printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); + return NULL; + } + +@@ -94,6 +92,17 @@ + printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); + BUG(); + } ++ ++ /* Have we accidentally picked a clean block with wasted space ? */ ++ if (ret->wasted_size) { ++ D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); ++ ret->dirty_size += ret->wasted_size; ++ c->wasted_size -= ret->wasted_size; ++ c->dirty_size += ret->wasted_size; ++ ret->wasted_size = 0; ++ } ++ ++ D1(jffs2_dump_block_lists(c)); + return ret; + } + +@@ -103,21 +112,90 @@ + */ + int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) + { +- struct jffs2_eraseblock *jeb; + struct jffs2_inode_info *f; +- struct jffs2_raw_node_ref *raw; +- struct jffs2_node_frag *frag; +- struct jffs2_full_dnode *fn = NULL; +- struct jffs2_full_dirent *fd; + struct jffs2_inode_cache *ic; +- __u32 start = 0, end = 0, nrfrags = 0; +- struct inode *inode; +- int ret = 0; ++ struct jffs2_eraseblock *jeb; ++ struct jffs2_raw_node_ref *raw; ++ int ret = 0, inum, nlink; + + if (down_interruptible(&c->alloc_sem)) + return -EINTR; + +- spin_lock_bh(&c->erase_completion_lock); ++ for (;;) { ++ spin_lock(&c->erase_completion_lock); ++ if (!c->unchecked_size) ++ break; ++ ++ /* We can't start doing GC yet. We haven't finished checking ++ the node CRCs etc. Do it now. */ ++ ++ /* checked_ino is protected by the alloc_sem */ ++ if (c->checked_ino > c->highest_ino) { ++ printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", ++ c->unchecked_size); ++ D1(jffs2_dump_block_lists(c)); ++ spin_unlock(&c->erase_completion_lock); ++ BUG(); ++ } ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ spin_lock(&c->inocache_lock); ++ ++ ic = jffs2_get_ino_cache(c, c->checked_ino++); ++ ++ if (!ic) { ++ spin_unlock(&c->inocache_lock); ++ continue; ++ } ++ ++ if (!ic->nlink) { ++ D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ++ ic->ino)); ++ spin_unlock(&c->inocache_lock); ++ continue; ++ } ++ switch(ic->state) { ++ case INO_STATE_CHECKEDABSENT: ++ case INO_STATE_PRESENT: ++ D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); ++ spin_unlock(&c->inocache_lock); ++ continue; ++ ++ case INO_STATE_GC: ++ case INO_STATE_CHECKING: ++ printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); ++ spin_unlock(&c->inocache_lock); ++ BUG(); ++ ++ case INO_STATE_READING: ++ /* We need to wait for it to finish, lest we move on ++ and trigger the BUG() above while we haven't yet ++ finished checking all its nodes */ ++ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); ++ up(&c->alloc_sem); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ return 0; ++ ++ default: ++ BUG(); ++ ++ case INO_STATE_UNCHECKED: ++ ; ++ } ++ ic->state = INO_STATE_CHECKING; ++ spin_unlock(&c->inocache_lock); ++ ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ++ ++ ret = jffs2_do_crccheck_inode(c, ic); ++ if (ret) ++ printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); ++ ++ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); ++ up(&c->alloc_sem); ++ return ret; ++ } + + /* First, work out which block we're garbage-collecting */ + jeb = c->gcblock; +@@ -127,12 +205,14 @@ + + if (!jeb) { + printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + return -EIO; + } + +- D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset)); ++ D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); ++ D1(if (c->nextblock) ++ printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); + + if (!jeb->used_size) { + up(&c->alloc_sem); +@@ -141,92 +221,211 @@ + + raw = jeb->gc_node; + +- while(raw->flash_offset & 1) { +- D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3)); +- jeb->gc_node = raw = raw->next_phys; +- if (!raw) { ++ while(ref_obsolete(raw)) { ++ D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); ++ raw = raw->next_phys; ++ if (unlikely(!raw)) { + printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); + printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); +- spin_unlock_bh(&c->erase_completion_lock); ++ jeb->gc_node = raw; ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + BUG(); + } + } +- D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3)); ++ jeb->gc_node = raw; ++ ++ D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); ++ + if (!raw->next_in_ino) { + /* Inode-less node. Clean marker, snapshot or something like that */ +- spin_unlock_bh(&c->erase_completion_lock); ++ /* FIXME: If it's something that needs to be copied, including something ++ we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ ++ spin_unlock(&c->erase_completion_lock); + jffs2_mark_node_obsolete(c, raw); + up(&c->alloc_sem); + goto eraseit_lock; + } + + ic = jffs2_raw_ref_to_ic(raw); +- D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino)); +- +- spin_unlock_bh(&c->erase_completion_lock); + +- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino)); +- if (!ic->nlink) { +- /* The inode has zero nlink but its nodes weren't yet marked +- obsolete. This has to be because we're still waiting for +- the final (close() and) iput() to happen. +- +- There's a possibility that the final iput() could have +- happened while we were contemplating. In order to ensure +- that we don't cause a new read_inode() (which would fail) +- for the inode in question, we use ilookup() in this case +- instead of iget(). +- +- The nlink can't _become_ zero at this point because we're +- holding the alloc_sem, and jffs2_do_unlink() would also +- need that while decrementing nlink on any inode. ++ /* We need to hold the inocache. Either the erase_completion_lock or ++ the inocache_lock are sufficient; we trade down since the inocache_lock ++ causes less contention. */ ++ spin_lock(&c->inocache_lock); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); ++ ++ /* Three possibilities: ++ 1. Inode is already in-core. We must iget it and do proper ++ updating to its fragtree, etc. ++ 2. Inode is not in-core, node is REF_PRISTINE. We lock the ++ inocache to prevent a read_inode(), copy the node intact. ++ 3. Inode is not in-core, node is not pristine. We must iget() ++ and take the slow path. + */ +- inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino); +- if (!inode) { +- D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", ++ ++ switch(ic->state) { ++ case INO_STATE_CHECKEDABSENT: ++ /* It's been checked, but it's not currently in-core. ++ We can just copy any pristine nodes, but have ++ to prevent anyone else from doing read_inode() while ++ we're at it, so we set the state accordingly */ ++ if (ref_flags(raw) == REF_PRISTINE) ++ ic->state = INO_STATE_GC; ++ else { ++ D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + ic->ino)); +- up(&c->alloc_sem); +- return 0; + } +- } else { +- /* Inode has links to it still; they're not going away because +- jffs2_do_unlink() would need the alloc_sem and we have it. +- Just iget() it, and if read_inode() is necessary that's OK. ++ break; ++ ++ case INO_STATE_PRESENT: ++ /* It's in-core. GC must iget() it. */ ++ break; ++ ++ case INO_STATE_UNCHECKED: ++ case INO_STATE_CHECKING: ++ case INO_STATE_GC: ++ /* Should never happen. We should have finished checking ++ by the time we actually start doing any GC, and since ++ we're holding the alloc_sem, no other garbage collection ++ can happen. + */ +- inode = iget(OFNI_BS_2SFFJ(c), ic->ino); +- if (!inode) { ++ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ++ ic->ino, ic->state); + up(&c->alloc_sem); +- return -ENOMEM; ++ spin_unlock(&c->inocache_lock); ++ BUG(); ++ ++ case INO_STATE_READING: ++ /* Someone's currently trying to read it. We must wait for ++ them to finish and then go through the full iget() route ++ to do the GC. However, sometimes read_inode() needs to get ++ the alloc_sem() (for marking nodes invalid) so we must ++ drop the alloc_sem before sleeping. */ ++ ++ up(&c->alloc_sem); ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ++ ic->ino, ic->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ /* And because we dropped the alloc_sem we must start again from the ++ beginning. Ponder chance of livelock here -- we're returning success ++ without actually making any progress. ++ ++ Q: What are the chances that the inode is back in INO_STATE_READING ++ again by the time we next enter this function? And that this happens ++ enough times to cause a real delay? ++ ++ A: Small enough that I don't care :) ++ */ ++ return 0; + } ++ ++ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the ++ node intact, and we don't have to muck about with the fragtree etc. ++ because we know it's not in-core. If it _was_ in-core, we go through ++ all the iget() crap anyway */ ++ ++ if (ic->state == INO_STATE_GC) { ++ spin_unlock(&c->inocache_lock); ++ ++ ret = jffs2_garbage_collect_pristine(c, ic, raw); ++ ++ spin_lock(&c->inocache_lock); ++ ic->state = INO_STATE_CHECKEDABSENT; ++ wake_up(&c->inocache_wq); ++ ++ if (ret != -EBADFD) { ++ spin_unlock(&c->inocache_lock); ++ goto release_sem; + } +- if (is_bad_inode(inode)) { +- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); +- /* NB. This will happen again. We need to do something appropriate here. */ ++ ++ /* Fall through if it wanted us to, with inocache_lock held */ ++ } ++ ++ /* Prevent the fairly unlikely race where the gcblock is ++ entirely obsoleted by the final close of a file which had ++ the only valid nodes in the block, followed by erasure, ++ followed by freeing of the ic because the erased block(s) ++ held _all_ the nodes of that inode.... never been seen but ++ it's vaguely possible. */ ++ ++ inum = ic->ino; ++ nlink = ic->nlink; ++ spin_unlock(&c->inocache_lock); ++ ++ f = jffs2_gc_fetch_inode(c, inum, nlink); ++ if (IS_ERR(f)) ++ return PTR_ERR(f); ++ if (!f) ++ return 0; ++ ++ ret = jffs2_garbage_collect_live(c, jeb, raw, f); ++ ++ jffs2_gc_release_inode(c, f); ++ ++ release_sem: + up(&c->alloc_sem); +- iput(inode); +- return -EIO; ++ ++ eraseit_lock: ++ /* If we've finished this block, start it erasing */ ++ spin_lock(&c->erase_completion_lock); ++ ++ eraseit: ++ if (c->gcblock && !c->gcblock->used_size) { ++ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); ++ /* We're GC'ing an empty block? */ ++ list_add_tail(&c->gcblock->list, &c->erase_pending_list); ++ c->gcblock = NULL; ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); + } ++ spin_unlock(&c->erase_completion_lock); ++ ++ return ret; ++} ++ ++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f) ++{ ++ struct jffs2_node_frag *frag; ++ struct jffs2_full_dnode *fn = NULL; ++ struct jffs2_full_dirent *fd; ++ uint32_t start = 0, end = 0, nrfrags = 0; ++ int ret = 0; + +- f = JFFS2_INODE_INFO(inode); + down(&f->sem); ++ + /* Now we have the lock for this inode. Check that it's still the one at the head + of the list. */ + +- if (raw->flash_offset & 1) { ++ spin_lock(&c->erase_completion_lock); ++ ++ if (c->gcblock != jeb) { ++ spin_unlock(&c->erase_completion_lock); ++ D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); ++ goto upnout; ++ } ++ if (ref_obsolete(raw)) { ++ spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); + /* They'll call again */ + goto upnout; + } ++ spin_unlock(&c->erase_completion_lock); ++ + /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ + if (f->metadata && f->metadata->raw == raw) { + fn = f->metadata; +- ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn); ++ ret = jffs2_garbage_collect_metadata(c, jeb, f, fn); + goto upnout; + } + +- for (frag = f->fraglist; frag; frag = frag->next) { ++ /* FIXME. Read node and do lookup? */ ++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + if (frag->node && frag->node->raw == raw) { + fn = frag->node; + end = frag->ofs + frag->size; +@@ -237,13 +436,22 @@ + } + } + if (fn) { ++ if (ref_flags(raw) == REF_PRISTINE) { ++ ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); ++ if (!ret) { ++ /* Urgh. Return it sensibly. */ ++ frag->node->raw = f->inocache->nodes; ++ } ++ if (ret != -EBADFD) ++ goto upnout; ++ } + /* We found a datanode. Do the GC */ + if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) { + /* It crosses a page boundary. Therefore, it must be a hole. */ +- ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end); ++ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end); + } else { + /* It could still be a hole. But we GC the page this way anyway */ +- ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end); ++ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end); + } + goto upnout; + } +@@ -255,12 +463,13 @@ + } + + if (fd && fd->ino) { +- ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd); ++ ret = jffs2_garbage_collect_dirent(c, jeb, f, fd); + } else if (fd) { +- ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd); ++ ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd); + } else { +- printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino); +- if (raw->flash_offset & 1) { ++ printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n", ++ ref_offset(raw), f->inocache->ino); ++ if (ref_obsolete(raw)) { + printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); + } else { + ret = -EIO; +@@ -268,46 +477,197 @@ + } + upnout: + up(&f->sem); +- up(&c->alloc_sem); +- iput(inode); + +- eraseit_lock: +- /* If we've finished this block, start it erasing */ +- spin_lock_bh(&c->erase_completion_lock); ++ return ret; ++} + +- eraseit: +- if (c->gcblock && !c->gcblock->used_size) { +- D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); +- /* We're GC'ing an empty block? */ +- list_add_tail(&c->gcblock->list, &c->erase_pending_list); +- c->gcblock = NULL; +- c->nr_erasing_blocks++; +- jffs2_erase_pending_trigger(c); ++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ++ struct jffs2_inode_cache *ic, ++ struct jffs2_raw_node_ref *raw) ++{ ++ union jffs2_node_union *node; ++ struct jffs2_raw_node_ref *nraw; ++ size_t retlen; ++ int ret; ++ uint32_t phys_ofs, alloclen; ++ uint32_t crc, rawlen; ++ int retried = 0; ++ ++ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); ++ ++ rawlen = ref_totlen(c, c->gcblock, raw); ++ ++ /* Ask for a small amount of space (or the totlen if smaller) because we ++ don't want to force wastage of the end of a block if splitting would ++ work. */ ++ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, ++ rawlen), &phys_ofs, &alloclen); ++ if (ret) ++ return ret; ++ ++ if (alloclen < rawlen) { ++ /* Doesn't fit untouched. We'll go the old route and split it */ ++ return -EBADFD; ++ } ++ ++ node = kmalloc(rawlen, GFP_KERNEL); ++ if (!node) ++ return -ENOMEM; ++ ++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); ++ if (!ret && retlen != rawlen) ++ ret = -EIO; ++ if (ret) ++ goto out_node; ++ ++ crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); ++ if (je32_to_cpu(node->u.hdr_crc) != crc) { ++ printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); ++ goto bail; ++ } ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ case JFFS2_NODETYPE_INODE: ++ crc = crc32(0, node, sizeof(node->i)-8); ++ if (je32_to_cpu(node->i.node_crc) != crc) { ++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->i.node_crc), crc); ++ goto bail; ++ } ++ ++ if (je32_to_cpu(node->i.dsize)) { ++ crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize)); ++ if (je32_to_cpu(node->i.data_crc) != crc) { ++ printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->i.data_crc), crc); ++ goto bail; + } +- spin_unlock_bh(&c->erase_completion_lock); ++ } ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ crc = crc32(0, node, sizeof(node->d)-8); ++ if (je32_to_cpu(node->d.node_crc) != crc) { ++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->d.node_crc), crc); ++ goto bail; ++ } ++ ++ if (node->d.nsize) { ++ crc = crc32(0, node->d.name, node->d.nsize); ++ if (je32_to_cpu(node->d.name_crc) != crc) { ++ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); ++ goto bail; ++ } ++ } ++ break; ++ default: ++ printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", ++ ref_offset(raw), je16_to_cpu(node->u.nodetype)); ++ goto bail; ++ } ++ ++ nraw = jffs2_alloc_raw_node_ref(); ++ if (!nraw) { ++ ret = -ENOMEM; ++ goto out_node; ++ } ++ ++ /* OK, all the CRCs are good; this node can just be copied as-is. */ ++ retry: ++ nraw->flash_offset = phys_ofs; ++ nraw->__totlen = rawlen; ++ nraw->next_phys = NULL; ++ ++ ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); ++ ++ if (ret || (retlen != rawlen)) { ++ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", ++ rawlen, phys_ofs, ret, retlen); ++ if (retlen) { ++ /* Doesn't belong to any inode */ ++ nraw->next_in_ino = NULL; ++ ++ nraw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, nraw); ++ jffs2_mark_node_obsolete(c, nraw); ++ } else { ++ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); ++ jffs2_free_raw_node_ref(nraw); ++ } ++ if (!retried && (nraw == jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); ++ ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); + ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(nraw); ++ } ++ ++ if (!ret) ++ ret = -EIO; ++ goto out_node; ++ } ++ nraw->flash_offset |= REF_PRISTINE; ++ jffs2_add_physical_node_ref(c, nraw); ++ ++ /* Link into per-inode list. This is safe because of the ic ++ state being INO_STATE_GC. Note that if we're doing this ++ for an inode which is in-code, the 'nraw' pointer is then ++ going to be fetched from ic->nodes by our caller. */ ++ nraw->next_in_ino = ic->nodes; ++ ic->nodes = nraw; ++ ++ jffs2_mark_node_obsolete(c, raw); ++ D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); ++ ++ out_node: ++ kfree(node); + return ret; ++ bail: ++ ret = -EBADFD; ++ goto out_node; + } + + static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *new_fn; + struct jffs2_raw_inode ri; +- unsigned short dev; ++ jint16_t dev; + char *mdata = NULL, mdatalen = 0; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { ++ if (S_ISBLK(JFFS2_F_I_MODE(f)) || ++ S_ISCHR(JFFS2_F_I_MODE(f)) ) { + /* For these, we don't actually need to read the old node */ +- dev = (MAJOR(to_kdev_t(inode->i_rdev)) << 8) | +- MINOR(to_kdev_t(inode->i_rdev)); ++ /* FIXME: for minor or major > 255. */ ++ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | ++ JFFS2_F_I_RDEV_MIN(f))); + mdata = (char *)&dev; + mdatalen = sizeof(dev); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); +- } else if (S_ISLNK(inode->i_mode)) { ++ } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { + mdatalen = fn->size; + mdata = kmalloc(fn->size, GFP_KERNEL); + if (!mdata) { +@@ -326,34 +686,34 @@ + + ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", + sizeof(ri)+ mdatalen, ret); + goto out; + } + + memset(&ri, 0, sizeof(ri)); +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri) + mdatalen; +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.offset = 0; +- ri.csize = mdatalen; +- ri.dsize = mdatalen; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.offset = cpu_to_je32(0); ++ ri.csize = cpu_to_je32(mdatalen); ++ ri.dsize = cpu_to_je32(mdatalen); + ri.compr = JFFS2_COMPR_NONE; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = crc32(0, mdata, mdatalen); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); + +- new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL); ++ new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); +@@ -364,41 +724,40 @@ + jffs2_free_full_dnode(fn); + f->metadata = new_fn; + out: +- if (S_ISLNK(inode->i_mode)) ++ if (S_ISLNK(JFFS2_F_I_MODE(f))) + kfree(mdata); + return ret; + } + + static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd) ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dirent *new_fd; + struct jffs2_raw_dirent rd; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- rd.magic = JFFS2_MAGIC_BITMASK; +- rd.nodetype = JFFS2_NODETYPE_DIRENT; ++ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd.nsize = strlen(fd->name); +- rd.totlen = sizeof(rd) + rd.nsize; +- rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4); ++ rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize); ++ rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4)); + +- rd.pino = inode->i_ino; +- rd.version = ++f->highest_version; +- rd.ino = fd->ino; +- rd.mctime = max(inode->i_mtime, inode->i_ctime); ++ rd.pino = cpu_to_je32(f->inocache->ino); ++ rd.version = cpu_to_je32(++f->highest_version); ++ rd.ino = cpu_to_je32(fd->ino); ++ rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f))); + rd.type = fd->type; +- rd.node_crc = crc32(0, &rd, sizeof(rd)-8); +- rd.name_crc = crc32(0, fd->name, rd.nsize); ++ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); ++ rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); + + ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", + sizeof(rd)+rd.nsize, ret); + return ret; + } +- new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL); ++ new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fd)) { + printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); +@@ -409,19 +768,98 @@ + } + + static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd) ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dirent **fdp = &f->dents; + int found = 0; + +- /* FIXME: When we run on NAND flash, we need to work out whether +- this deletion dirent is still needed to actively delete a +- 'real' dirent with the same name that's still somewhere else +- on the flash. For now, we know that we've actually obliterated +- all the older dirents when they became obsolete, so we didn't +- really need to write the deletion to flash in the first place. +- */ ++ /* On a medium where we can't actually mark nodes obsolete ++ pernamently, such as NAND flash, we need to work out ++ whether this deletion dirent is still needed to actively ++ delete a 'real' dirent with the same name that's still ++ somewhere else on the flash. */ ++ if (!jffs2_can_mark_obsolete(c)) { ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_raw_node_ref *raw; ++ int ret; ++ size_t retlen; ++ int name_len = strlen(fd->name); ++ uint32_t name_crc = crc32(0, fd->name, name_len); ++ uint32_t rawlen = ref_totlen(c, jeb, fd->raw); ++ ++ rd = kmalloc(rawlen, GFP_KERNEL); ++ if (!rd) ++ return -ENOMEM; ++ ++ /* Prevent the erase code from nicking the obsolete node refs while ++ we're looking at them. I really don't like this extra lock but ++ can't see any alternative. Suggestions on a postcard to... */ ++ down(&c->erase_free_sem); ++ ++ for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { ++ ++ /* We only care about obsolete ones */ ++ if (!(ref_obsolete(raw))) ++ continue; ++ ++ /* Any dirent with the same name is going to have the same length... */ ++ if (ref_totlen(c, NULL, raw) != rawlen) ++ continue; ++ ++ /* Doesn't matter if there's one in the same erase block. We're going to ++ delete it too at the same time. */ ++ if ((raw->flash_offset & ~(c->sector_size-1)) == ++ (fd->raw->flash_offset & ~(c->sector_size-1))) ++ continue; ++ ++ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); ++ ++ /* This is an obsolete node belonging to the same directory, and it's of the right ++ length. We need to take a closer look...*/ ++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); ++ if (ret) { ++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); ++ /* If we can't read it, we don't need to continue to obsolete it. Continue */ ++ continue; ++ } ++ if (retlen != rawlen) { ++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", ++ retlen, rawlen, ref_offset(raw)); ++ continue; ++ } ++ ++ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) ++ continue; ++ ++ /* If the name CRC doesn't match, skip */ ++ if (je32_to_cpu(rd->name_crc) != name_crc) ++ continue; ++ ++ /* If the name length doesn't match, or it's another deletion dirent, skip */ ++ if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) ++ continue; ++ ++ /* OK, check the actual name now */ ++ if (memcmp(rd->name, fd->name, name_len)) ++ continue; ++ ++ /* OK. The name really does match. There really is still an older node on ++ the flash which our deletion dirent obsoletes. So we have to write out ++ a new deletion dirent to replace it */ ++ up(&c->erase_free_sem); ++ ++ D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ++ ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); ++ kfree(rd); ++ ++ return jffs2_garbage_collect_dirent(c, jeb, f, fd); ++ } ++ ++ up(&c->erase_free_sem); ++ kfree(rd); ++ } ++ ++ /* No need for it any more. Just mark it obsolete and remove it from the list */ + while (*fdp) { + if ((*fdp) == fd) { + found = 1; +@@ -431,7 +869,7 @@ + fdp = &(*fdp)->next; + } + if (!found) { +- printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino); ++ printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino); + } + jffs2_mark_node_obsolete(c, fd->raw); + jffs2_free_full_dirent(fd); +@@ -439,93 +877,95 @@ + } + + static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_inode ri; + struct jffs2_node_frag *frag; + struct jffs2_full_dnode *new_fn; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n", +- inode->i_ino, start, end)); ++ D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", ++ f->inocache->ino, start, end)); + + memset(&ri, 0, sizeof(ri)); + + if(fn->frags > 1) { + size_t readlen; +- __u32 crc; ++ uint32_t crc; + /* It's partially obsoleted by a later write. So we have to + write it out again with the _same_ version as before */ +- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri); ++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri); + if (readlen != sizeof(ri) || ret) { +- printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen); ++ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen); + goto fill; + } +- if (ri.nodetype != JFFS2_NODETYPE_INODE) { ++ if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n", +- fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE); ++ ref_offset(fn->raw), ++ je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE); + return -EIO; + } +- if (ri.totlen != sizeof(ri)) { +- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n", +- fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri)); ++ if (je32_to_cpu(ri.totlen) != sizeof(ri)) { ++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n", ++ ref_offset(fn->raw), ++ je32_to_cpu(ri.totlen), sizeof(ri)); + return -EIO; + } + crc = crc32(0, &ri, sizeof(ri)-8); +- if (crc != ri.node_crc) { ++ if (crc != je32_to_cpu(ri.node_crc)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n", +- fn->raw->flash_offset & ~3, ri.node_crc, crc); ++ ref_offset(fn->raw), ++ je32_to_cpu(ri.node_crc), crc); + /* FIXME: We could possibly deal with this by writing new holes for each frag */ +- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", +- start, end, inode->i_ino); ++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", ++ start, end, f->inocache->ino); + goto fill; + } + if (ri.compr != JFFS2_COMPR_ZERO) { +- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3); +- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", +- start, end, inode->i_ino); ++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw)); ++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", ++ start, end, f->inocache->ino); + goto fill; + } + } else { + fill: +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri); +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.offset = start; +- ri.dsize = end - start; +- ri.csize = 0; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.offset = cpu_to_je32(start); ++ ri.dsize = cpu_to_je32(end - start); ++ ri.csize = cpu_to_je32(0); + ri.compr = JFFS2_COMPR_ZERO; + } +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.data_crc = 0; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.data_crc = cpu_to_je32(0); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + + ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", + sizeof(ri), ret); + return ret; + } +- new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL); ++ new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); + return PTR_ERR(new_fn); + } +- if (ri.version == f->highest_version) { ++ if (je32_to_cpu(ri.version) == f->highest_version) { + jffs2_add_full_dnode_to_inode(c, f, new_fn); + if (f->metadata) { + jffs2_mark_node_obsolete(c, f->metadata->raw); +@@ -541,12 +981,17 @@ + * number as before. (Except in case of error -- see 'goto fill;' + * above.) + */ +- D1(if(fn->frags <= 1) { ++ D1(if(unlikely(fn->frags <= 1)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", +- fn->frags, ri.version, f->highest_version, ri.ino); ++ fn->frags, je32_to_cpu(ri.version), f->highest_version, ++ je32_to_cpu(ri.ino)); + }); + +- for (frag = f->fraglist; frag; frag = frag->next) { ++ /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ ++ mark_ref_normal(new_fn->raw); ++ ++ for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); ++ frag; frag = frag_next(frag)) { + if (frag->ofs > fn->size + fn->ofs) + break; + if (frag->node == fn) { +@@ -571,49 +1016,146 @@ + } + + static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *new_fn; + struct jffs2_raw_inode ri; +- __u32 alloclen, phys_ofs, offset, orig_end; ++ uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; + int ret = 0; + unsigned char *comprbuf = NULL, *writebuf; +- struct page *pg; ++ unsigned long pg; + unsigned char *pg_ptr; + +- + memset(&ri, 0, sizeof(ri)); + +- D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n", +- inode->i_ino, start, end)); ++ D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", ++ f->inocache->ino, start, end)); + + orig_end = end; ++ orig_start = start; + ++ if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { ++ /* Attempt to do some merging. But only expand to cover logically ++ adjacent frags if the block containing them is already considered ++ to be dirty. Otherwise we end up with GC just going round in ++ circles dirtying the nodes it already wrote out, especially ++ on NAND where we have small eraseblocks and hence a much higher ++ chance of nodes having to be split to cross boundaries. */ + +- /* If we're looking at the last node in the block we're +- garbage-collecting, we allow ourselves to merge as if the +- block was already erasing. We're likely to be GC'ing a +- partial page, and the next block we GC is likely to have +- the other half of this page right at the beginning, which +- means we'd expand it _then_, as nr_erasing_blocks would have +- increased since we checked, and in doing so would obsolete +- the partial node which we'd have written here. Meaning that +- the GC would churn and churn, and just leave dirty blocks in +- it's wake. +- */ +- if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) { +- /* Shitloads of space */ +- /* FIXME: Integrate this properly with GC calculations */ +- start &= ~(PAGE_CACHE_SIZE-1); +- end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size); +- D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n", +- start, end)); +- if (end < orig_end) { +- printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end); +- end = orig_end; ++ struct jffs2_node_frag *frag; ++ uint32_t min, max; ++ ++ min = start & ~(PAGE_CACHE_SIZE-1); ++ max = min + PAGE_CACHE_SIZE; ++ ++ frag = jffs2_lookup_node_frag(&f->fragtree, start); ++ ++ /* BUG_ON(!frag) but that'll happen anyway... */ ++ ++ BUG_ON(frag->ofs != start); ++ ++ /* First grow down... */ ++ while((frag = frag_prev(frag)) && frag->ofs >= min) { ++ ++ /* If the previous frag doesn't even reach the beginning, there's ++ excessive fragmentation. Just merge. */ ++ if (frag->ofs > min) { ++ D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", ++ frag->ofs, frag->ofs+frag->size)); ++ start = frag->ofs; ++ continue; ++ } ++ /* OK. This frag holds the first byte of the page. */ ++ if (!frag->node || !frag->node->raw) { ++ D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", ++ frag->ofs, frag->ofs+frag->size)); ++ break; ++ } else { ++ ++ /* OK, it's a frag which extends to the beginning of the page. Does it live ++ in a block which is still considered clean? If so, don't obsolete it. ++ If not, cover it anyway. */ ++ ++ struct jffs2_raw_node_ref *raw = frag->node->raw; ++ struct jffs2_eraseblock *jeb; ++ ++ jeb = &c->blocks[raw->flash_offset / c->sector_size]; ++ ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", ++ frag->ofs, frag->ofs+frag->size, ref_offset(raw))); ++ start = frag->ofs; ++ break; + } ++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { ++ D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ start = frag->ofs; ++ break; ++ } ++ } ++ ++ /* ... then up */ ++ ++ /* Find last frag which is actually part of the node we're to GC. */ ++ frag = jffs2_lookup_node_frag(&f->fragtree, end-1); ++ ++ while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { ++ ++ /* If the previous frag doesn't even reach the beginning, there's lots ++ of fragmentation. Just merge. */ ++ if (frag->ofs+frag->size < max) { ++ D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", ++ frag->ofs, frag->ofs+frag->size)); ++ end = frag->ofs + frag->size; ++ continue; ++ } ++ ++ if (!frag->node || !frag->node->raw) { ++ D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", ++ frag->ofs, frag->ofs+frag->size)); ++ break; ++ } else { ++ ++ /* OK, it's a frag which extends to the beginning of the page. Does it live ++ in a block which is still considered clean? If so, don't obsolete it. ++ If not, cover it anyway. */ ++ ++ struct jffs2_raw_node_ref *raw = frag->node->raw; ++ struct jffs2_eraseblock *jeb; ++ ++ jeb = &c->blocks[raw->flash_offset / c->sector_size]; ++ ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", ++ frag->ofs, frag->ofs+frag->size, ref_offset(raw))); ++ end = frag->ofs + frag->size; ++ break; ++ } ++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { ++ D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ end = frag->ofs + frag->size; ++ break; ++ } ++ } ++ D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", ++ orig_start, orig_end, start, end)); ++ ++ BUG_ON(end > JFFS2_F_I_SIZE(f)); ++ BUG_ON(end < orig_end); ++ BUG_ON(start > orig_start); + } + + /* First, use readpage() to read the appropriate page into the page cache */ +@@ -623,63 +1165,57 @@ + * page OK. We'll actually write it out again in commit_write, which is a little + * suboptimal, but at least we're correct. + */ +- pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); ++ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + +- if (IS_ERR(pg)) { +- printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); +- return PTR_ERR(pg); ++ if (IS_ERR(pg_ptr)) { ++ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); ++ return PTR_ERR(pg_ptr); + } +- pg_ptr = (char *)kmap(pg); +- comprbuf = kmalloc(end - start, GFP_KERNEL); + + offset = start; + while(offset < orig_end) { +- __u32 datalen; +- __u32 cdatalen; ++ uint32_t datalen; ++ uint32_t cdatalen; + char comprtype = JFFS2_COMPR_NONE; + + ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); + + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", + sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret); + break; + } +- cdatalen = min(alloclen - sizeof(ri), end - offset); ++ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); + datalen = end - offset; + + writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); + +- if (comprbuf) { +- comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen); +- } +- if (comprtype) { +- writebuf = comprbuf; +- } else { +- datalen = cdatalen; +- } +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri) + cdatalen; +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.offset = offset; +- ri.csize = cdatalen; +- ri.dsize = datalen; ++ comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen); ++ ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.offset = cpu_to_je32(offset); ++ ri.csize = cpu_to_je32(cdatalen); ++ ri.dsize = cpu_to_je32(datalen); + ri.compr = comprtype; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = crc32(0, writebuf, cdatalen); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); ++ ++ new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + +- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL); ++ jffs2_free_comprbuf(comprbuf, writebuf); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); +@@ -694,12 +1230,8 @@ + f->metadata = NULL; + } + } +- if (comprbuf) kfree(comprbuf); + +- kunmap(pg); +- /* XXX: Does the page get freed automatically? */ +- /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */ +- page_cache_release(pg); ++ jffs2_gc_release_page(c, pg_ptr, &pg); + return ret; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/ioctl.c linux/fs/jffs2/ioctl.c +--- linux-mips-2.4.27/fs/jffs2/ioctl.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/ioctl.c 2004-11-19 10:25:12.108168024 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: ioctl.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $ ++ * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ + * + */ + +@@ -42,6 +18,6 @@ + { + /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which + will include compression support etc. */ +- return -EINVAL; ++ return -ENOTTY; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/malloc.c linux/fs/jffs2/malloc.c +--- linux-mips-2.4.27/fs/jffs2/malloc.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/malloc.c 2004-11-19 10:25:12.110167720 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $ ++ * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ + * + */ + +@@ -47,6 +23,9 @@ + #define JFFS2_SLAB_POISON 0 + #endif + ++// replace this by #define D3 (x) x for cache debugging ++#define D3(x) ++ + /* These are initialised to NULL in the kernel startup code. + If you're porting to other operating systems, beware */ + static kmem_cache_t *full_dnode_slab; +@@ -57,57 +36,47 @@ + static kmem_cache_t *node_frag_slab; + static kmem_cache_t *inode_cache_slab; + +-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) +-{ +- struct jffs2_tmp_dnode_info *next; +- +- while (tn) { +- next = tn; +- tn = tn->next; +- jffs2_free_full_dnode(next->fn); +- jffs2_free_tmp_dnode_info(next); +- } +-} +- +-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) +-{ +- struct jffs2_full_dirent *next; +- +- while (fd) { +- next = fd->next; +- jffs2_free_full_dirent(fd); +- fd = next; +- } +-} +- + int __init jffs2_create_slab_caches(void) + { +- full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ full_dnode_slab = kmem_cache_create("jffs2_full_dnode", ++ sizeof(struct jffs2_full_dnode), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!full_dnode_slab) + goto err; + +- raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", ++ sizeof(struct jffs2_raw_dirent), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_dirent_slab) + goto err; + +- raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_inode_slab = kmem_cache_create("jffs2_raw_inode", ++ sizeof(struct jffs2_raw_inode), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_inode_slab) + goto err; + +- tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", ++ sizeof(struct jffs2_tmp_dnode_info), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!tmp_dnode_info_slab) + goto err; + +- raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", ++ sizeof(struct jffs2_raw_node_ref), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_node_ref_slab) + goto err; + +- node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ node_frag_slab = kmem_cache_create("jffs2_node_frag", ++ sizeof(struct jffs2_node_frag), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!node_frag_slab) + goto err; + +- inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); +- ++ inode_cache_slab = kmem_cache_create("jffs2_inode_cache", ++ sizeof(struct jffs2_inode_cache), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (inode_cache_slab) + return 0; + err: +@@ -131,7 +100,6 @@ + kmem_cache_destroy(node_frag_slab); + if(inode_cache_slab) + kmem_cache_destroy(inode_cache_slab); +- + } + + struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) +@@ -146,75 +114,92 @@ + + struct jffs2_full_dnode *jffs2_alloc_full_dnode(void) + { +- void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); ++ struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret)); + return ret; + } + + void jffs2_free_full_dnode(struct jffs2_full_dnode *x) + { ++ D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x)); + kmem_cache_free(full_dnode_slab, x); + } + + struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void) + { +- return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); ++ struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret)); ++ return ret; + } + + void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x)); + kmem_cache_free(raw_dirent_slab, x); + } + + struct jffs2_raw_inode *jffs2_alloc_raw_inode(void) + { +- return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); ++ struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret)); ++ return ret; + } + + void jffs2_free_raw_inode(struct jffs2_raw_inode *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x)); + kmem_cache_free(raw_inode_slab, x); + } + + struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void) + { +- return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); ++ struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret)); ++ return ret; + } + + void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x) + { ++ D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x)); + kmem_cache_free(tmp_dnode_info_slab, x); + } + + struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) + { +- return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); ++ struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret)); ++ return ret; + } + + void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x)); + kmem_cache_free(raw_node_ref_slab, x); + } + + struct jffs2_node_frag *jffs2_alloc_node_frag(void) + { +- return kmem_cache_alloc(node_frag_slab, GFP_KERNEL); ++ struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret)); ++ return ret; + } + + void jffs2_free_node_frag(struct jffs2_node_frag *x) + { ++ D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x)); + kmem_cache_free(node_frag_slab, x); + } + + struct jffs2_inode_cache *jffs2_alloc_inode_cache(void) + { + struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL); +- D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret)); ++ D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret)); + return ret; + } + + void jffs2_free_inode_cache(struct jffs2_inode_cache *x) + { +- D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x)); ++ D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x)); + kmem_cache_free(inode_cache_slab, x); + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.c linux/fs/jffs2/nodelist.c +--- linux-mips-2.4.27/fs/jffs2/nodelist.c 2003-07-05 05:23:44.000000000 +0200 ++++ linux/fs/jffs2/nodelist.c 2004-11-19 10:25:12.112167416 +0100 +@@ -1,44 +1,24 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodelist.c,v 1.30.2.6 2003/02/24 21:49:33 dwmw2 Exp $ ++ * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ + * + */ + + #include +-#include ++#include + #include + #include ++#include ++#include ++#include ++#include + #include "nodelist.h" + + void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) +@@ -78,7 +58,7 @@ + /* Put a new tmp_dnode_info into the list, keeping the list in + order of increasing version + */ +-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) ++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) + { + struct jffs2_tmp_dnode_info **prev = list; + +@@ -89,13 +69,37 @@ + *prev = tn; + } + ++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) ++{ ++ struct jffs2_tmp_dnode_info *next; ++ ++ while (tn) { ++ next = tn; ++ tn = tn->next; ++ jffs2_free_full_dnode(next->fn); ++ jffs2_free_tmp_dnode_info(next); ++ } ++} ++ ++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) ++{ ++ struct jffs2_full_dirent *next; ++ ++ while (fd) { ++ next = fd->next; ++ jffs2_free_full_dirent(fd); ++ fd = next; ++ } ++} ++ ++ + /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated + with this ino, returning the former in order of version */ + + int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, + struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, +- __u32 *highest_version, __u32 *latest_mctime, +- __u32 *mctime_ver) ++ uint32_t *highest_version, uint32_t *latest_mctime, ++ uint32_t *mctime_ver) + { + struct jffs2_raw_node_ref *ref = f->inocache->nodes; + struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; +@@ -109,43 +113,71 @@ + + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); + if (!f->inocache->nodes) { +- printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino); ++ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino); + } ++ ++ spin_lock(&c->erase_completion_lock); ++ + for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) { + /* Work out whether it's a data node or a dirent node */ +- if (ref->flash_offset & 1) { ++ if (ref_obsolete(ref)) { + /* FIXME: On NAND flash we may need to read these */ +- D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); + continue; + } +- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node); ++ /* We can hold a pointer to a non-obsolete node without the spinlock, ++ but _obsolete_ nodes may disappear at any time, if the block ++ they're in gets erased */ ++ spin_unlock(&c->erase_completion_lock); ++ ++ cond_resched(); ++ ++ /* FIXME: point() */ ++ err = jffs2_flash_read(c, (ref_offset(ref)), ++ min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), ++ &retlen, (void *)&node); + if (err) { +- printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3); ++ printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); + goto free_out; + } + + + /* Check we've managed to read at least the common node header */ +- if (retlen < min(ref->totlen, sizeof(node.u))) { ++ if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) { + printk(KERN_WARNING "short read in get_inode_nodes()\n"); + err = -EIO; + goto free_out; + } + +- switch (node.u.nodetype) { ++ switch (je16_to_cpu(node.u.nodetype)) { + case JFFS2_NODETYPE_DIRENT: +- D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref))); ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref)); ++ BUG(); ++ } + if (retlen < sizeof(node.d)) { + printk(KERN_WARNING "short read in get_inode_nodes()\n"); + err = -EIO; + goto free_out; + } +- if (node.d.version > *highest_version) +- *highest_version = node.d.version; +- if (ref->flash_offset & 1) { +- /* Obsoleted */ ++ /* sanity check */ ++ if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n", ++ ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen)); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); + continue; + } ++ if (je32_to_cpu(node.d.version) > *highest_version) ++ *highest_version = je32_to_cpu(node.d.version); ++ if (ref_obsolete(ref)) { ++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ ++ printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n", ++ ref_offset(ref)); ++ BUG(); ++ } ++ + fd = jffs2_alloc_full_dirent(node.d.nsize+1); + if (!fd) { + err = -ENOMEM; +@@ -153,29 +185,30 @@ + } + memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); + fd->raw = ref; +- fd->version = node.d.version; +- fd->ino = node.d.ino; ++ fd->version = je32_to_cpu(node.d.version); ++ fd->ino = je32_to_cpu(node.d.ino); + fd->type = node.d.type; + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; +- *latest_mctime = node.d.mctime; ++ *latest_mctime = je32_to_cpu(node.d.mctime); + } + + /* memcpy as much of the name as possible from the raw + dirent we've already read from the flash + */ + if (retlen > sizeof(struct jffs2_raw_dirent)) +- memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); ++ memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); + + /* Do we need to copy any more of the name directly + from the flash? + */ + if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { ++ /* FIXME: point() */ + int already = retlen - sizeof(struct jffs2_raw_dirent); + +- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen, ++ err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, + node.d.nsize - already, &retlen, &fd->name[already]); + if (!err && retlen != node.d.nsize - already) + err = -EIO; +@@ -196,21 +229,126 @@ + break; + + case JFFS2_NODETYPE_INODE: +- D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref))); + if (retlen < sizeof(node.i)) { + printk(KERN_WARNING "read too short for dnode\n"); + err = -EIO; + goto free_out; + } +- if (node.i.version > *highest_version) +- *highest_version = node.i.version; +- D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version)); +- +- if (ref->flash_offset & 1) { +- D1(printk(KERN_DEBUG "obsoleted\n")); +- /* Obsoleted */ ++ if (je32_to_cpu(node.i.version) > *highest_version) ++ *highest_version = je32_to_cpu(node.i.version); ++ D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version)); ++ ++ if (ref_obsolete(ref)) { ++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ ++ printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n", ++ ref_offset(ref)); ++ BUG(); ++ } ++ ++ /* If we've never checked the CRCs on this node, check them now. */ ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ uint32_t crc, len; ++ struct jffs2_eraseblock *jeb; ++ ++ crc = crc32(0, &node, sizeof(node.i)-8); ++ if (crc != je32_to_cpu(node.i.node_crc)) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(ref), je32_to_cpu(node.i.node_crc), crc); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); + continue; + } ++ ++ /* sanity checks */ ++ if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) || ++ PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n", ++ ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino), ++ je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize), ++ je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize)); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); ++ continue; ++ } ++ ++ if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) { ++ unsigned char *buf=NULL; ++ uint32_t pointed = 0; ++#ifndef __ECOS ++ if (c->mtd->point) { ++ err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), ++ &retlen, &buf); ++ if (!err && retlen < je32_to_cpu(node.i.csize)) { ++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); ++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); ++ } else if (err){ ++ D1(printk(KERN_DEBUG "MTD point failed %d\n", err)); ++ } else ++ pointed = 1; /* succefully pointed to device */ ++ } ++#endif ++ if(!pointed){ ++ buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), ++ &retlen, buf); ++ if (!err && retlen != je32_to_cpu(node.i.csize)) ++ err = -EIO; ++ if (err) { ++ kfree(buf); ++ return err; ++ } ++ } ++ crc = crc32(0, buf, je32_to_cpu(node.i.csize)); ++ if(!pointed) ++ kfree(buf); ++#ifndef __ECOS ++ else ++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); ++#endif ++ ++ if (crc != je32_to_cpu(node.i.data_crc)) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(ref), je32_to_cpu(node.i.data_crc), crc); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); ++ continue; ++ } ++ ++ } ++ ++ /* Mark the node as having been checked and fix the accounting accordingly */ ++ spin_lock(&c->erase_completion_lock); ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, ref); ++ ++ jeb->used_size += len; ++ jeb->unchecked_size -= len; ++ c->used_size += len; ++ c->unchecked_size -= len; ++ ++ /* If node covers at least a whole page, or if it starts at the ++ beginning of a page and runs to the end of the file, or if ++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. ++ ++ If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) ++ when the overlapping node(s) get added to the tree anyway. ++ */ ++ if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) || ++ ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) && ++ (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) { ++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref))); ++ ref->flash_offset = ref_offset(ref) | REF_PRISTINE; ++ } else { ++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref))); ++ ref->flash_offset = ref_offset(ref) | REF_NORMAL; ++ } ++ spin_unlock(&c->erase_completion_lock); ++ } ++ + tn = jffs2_alloc_tmp_dnode_info(); + if (!tn) { + D1(printk(KERN_DEBUG "alloc tn failed\n")); +@@ -225,36 +363,76 @@ + jffs2_free_tmp_dnode_info(tn); + goto free_out; + } +- tn->version = node.i.version; +- tn->fn->ofs = node.i.offset; ++ tn->version = je32_to_cpu(node.i.version); ++ tn->fn->ofs = je32_to_cpu(node.i.offset); + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ +- if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize) +- tn->fn->size = node.i.csize; ++ if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize)) ++ tn->fn->size = je32_to_cpu(node.i.csize); + else // normal case... +- tn->fn->size = node.i.dsize; ++ tn->fn->size = je32_to_cpu(node.i.dsize); + tn->fn->raw = ref; +- D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize)); ++ D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ++ ref_offset(ref), je32_to_cpu(node.i.version), ++ je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize))); + jffs2_add_tn_to_list(tn, &ret_tn); + break; + + default: +- switch(node.u.nodetype & JFFS2_COMPAT_MASK) { ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ struct jffs2_eraseblock *jeb; ++ uint32_t len; ++ ++ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", ++ je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ ++ /* Mark the node as having been checked and fix the accounting accordingly */ ++ spin_lock(&c->erase_completion_lock); ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, ref); ++ ++ jeb->used_size += len; ++ jeb->unchecked_size -= len; ++ c->used_size += len; ++ c->unchecked_size -= len; ++ ++ mark_ref_normal(ref); ++ spin_unlock(&c->erase_completion_lock); ++ } ++ node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype)); ++ if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) { ++ /* Hmmm. This should have been caught at scan time. */ ++ printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n", ++ ref_offset(ref)); ++ printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", ++ je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen), ++ je32_to_cpu(node.u.hdr_crc)); ++ jffs2_mark_node_obsolete(c, ref); ++ } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) { + case JFFS2_FEATURE_INCOMPAT: +- printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ /* EEP */ ++ BUG(); + break; + case JFFS2_FEATURE_ROCOMPAT: +- printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ if (!(c->flags & JFFS2_SB_FLAG_RO)) ++ BUG(); + break; + case JFFS2_FEATURE_RWCOMPAT_COPY: +- printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); + break; + case JFFS2_FEATURE_RWCOMPAT_DELETE: +- printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ jffs2_mark_node_obsolete(c, ref); + break; + } ++ + } ++ spin_lock(&c->erase_completion_lock); ++ + } ++ spin_unlock(&c->erase_completion_lock); + *tnp = ret_tn; + *fdp = ret_fd; + +@@ -266,19 +444,30 @@ + return err; + } + ++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) ++{ ++ spin_lock(&c->inocache_lock); ++ ic->state = state; ++ wake_up(&c->inocache_wq); ++ spin_unlock(&c->inocache_lock); ++} ++ ++/* During mount, this needs no locking. During normal operation, its ++ callers want to do other stuff while still holding the inocache_lock. ++ Rather than introducing special case get_ino_cache functions or ++ callbacks, we just let the caller do the locking itself. */ ++ + struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino) + { + struct jffs2_inode_cache *ret; + + D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); +- spin_lock (&c->inocache_lock); ++ + ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; + while (ret && ret->ino < ino) { + ret = ret->next; + } + +- spin_unlock(&c->inocache_lock); +- + if (ret && ret->ino != ino) + ret = NULL; + +@@ -299,6 +488,7 @@ + } + new->next = *prev; + *prev = new; ++ + spin_unlock(&c->inocache_lock); + } + +@@ -316,6 +506,7 @@ + if ((*prev) == old) { + *prev = old->next; + } ++ + spin_unlock(&c->inocache_lock); + } + +@@ -352,3 +543,128 @@ + } + } + ++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset) ++{ ++ /* The common case in lookup is that there will be a node ++ which precisely matches. So we go looking for that first */ ++ struct rb_node *next; ++ struct jffs2_node_frag *prev = NULL; ++ struct jffs2_node_frag *frag = NULL; ++ ++ D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset)); ++ ++ next = fragtree->rb_node; ++ ++ while(next) { ++ frag = rb_entry(next, struct jffs2_node_frag, rb); ++ ++ D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n", ++ frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right)); ++ if (frag->ofs + frag->size <= offset) { ++ D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n", ++ frag->ofs, frag->ofs+frag->size)); ++ /* Remember the closest smaller match on the way down */ ++ if (!prev || frag->ofs > prev->ofs) ++ prev = frag; ++ next = frag->rb.rb_right; ++ } else if (frag->ofs > offset) { ++ D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n", ++ frag->ofs, frag->ofs+frag->size)); ++ next = frag->rb.rb_left; ++ } else { ++ D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n", ++ frag->ofs, frag->ofs+frag->size)); ++ return frag; ++ } ++ } ++ ++ /* Exact match not found. Go back up looking at each parent, ++ and return the closest smaller one */ ++ ++ if (prev) ++ D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n", ++ prev->ofs, prev->ofs+prev->size)); ++ else ++ D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n")); ++ ++ return prev; ++} ++ ++/* Pass 'c' argument to indicate that nodes should be marked obsolete as ++ they're killed. */ ++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) ++{ ++ struct jffs2_node_frag *frag; ++ struct jffs2_node_frag *parent; ++ ++ if (!root->rb_node) ++ return; ++ ++ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); ++ ++ while(frag) { ++ if (frag->rb.rb_left) { ++ D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", ++ frag, frag->ofs, frag->ofs+frag->size)); ++ frag = frag_left(frag); ++ continue; ++ } ++ if (frag->rb.rb_right) { ++ D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", ++ frag, frag->ofs, frag->ofs+frag->size)); ++ frag = frag_right(frag); ++ continue; ++ } ++ ++ D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n", ++ frag->ofs, frag->ofs+frag->size, frag->node, ++ frag->node?frag->node->frags:0)); ++ ++ if (frag->node && !(--frag->node->frags)) { ++ /* Not a hole, and it's the final remaining frag ++ of this node. Free the node */ ++ if (c) ++ jffs2_mark_node_obsolete(c, frag->node->raw); ++ ++ jffs2_free_full_dnode(frag->node); ++ } ++ parent = frag_parent(frag); ++ if (parent) { ++ if (frag_left(parent) == frag) ++ parent->rb.rb_left = NULL; ++ else ++ parent->rb.rb_right = NULL; ++ } ++ ++ jffs2_free_node_frag(frag); ++ frag = parent; ++ ++ cond_resched(); ++ } ++} ++ ++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) ++{ ++ struct rb_node *parent = &base->rb; ++ struct rb_node **link = &parent; ++ ++ D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, ++ newfrag->ofs, newfrag->ofs+newfrag->size, base)); ++ ++ while (*link) { ++ parent = *link; ++ base = rb_entry(parent, struct jffs2_node_frag, rb); ++ ++ D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs)); ++ if (newfrag->ofs > base->ofs) ++ link = &base->rb.rb_right; ++ else if (newfrag->ofs < base->ofs) ++ link = &base->rb.rb_left; ++ else { ++ printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); ++ BUG(); ++ } ++ } ++ ++ rb_link_node(&newfrag->rb, &base->rb, link); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.h linux/fs/jffs2/nodelist.h +--- linux-mips-2.4.27/fs/jffs2/nodelist.h 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/nodelist.h 2004-11-19 10:25:12.113167264 +0100 +@@ -1,48 +1,35 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodelist.h,v 1.46.2.5 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: nodelist.h,v 1.115 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + ++#ifndef __JFFS2_NODELIST_H__ ++#define __JFFS2_NODELIST_H__ ++ + #include + #include +- ++#include ++#include + #include + #include + ++#ifdef __ECOS ++#include "os-ecos.h" ++#else ++#include /* For min/max in older kernels */ ++#include "os-linux.h" ++#endif ++ + #ifndef CONFIG_JFFS2_FS_DEBUG +-#define CONFIG_JFFS2_FS_DEBUG 2 ++#define CONFIG_JFFS2_FS_DEBUG 1 + #endif + + #if CONFIG_JFFS2_FS_DEBUG > 0 +@@ -71,17 +58,21 @@ + for this inode instead. The inode_cache will have NULL in the first + word so you know when you've got there :) */ + struct jffs2_raw_node_ref *next_phys; +- // __u32 ino; +- __u32 flash_offset; +- __u32 totlen; +-// __u16 nodetype; ++ uint32_t flash_offset; ++ uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ ++}; + + /* flash_offset & 3 always has to be zero, because nodes are + always aligned at 4 bytes. So we have a couple of extra bits +- to play with. So we set the least significant bit to 1 to +- signify that the node is obsoleted by later nodes. +- */ +-}; ++ to play with, which indicate the node's status; see below: */ ++#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ ++#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ ++#define REF_PRISTINE 2 /* Completely clean. GC without looking */ ++#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */ ++#define ref_flags(ref) ((ref)->flash_offset & 3) ++#define ref_offset(ref) ((ref)->flash_offset & ~3) ++#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) ++#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) + + /* + Used for keeping track of deletion nodes &c, which can only be marked +@@ -101,19 +92,35 @@ + a pointer to the first physical node which is part of this inode, too. + */ + struct jffs2_inode_cache { +- struct jffs2_scan_info *scan; /* Used during scan to hold +- temporary lists of nodes, and later must be set to ++ struct jffs2_full_dirent *scan_dents; /* Used during scan to hold ++ temporary lists of dirents, and later must be set to + NULL to mark the end of the raw_node_ref->next_in_ino + chain. */ + struct jffs2_inode_cache *next; + struct jffs2_raw_node_ref *nodes; +- __u32 ino; ++ uint32_t ino; + int nlink; ++ int state; + }; + ++/* Inode states for 'state' above. We need the 'GC' state to prevent ++ someone from doing a read_inode() while we're moving a 'REF_PRISTINE' ++ node without going through all the iget() nonsense */ ++#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */ ++#define INO_STATE_CHECKING 1 /* CRC checks in progress */ ++#define INO_STATE_PRESENT 2 /* In core */ ++#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ ++#define INO_STATE_GC 4 /* GCing a 'pristine' node */ ++#define INO_STATE_READING 5 /* In read_inode() */ ++ ++#define INOCACHE_HASHSIZE 128 ++ + struct jffs2_scan_info { + struct jffs2_full_dirent *dents; + struct jffs2_tmp_dnode_info *tmpnodes; ++ /* Latest i_size info */ ++ uint32_t version; ++ uint32_t isize; + }; + /* + Larger representation of a raw node, kept in-core only when the +@@ -123,9 +130,9 @@ + struct jffs2_full_dnode + { + struct jffs2_raw_node_ref *raw; +- __u32 ofs; /* Don't really need this, but optimisation */ +- __u32 size; +- __u32 frags; /* Number of fragments which currently refer ++ uint32_t ofs; /* Don't really need this, but optimisation */ ++ uint32_t size; ++ uint32_t frags; /* Number of fragments which currently refer + to this node. When this reaches zero, + the node is obsolete. + */ +@@ -140,15 +147,15 @@ + { + struct jffs2_tmp_dnode_info *next; + struct jffs2_full_dnode *fn; +- __u32 version; ++ uint32_t version; + }; + + struct jffs2_full_dirent + { + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *next; +- __u32 version; +- __u32 ino; /* == zero for unlink */ ++ uint32_t version; ++ uint32_t ino; /* == zero for unlink */ + unsigned int nhash; + unsigned char type; + unsigned char name[0]; +@@ -159,21 +166,23 @@ + */ + struct jffs2_node_frag + { +- struct jffs2_node_frag *next; ++ struct rb_node rb; + struct jffs2_full_dnode *node; /* NULL for holes */ +- __u32 size; +- __u32 ofs; /* Don't really need this, but optimisation */ ++ uint32_t size; ++ uint32_t ofs; /* Don't really need this, but optimisation */ + }; + + struct jffs2_eraseblock + { + struct list_head list; + int bad_count; +- __u32 offset; /* of this block in the MTD */ ++ uint32_t offset; /* of this block in the MTD */ + +- __u32 used_size; +- __u32 dirty_size; +- __u32 free_size; /* Note that sector_size - free_size ++ uint32_t unchecked_size; ++ uint32_t used_size; ++ uint32_t dirty_size; ++ uint32_t wasted_size; ++ uint32_t free_size; /* Note that sector_size - free_size + is the address of the first free space */ + struct jffs2_raw_node_ref *first_node; + struct jffs2_raw_node_ref *last_node; +@@ -190,45 +199,134 @@ + }; + + #define ACCT_SANITY_CHECK(c, jeb) do { \ +- if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \ +- printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \ +- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \ +- jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \ ++ struct jffs2_eraseblock *___j = jeb; \ ++ if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \ ++ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \ ++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \ ++ ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \ + BUG(); \ + } \ +- if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \ ++ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \ + printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \ +- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \ +- c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \ ++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \ ++ c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \ + BUG(); \ + } \ + } while(0) + ++static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_raw_node_ref *ref; ++ int i=0; ++ ++ printk(KERN_NOTICE); ++ for (ref = jeb->first_node; ref; ref = ref->next_phys) { ++ printk("%08x->", ref_offset(ref)); ++ if (++i == 8) { ++ i = 0; ++ printk("\n" KERN_NOTICE); ++ } ++ } ++ printk("\n"); ++} ++ ++ + #define ACCT_PARANOIA_CHECK(jeb) do { \ +- __u32 my_used_size = 0; \ ++ uint32_t my_used_size = 0; \ ++ uint32_t my_unchecked_size = 0; \ + struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ + while (ref2) { \ +- if (!(ref2->flash_offset & 1)) \ +- my_used_size += ref2->totlen; \ ++ if (unlikely(ref2->flash_offset < jeb->offset || \ ++ ref2->flash_offset > jeb->offset + c->sector_size)) { \ ++ printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \ ++ ref_offset(ref2), jeb->offset); \ ++ paranoia_failed_dump(jeb); \ ++ BUG(); \ ++ } \ ++ if (ref_flags(ref2) == REF_UNCHECKED) \ ++ my_unchecked_size += ref_totlen(c, jeb, ref2); \ ++ else if (!ref_obsolete(ref2)) \ ++ my_used_size += ref_totlen(c, jeb, ref2); \ ++ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ ++ printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ ++ ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ ++ jeb->last_node, ref_offset(jeb->last_node)); \ ++ paranoia_failed_dump(jeb); \ ++ BUG(); \ ++ } \ + ref2 = ref2->next_phys; \ + } \ + if (my_used_size != jeb->used_size) { \ + printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \ + BUG(); \ + } \ ++ if (my_unchecked_size != jeb->unchecked_size) { \ ++ printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \ ++ BUG(); \ ++ } \ + } while(0) + ++/* Calculate totlen from surrounding nodes or eraseblock */ ++static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, ++ struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *ref) ++{ ++ uint32_t ref_end; ++ ++ if (ref->next_phys) ++ ref_end = ref_offset(ref->next_phys); ++ else { ++ if (!jeb) ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ ++ /* Last node in block. Use free_space */ ++ BUG_ON(ref != jeb->last_node); ++ ref_end = jeb->offset + c->sector_size - jeb->free_size; ++ } ++ return ref_end - ref_offset(ref); ++} ++ ++static inline uint32_t ref_totlen(struct jffs2_sb_info *c, ++ struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *ref) ++{ ++ uint32_t ret; ++ ++ D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { ++ printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", ++ jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); ++ BUG(); ++ }) ++ ++#if 1 ++ ret = ref->__totlen; ++#else ++ /* This doesn't actually work yet */ ++ ret = __ref_totlen(c, jeb, ref); ++ if (ret != ref->__totlen) { ++ printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", ++ ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, ++ ret, ref->__totlen); ++ if (!jeb) ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ paranoia_failed_dump(jeb); ++ BUG(); ++ } ++#endif ++ return ret; ++} ++ ++ + #define ALLOC_NORMAL 0 /* Normal allocation */ + #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ + #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ ++#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ + +-#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */ +-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */ +-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */ +-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */ +-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */ +-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */ ++/* How much dirty space before it goes on the very_dirty_list */ ++#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) + ++/* check if dirty space is more than 255 Byte */ ++#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) + + #define PAD(x) (((x)+3)&~3) + +@@ -241,43 +339,75 @@ + return ((struct jffs2_inode_cache *)raw); + } + ++static inline struct jffs2_node_frag *frag_first(struct rb_root *root) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ if (!node) ++ return NULL; ++ while(node->rb_left) ++ node = node->rb_left; ++ return rb_entry(node, struct jffs2_node_frag, rb); ++} ++#define rb_parent(rb) ((rb)->rb_parent) ++#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) ++#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) ++#define frag_erase(frag, list) rb_erase(&frag->rb, list); ++ + /* nodelist.c */ + D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); + void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); +-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); + int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, + struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, +- __u32 *highest_version, __u32 *latest_mctime, +- __u32 *mctime_ver); ++ uint32_t *highest_version, uint32_t *latest_mctime, ++ uint32_t *mctime_ver); ++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); + struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino); + void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); + void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); + void jffs2_free_ino_caches(struct jffs2_sb_info *c); + void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); ++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); ++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); ++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base); ++struct rb_node *rb_next(struct rb_node *); ++struct rb_node *rb_prev(struct rb_node *); ++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); + + /* nodemgmt.c */ +-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio); +-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len); +-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty); ++int jffs2_thread_should_wake(struct jffs2_sb_info *c); ++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio); ++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); ++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); + void jffs2_complete_reservation(struct jffs2_sb_info *c); + void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); ++void jffs2_dump_block_lists(struct jffs2_sb_info *c); + + /* write.c */ +-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); +-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen); +-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen); ++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); ++ ++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); ++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); ++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *ri, unsigned char *buf, ++ uint32_t offset, uint32_t writelen, uint32_t *retlen); ++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen); ++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f); ++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen); ++ + + /* readinode.c */ +-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size); +-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn); ++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); + int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); +-void jffs2_read_inode (struct inode *); +-void jffs2_clear_inode (struct inode *); ++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ uint32_t ino, struct jffs2_raw_inode *latest_node); ++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); ++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); + + /* malloc.c */ +-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn); +-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd); +- + int jffs2_create_slab_caches(void); + void jffs2_destroy_slab_caches(void); + +@@ -301,54 +431,41 @@ + /* gc.c */ + int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); + +-/* background.c */ +-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); +-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); +-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); +- +-/* dir.c */ +-extern struct file_operations jffs2_dir_operations; +-extern struct inode_operations jffs2_dir_inode_operations; +- +-/* file.c */ +-extern struct file_operations jffs2_file_operations; +-extern struct inode_operations jffs2_file_inode_operations; +-extern struct address_space_operations jffs2_file_address_operations; +-int jffs2_null_fsync(struct file *, struct dentry *, int); +-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr); +-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); +-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); +-int jffs2_readpage (struct file *, struct page *); +-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); +-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); +- +-/* ioctl.c */ +-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +- + /* read.c */ + int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); ++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ unsigned char *buf, uint32_t offset, uint32_t len); ++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); + + /* compr.c */ +-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen); ++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen); ++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen); ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + + /* scan.c */ + int jffs2_scan_medium(struct jffs2_sb_info *c); ++void jffs2_rotate_lists(struct jffs2_sb_info *c); + + /* build.c */ +-int jffs2_build_filesystem(struct jffs2_sb_info *c); +- +-/* symlink.c */ +-extern struct inode_operations jffs2_symlink_inode_operations; ++int jffs2_do_mount_fs(struct jffs2_sb_info *c); + + /* erase.c */ + void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c); +-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c); +-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); ++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); ++ ++#ifdef CONFIG_JFFS2_FS_NAND ++/* wbuf.c */ ++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); ++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); ++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++#endif + + /* compr_zlib.c */ + int jffs2_zlib_init(void); + void jffs2_zlib_exit(void); ++ ++#endif /* __JFFS2_NODELIST_H__ */ +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodemgmt.c linux/fs/jffs2/nodemgmt.c +--- linux-mips-2.4.27/fs/jffs2/nodemgmt.c 2002-06-27 00:36:20.000000000 +0200 ++++ linux/fs/jffs2/nodemgmt.c 2004-11-19 10:25:12.115166960 +0100 +@@ -1,45 +1,21 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $ ++ * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + + #include + #include +-#include + #include +-#include ++#include ++#include /* For cond_resched() */ + #include "nodelist.h" + + /** +@@ -62,53 +38,95 @@ + * for the requested allocation. + */ + +-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len); ++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); + +-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio) ++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) + { + int ret = -EAGAIN; +- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE; ++ int blocksneeded = c->resv_blocks_write; + /* align it */ + minsize = PAD(minsize); + +- if (prio == ALLOC_DELETION) +- blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION; +- + D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); + down(&c->alloc_sem); + + D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + +- /* this needs a little more thought */ ++ /* this needs a little more thought (true :)) */ + while(ret == -EAGAIN) { + while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { + int ret; ++ uint32_t dirty, avail; ++ ++ /* calculate real dirty size ++ * dirty_size contains blocks on erase_pending_list ++ * those blocks are counted in c->nr_erasing_blocks. ++ * If one block is actually erased, it is not longer counted as dirty_space ++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it ++ * with c->nr_erasing_blocks * c->sector_size again. ++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks ++ * This helps us to force gc and pick eventually a clean block to spread the load. ++ * We add unchecked_size here, as we hopefully will find some space to use. ++ * This will affect the sum only once, as gc first finishes checking ++ * of nodes. ++ */ ++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; ++ if (dirty < c->nospc_dirty_size) { ++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { ++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"); ++ break; ++ } ++ D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", ++ dirty, c->unchecked_size, c->sector_size)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ up(&c->alloc_sem); ++ return -ENOSPC; ++ } + ++ /* Calc possibly available space. Possibly available means that we ++ * don't know, if unchecked size contains obsoleted nodes, which could give us some ++ * more usable space. This will affect the sum only once, as gc first finishes checking ++ * of nodes. ++ + Return -ENOSPC, if the maximum possibly available space is less or equal than ++ * blocksneeded * sector_size. ++ * This blocks endless gc looping on a filesystem, which is nearly full, even if ++ * the check above passes. ++ */ ++ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; ++ if ( (avail / c->sector_size) <= blocksneeded) { ++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { ++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", ++ avail, blocksneeded * c->sector_size)); ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); +- if (c->dirty_size < c->sector_size) { +- D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size)); +- spin_unlock_bh(&c->erase_completion_lock); + return -ENOSPC; + } +- D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", +- c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, +- c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); +- spin_unlock_bh(&c->erase_completion_lock); ++ ++ up(&c->alloc_sem); ++ ++ D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", ++ c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, ++ c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); ++ spin_unlock(&c->erase_completion_lock); + + ret = jffs2_garbage_collect_pass(c); + if (ret) + return ret; + +- if (current->need_resched) +- schedule(); ++ cond_resched(); + + if (signal_pending(current)) + return -EINTR; + + down(&c->alloc_sem); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + } + + ret = jffs2_do_reserve_space(c, minsize, ofs, len); +@@ -116,45 +134,72 @@ + D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); + } + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + if (ret) + up(&c->alloc_sem); + return ret; + } + +-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len) ++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) + { + int ret = -EAGAIN; + minsize = PAD(minsize); + + D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + while(ret == -EAGAIN) { + ret = jffs2_do_reserve_space(c, minsize, ofs, len); + if (ret) { + D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); + } + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + return ret; + } + + /* Called with alloc sem _and_ erase_completion_lock */ +-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len) ++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) + { + struct jffs2_eraseblock *jeb = c->nextblock; + + restart: + if (jeb && minsize > jeb->free_size) { + /* Skip the end of this block and file it as having some dirty space */ +- c->dirty_size += jeb->free_size; ++ /* If there's a pending write to it, flush now */ ++ if (jffs2_wbuf_dirty(c)) { ++ spin_unlock(&c->erase_completion_lock); ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ jeb = c->nextblock; ++ goto restart; ++ } ++ c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; +- jeb->dirty_size += jeb->free_size; ++ jeb->wasted_size += jeb->free_size; + jeb->free_size = 0; ++ ++ /* Check, if we have a dirty block now, or if it was dirty already */ ++ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->dirty_size += jeb->wasted_size; ++ jeb->wasted_size = 0; ++ if (VERYDIRTY(c, jeb->dirty_size)) { ++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ list_add_tail(&jeb->list, &c->very_dirty_list); ++ } else { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + list_add_tail(&jeb->list, &c->dirty_list); ++ } ++ } else { ++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ list_add_tail(&jeb->list, &c->clean_list); ++ } + c->nextblock = jeb = NULL; + } + +@@ -164,33 +209,44 @@ + + if (list_empty(&c->free_list)) { + +- DECLARE_WAITQUEUE(wait, current); ++ if (!c->nr_erasing_blocks && ++ !list_empty(&c->erasable_list)) { ++ struct jffs2_eraseblock *ejeb; ++ ++ ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); ++ list_del(&ejeb->list); ++ list_add_tail(&ejeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", ++ ejeb->offset)); ++ } ++ ++ if (!c->nr_erasing_blocks && ++ !list_empty(&c->erasable_pending_wbuf_list)) { ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); ++ /* c->nextblock is NULL, no update to c->nextblock allowed */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ /* Have another go. It'll be on the erasable_list now */ ++ return -EAGAIN; ++ } + + if (!c->nr_erasing_blocks) { +-// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) { + /* Ouch. We're in GC, or we wouldn't have got here. + And there's no space left. At all. */ +- printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", +- c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); ++ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", ++ c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", ++ list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); + return -ENOSPC; + } +- /* Make sure this can't deadlock. Someone has to start the erases +- of erase_pending blocks */ +- set_current_state(TASK_INTERRUPTIBLE); +- add_wait_queue(&c->erase_wait, &wait); +- D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", +- c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no")); +- if (!list_empty(&c->erase_pending_list)) { +- D1(printk(KERN_DEBUG "Triggering pending erases\n")); +- jffs2_erase_pending_trigger(c); +- } +- spin_unlock_bh(&c->erase_completion_lock); +- schedule(); +- remove_wait_queue(&c->erase_wait, &wait); +- spin_lock_bh(&c->erase_completion_lock); +- if (signal_pending(current)) { +- return -EINTR; +- } ++ ++ spin_unlock(&c->erase_completion_lock); ++ /* Don't wait for it; just erase one right now */ ++ jffs2_erase_pending_blocks(c, 1); ++ spin_lock(&c->erase_completion_lock); ++ + /* An erase may have failed, decreasing the + amount of free space available. So we must + restart from the beginning */ +@@ -201,7 +257,8 @@ + list_del(next); + c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); + c->nr_free_blocks--; +- if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) { ++ ++ if (jeb->free_size != c->sector_size - c->cleanmarker_size) { + printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); + goto restart; + } +@@ -210,6 +267,20 @@ + enough space */ + *ofs = jeb->offset + (c->sector_size - jeb->free_size); + *len = jeb->free_size; ++ ++ if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && ++ !jeb->first_node->next_in_ino) { ++ /* Only node in it beforehand was a CLEANMARKER node (we think). ++ So mark it obsolete now that there's going to be another node ++ in the block. This will reduce used_size to zero but We've ++ already set c->nextblock so that jffs2_mark_node_obsolete() ++ won't try to refile it to the dirty_list. ++ */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_mark_node_obsolete(c, jeb->first_node); ++ spin_lock(&c->erase_completion_lock); ++ } ++ + D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); + return 0; + } +@@ -217,9 +288,9 @@ + /** + * jffs2_add_physical_node_ref - add a physical node reference to the list + * @c: superblock info +- * @ofs: physical location of this physical node ++ * @new: new node reference to add + * @len: length of this physical node +- * @ino: inode number with which this physical node is associated ++ * @dirty: dirty flag for new node + * + * Should only be used to report nodes for which space has been allocated + * by jffs2_reserve_space. +@@ -227,47 +298,58 @@ + * Must be called with the alloc_sem held. + */ + +-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty) ++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) + { + struct jffs2_eraseblock *jeb; ++ uint32_t len; ++ ++ jeb = &c->blocks[new->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, new); + +- len = PAD(len); +- jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size]; +- D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len)); ++ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); + #if 1 +- if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) { ++ if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { + printk(KERN_WARNING "argh. node added in wrong place\n"); + jffs2_free_raw_node_ref(new); + return -EINVAL; + } + #endif ++ spin_lock(&c->erase_completion_lock); ++ + if (!jeb->first_node) + jeb->first_node = new; + if (jeb->last_node) + jeb->last_node->next_phys = new; + jeb->last_node = new; + +- spin_lock_bh(&c->erase_completion_lock); + jeb->free_size -= len; + c->free_size -= len; +- if (dirty) { +- new->flash_offset |= 1; ++ if (ref_obsolete(new)) { + jeb->dirty_size += len; + c->dirty_size += len; + } else { + jeb->used_size += len; + c->used_size += len; + } +- spin_unlock_bh(&c->erase_completion_lock); ++ + if (!jeb->free_size && !jeb->dirty_size) { + /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ if (jffs2_wbuf_dirty(c)) { ++ /* Flush the last write in the block if it's outstanding */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ } ++ + list_add_tail(&jeb->list, &c->clean_list); + c->nextblock = NULL; + } + ACCT_SANITY_CHECK(c,jeb); +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ spin_unlock(&c->erase_completion_lock); + + return 0; + } +@@ -280,20 +362,34 @@ + up(&c->alloc_sem); + } + ++static inline int on_list(struct list_head *obj, struct list_head *head) ++{ ++ struct list_head *this; ++ ++ list_for_each(this, head) { ++ if (this == obj) { ++ D1(printk("%p is on list at %p\n", obj, head)); ++ return 1; ++ ++ } ++ } ++ return 0; ++} ++ + void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) + { + struct jffs2_eraseblock *jeb; + int blocknr; + struct jffs2_unknown_node n; +- int ret; +- ssize_t retlen; ++ int ret, addedsize; ++ size_t retlen; + + if(!ref) { + printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); + return; + } +- if (ref->flash_offset & 1) { +- D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3)); ++ if (ref_obsolete(ref)) { ++ D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref))); + return; + } + blocknr = ref->flash_offset / c->sector_size; +@@ -302,22 +398,63 @@ + BUG(); + } + jeb = &c->blocks[blocknr]; +- if (jeb->used_size < ref->totlen) { ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { ++ printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", ++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); ++ BUG(); ++ }) ++ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); ++ jeb->unchecked_size -= ref_totlen(c, jeb, ref); ++ c->unchecked_size -= ref_totlen(c, jeb, ref); ++ } else { ++ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { + printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", +- ref->totlen, blocknr, ref->flash_offset, jeb->used_size); ++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); + BUG(); ++ }) ++ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); ++ jeb->used_size -= ref_totlen(c, jeb, ref); ++ c->used_size -= ref_totlen(c, jeb, ref); ++ } ++ ++ // Take care, that wasted size is taken into concern ++ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { ++ D1(printk("Dirtying\n")); ++ addedsize = ref_totlen(c, jeb, ref); ++ jeb->dirty_size += ref_totlen(c, jeb, ref); ++ c->dirty_size += ref_totlen(c, jeb, ref); ++ ++ /* Convert wasted space to dirty, if not a bad block */ ++ if (jeb->wasted_size) { ++ if (on_list(&jeb->list, &c->bad_used_list)) { ++ D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", ++ jeb->offset)); ++ addedsize = 0; /* To fool the refiling code later */ ++ } else { ++ D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", ++ jeb->wasted_size, jeb->offset)); ++ addedsize += jeb->wasted_size; ++ jeb->dirty_size += jeb->wasted_size; ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->wasted_size = 0; + } +- +- spin_lock_bh(&c->erase_completion_lock); +- jeb->used_size -= ref->totlen; +- jeb->dirty_size += ref->totlen; +- c->used_size -= ref->totlen; +- c->dirty_size += ref->totlen; +- ref->flash_offset |= 1; ++ } ++ } else { ++ D1(printk("Wasting\n")); ++ addedsize = 0; ++ jeb->wasted_size += ref_totlen(c, jeb, ref); ++ c->wasted_size += ref_totlen(c, jeb, ref); ++ } ++ ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; + + ACCT_SANITY_CHECK(c, jeb); + +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + + if (c->flags & JFFS2_SB_FLAG_MOUNTING) { + /* Mount in progress. Don't muck about with the block +@@ -325,68 +462,280 @@ + obliterate nodes that look obsolete. If they weren't + marked obsolete on the flash at the time they _became_ + obsolete, there was probably a reason for that. */ +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + return; + } ++ + if (jeb == c->nextblock) { + D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); +- } else if (jeb == c->gcblock) { +- D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); +-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static +- data and a few blocks free, and you just create new files and keep deleting/overwriting +- them, then you'd keep erasing and reusing those blocks without ever moving stuff around. +- So we leave completely obsoleted blocks on the dirty_list and let the GC delete them +- when it finds them there. That way, we still get the 'once in a while, take a clean block' +- to spread out the flash usage */ +- } else if (!jeb->used_size) { ++ } else if (!jeb->used_size && !jeb->unchecked_size) { ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset)); ++ c->gcblock = NULL; ++ } else { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); + list_del(&jeb->list); ++ } ++ if (jffs2_wbuf_dirty(c)) { ++ D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); ++ } else { ++ if (jiffies & 127) { ++ /* Most of the time, we just erase it immediately. Otherwise we ++ spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); +- // OFNI_BS_2SFFJ(c)->s_dirt = 1; ++ } else { ++ /* Sometimes, however, we leave it elsewhere so it doesn't get ++ immediately reused, and we spread the load a bit. */ ++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_list); ++ } ++ } + D1(printk(KERN_DEBUG "Done OK\n")); +-#endif +- } else if (jeb->dirty_size == ref->totlen) { ++ } else if (jeb == c->gcblock) { ++ D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset)); ++ } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset)); + list_del(&jeb->list); + D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); + list_add_tail(&jeb->list, &c->dirty_list); ++ } else if (VERYDIRTY(c, jeb->dirty_size) && ++ !VERYDIRTY(c, jeb->dirty_size - addedsize)) { ++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); ++ list_del(&jeb->list); ++ D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); ++ list_add_tail(&jeb->list, &c->very_dirty_list); ++ } else { ++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + } +- spin_unlock_bh(&c->erase_completion_lock); + +- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM) ++ spin_unlock(&c->erase_completion_lock); ++ ++ if (!jffs2_can_mark_obsolete(c)) + return; +- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) ++ if (jffs2_is_readonly(c)) + return; + +- D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3)); +- ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n); ++ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ++ ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); + if (ret) { +- printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret); ++ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); + return; + } + if (retlen != sizeof(n)) { +- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); ++ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + return; + } +- if (PAD(n.totlen) != PAD(ref->totlen)) { +- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen); ++ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { ++ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); + return; + } +- if (!(n.nodetype & JFFS2_NODE_ACCURATE)) { +- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype)); ++ if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { ++ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); + return; + } +- n.nodetype &= ~JFFS2_NODE_ACCURATE; +- ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n); ++ /* XXX FIXME: This is ugly now */ ++ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ++ ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); + if (ret) { +- printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret); ++ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); + return; + } + if (retlen != sizeof(n)) { +- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); ++ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + return; + } + } ++ ++#if CONFIG_JFFS2_FS_DEBUG > 0 ++void jffs2_dump_block_lists(struct jffs2_sb_info *c) ++{ ++ ++ ++ printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); ++ printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); ++ printk(KERN_DEBUG "used_size: %08x\n", c->used_size); ++ printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); ++ printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size); ++ printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size); ++ printk(KERN_DEBUG "free_size: %08x\n", c->free_size); ++ printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); ++ printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); ++ printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); ++ printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write); ++ ++ if (c->nextblock) { ++ printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); ++ } else { ++ printk(KERN_DEBUG "nextblock: NULL\n"); ++ } ++ if (c->gcblock) { ++ printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); ++ } else { ++ printk(KERN_DEBUG "gcblock: NULL\n"); ++ } ++ if (list_empty(&c->clean_list)) { ++ printk(KERN_DEBUG "clean_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->clean_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->wasted_size; ++ printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->very_dirty_list)) { ++ printk(KERN_DEBUG "very_dirty_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->very_dirty_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->dirty_size; ++ printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", ++ numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->dirty_list)) { ++ printk(KERN_DEBUG "dirty_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->dirty_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->dirty_size; ++ printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", ++ numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->erasable_list)) { ++ printk(KERN_DEBUG "erasable_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasable_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erasing_list)) { ++ printk(KERN_DEBUG "erasing_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasing_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erase_pending_list)) { ++ printk(KERN_DEBUG "erase_pending_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erase_pending_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erasable_pending_wbuf_list)) { ++ printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasable_pending_wbuf_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->free_list)) { ++ printk(KERN_DEBUG "free_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->free_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->bad_list)) { ++ printk(KERN_DEBUG "bad_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->bad_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->bad_used_list)) { ++ printk(KERN_DEBUG "bad_used_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->bad_used_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++} ++#endif /* CONFIG_JFFS2_FS_DEBUG */ ++ ++int jffs2_thread_should_wake(struct jffs2_sb_info *c) ++{ ++ int ret = 0; ++ uint32_t dirty; ++ ++ if (c->unchecked_size) { ++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", ++ c->unchecked_size, c->checked_ino)); ++ return 1; ++ } ++ ++ /* dirty_size contains blocks on erase_pending_list ++ * those blocks are counted in c->nr_erasing_blocks. ++ * If one block is actually erased, it is not longer counted as dirty_space ++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it ++ * with c->nr_erasing_blocks * c->sector_size again. ++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks ++ * This helps us to force gc and pick eventually a clean block to spread the load. ++ */ ++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; ++ ++ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && ++ (dirty > c->nospc_dirty_size)) ++ ret = 1; ++ ++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", ++ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); ++ ++ return ret; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/os-linux.h linux/fs/jffs2/os-linux.h +--- linux-mips-2.4.27/fs/jffs2/os-linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/os-linux.h 2004-11-19 10:25:12.116166808 +0100 +@@ -0,0 +1,212 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2002-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: os-linux.h,v 1.41 2003/11/26 13:02:46 dwmw2 Exp $ ++ * ++ */ ++ ++#ifndef __JFFS2_OS_LINUX_H__ ++#define __JFFS2_OS_LINUX_H__ ++#include ++ ++/* JFFS2 uses Linux mode bits natively -- no need for conversion */ ++#define os_to_jffs2_mode(x) (x) ++#define jffs2_to_os_mode(x) (x) ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) ++#define kstatfs statfs ++#endif ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) ++#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) ++#define JFFS2_SB_INFO(sb) (sb->s_fs_info) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) ++#elif defined(JFFS2_OUT_OF_KERNEL) ++#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u) ++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) ++#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++#else ++#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i) ++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) ++#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++#endif ++ ++ ++#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) ++#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) ++#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) ++#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1) ++#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) ++#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) ++#else ++#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) ++#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) ++#endif ++ ++/* Urgh. The things we do to keep the 2.4 build working */ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47) ++#define ITIME(sec) ((struct timespec){sec, 0}) ++#define I_SEC(tv) ((tv).tv_sec) ++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec) ++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec) ++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec) ++#else ++#define ITIME(x) (x) ++#define I_SEC(x) (x) ++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime) ++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime) ++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) ++#endif ++ ++#define sleep_on_spinunlock(wq, s) \ ++ do { \ ++ DECLARE_WAITQUEUE(__wait, current); \ ++ add_wait_queue((wq), &__wait); \ ++ set_current_state(TASK_UNINTERRUPTIBLE); \ ++ spin_unlock(s); \ ++ schedule(); \ ++ remove_wait_queue((wq), &__wait); \ ++ } while(0) ++ ++static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) ++{ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++ f->highest_version = 0; ++ f->fragtree = RB_ROOT; ++ f->metadata = NULL; ++ f->dents = NULL; ++ f->flags = 0; ++ f->usercompr = 0; ++#else ++ memset(f, 0, sizeof(*f)); ++ init_MUTEX_LOCKED(&f->sem); ++#endif ++} ++ ++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) ++ ++#ifndef CONFIG_JFFS2_FS_NAND ++#define jffs2_can_mark_obsolete(c) (1) ++#define jffs2_cleanmarker_oob(c) (0) ++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) ++ ++#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; }) ++#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; }) ++#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) ++#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0) ++#define jffs2_nand_flash_setup(c) (0) ++#define jffs2_nand_flash_cleanup(c) do {} while(0) ++#define jffs2_wbuf_dirty(c) (0) ++#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) ++#define jffs2_wbuf_timeout NULL ++#define jffs2_wbuf_process NULL ++ ++#else /* NAND support present */ ++ ++#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) ++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) ++ ++#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) ++struct kstatfs; ++ ++/* wbuf.c */ ++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); ++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); ++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); ++int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); ++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++void jffs2_wbuf_timeout(unsigned long data); ++void jffs2_wbuf_process(void *data); ++int jffs2_nand_flash_setup(struct jffs2_sb_info *c); ++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); ++#endif /* NAND */ ++ ++/* erase.c */ ++static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) ++{ ++ OFNI_BS_2SFFJ(c)->s_dirt = 1; ++} ++ ++/* background.c */ ++int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); ++void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); ++void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); ++ ++/* dir.c */ ++extern struct file_operations jffs2_dir_operations; ++extern struct inode_operations jffs2_dir_inode_operations; ++ ++/* file.c */ ++extern struct file_operations jffs2_file_operations; ++extern struct inode_operations jffs2_file_inode_operations; ++extern struct address_space_operations jffs2_file_address_operations; ++int jffs2_fsync(struct file *, struct dentry *, int); ++int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); ++int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); ++int jffs2_readpage (struct file *, struct page *); ++int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); ++int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); ++ ++/* ioctl.c */ ++int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); ++ ++/* symlink.c */ ++extern struct inode_operations jffs2_symlink_inode_operations; ++ ++/* fs.c */ ++int jffs2_setattr (struct dentry *, struct iattr *); ++void jffs2_read_inode (struct inode *); ++void jffs2_clear_inode (struct inode *); ++void jffs2_dirty_inode(struct inode *inode); ++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, ++ struct jffs2_raw_inode *ri); ++int jffs2_statfs (struct super_block *, struct kstatfs *); ++void jffs2_write_super (struct super_block *); ++int jffs2_remount_fs (struct super_block *, int *, char *); ++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); ++void jffs2_gc_release_inode(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f); ++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, ++ int inum, int nlink); ++ ++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ unsigned long offset, ++ unsigned long *priv); ++void jffs2_gc_release_page(struct jffs2_sb_info *c, ++ unsigned char *pg, ++ unsigned long *priv); ++ ++ ++/* writev.c */ ++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen); ++ ++/* Compression config */ ++#define JFFS2_COMPRESSION ++#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ ++#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */ ++#define JFFS2_USE_ZLIB ++#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */ ++ ++ ++#endif /* __JFFS2_OS_LINUX_H__ */ ++ ++ +diff -Nurb linux-mips-2.4.27/fs/jffs2/pushpull.h linux/fs/jffs2/pushpull.h +--- linux-mips-2.4.27/fs/jffs2/pushpull.h 2001-11-05 21:16:19.000000000 +0100 ++++ linux/fs/jffs2/pushpull.h 2004-11-19 10:25:12.118166504 +0100 +@@ -1,42 +1,21 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001, 2002 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $ ++ * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * + */ + + #ifndef __PUSHPULL_H__ + #define __PUSHPULL_H__ ++ ++#include ++ + struct pushpull { + unsigned char *buf; + unsigned int buflen; +@@ -44,9 +23,36 @@ + unsigned int reserve; + }; + +-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned); +-int pushbit(struct pushpull *pp, int bit, int use_reserved); +-int pushedbits(struct pushpull *pp); ++ ++static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) ++{ ++ pp->buf = buf; ++ pp->buflen = buflen; ++ pp->ofs = ofs; ++ pp->reserve = reserve; ++} ++ ++static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) ++{ ++ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { ++ return -ENOSPC; ++ } ++ ++ if (bit) { ++ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); ++ } ++ else { ++ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); ++ } ++ pp->ofs++; ++ ++ return 0; ++} ++ ++static inline int pushedbits(struct pushpull *pp) ++{ ++ return pp->ofs; ++} + + static inline int pullbit(struct pushpull *pp) + { +diff -Nurb linux-mips-2.4.27/fs/jffs2/rbtree.c linux/fs/jffs2/rbtree.c +--- linux-mips-2.4.27/fs/jffs2/rbtree.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/rbtree.c 2004-11-19 10:25:12.120166200 +0100 +@@ -0,0 +1,363 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli ++ (C) 2002 David Woodhouse ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ $Id: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $ ++*/ ++ ++#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */ ++#error "Licence problem. eCos has its own rbtree code." ++#endif ++ ++#include ++#include ++ ++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \ ++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE)) ++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * right = node->rb_right; ++ ++ if ((node->rb_right = right->rb_left)) ++ right->rb_left->rb_parent = node; ++ right->rb_left = node; ++ ++ if ((right->rb_parent = node->rb_parent)) ++ { ++ if (node == node->rb_parent->rb_left) ++ node->rb_parent->rb_left = right; ++ else ++ node->rb_parent->rb_right = right; ++ } ++ else ++ root->rb_node = right; ++ node->rb_parent = right; ++} ++ ++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * left = node->rb_left; ++ ++ if ((node->rb_left = left->rb_right)) ++ left->rb_right->rb_parent = node; ++ left->rb_right = node; ++ ++ if ((left->rb_parent = node->rb_parent)) ++ { ++ if (node == node->rb_parent->rb_right) ++ node->rb_parent->rb_right = left; ++ else ++ node->rb_parent->rb_left = left; ++ } ++ else ++ root->rb_node = left; ++ node->rb_parent = left; ++} ++ ++void rb_insert_color(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * parent, * gparent; ++ ++ while ((parent = node->rb_parent) && parent->rb_color == RB_RED) ++ { ++ gparent = parent->rb_parent; ++ ++ if (parent == gparent->rb_left) ++ { ++ { ++ register struct rb_node * uncle = gparent->rb_right; ++ if (uncle && uncle->rb_color == RB_RED) ++ { ++ uncle->rb_color = RB_BLACK; ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_right == node) ++ { ++ register struct rb_node * tmp; ++ __rb_rotate_left(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ __rb_rotate_right(gparent, root); ++ } else { ++ { ++ register struct rb_node * uncle = gparent->rb_left; ++ if (uncle && uncle->rb_color == RB_RED) ++ { ++ uncle->rb_color = RB_BLACK; ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_left == node) ++ { ++ register struct rb_node * tmp; ++ __rb_rotate_right(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ __rb_rotate_left(gparent, root); ++ } ++ } ++ ++ root->rb_node->rb_color = RB_BLACK; ++} ++ ++static void __rb_erase_color(struct rb_node * node, struct rb_node * parent, ++ struct rb_root * root) ++{ ++ struct rb_node * other; ++ ++ while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) ++ { ++ if (parent->rb_left == node) ++ { ++ other = parent->rb_right; ++ if (other->rb_color == RB_RED) ++ { ++ other->rb_color = RB_BLACK; ++ parent->rb_color = RB_RED; ++ __rb_rotate_left(parent, root); ++ other = parent->rb_right; ++ } ++ if ((!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ && (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK)) ++ { ++ other->rb_color = RB_RED; ++ node = parent; ++ parent = node->rb_parent; ++ } ++ else ++ { ++ if (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK) ++ { ++ register struct rb_node * o_left; ++ if ((o_left = other->rb_left)) ++ o_left->rb_color = RB_BLACK; ++ other->rb_color = RB_RED; ++ __rb_rotate_right(other, root); ++ other = parent->rb_right; ++ } ++ other->rb_color = parent->rb_color; ++ parent->rb_color = RB_BLACK; ++ if (other->rb_right) ++ other->rb_right->rb_color = RB_BLACK; ++ __rb_rotate_left(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ else ++ { ++ other = parent->rb_left; ++ if (other->rb_color == RB_RED) ++ { ++ other->rb_color = RB_BLACK; ++ parent->rb_color = RB_RED; ++ __rb_rotate_right(parent, root); ++ other = parent->rb_left; ++ } ++ if ((!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ && (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK)) ++ { ++ other->rb_color = RB_RED; ++ node = parent; ++ parent = node->rb_parent; ++ } ++ else ++ { ++ if (!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ { ++ register struct rb_node * o_right; ++ if ((o_right = other->rb_right)) ++ o_right->rb_color = RB_BLACK; ++ other->rb_color = RB_RED; ++ __rb_rotate_left(other, root); ++ other = parent->rb_left; ++ } ++ other->rb_color = parent->rb_color; ++ parent->rb_color = RB_BLACK; ++ if (other->rb_left) ++ other->rb_left->rb_color = RB_BLACK; ++ __rb_rotate_right(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ } ++ if (node) ++ node->rb_color = RB_BLACK; ++} ++ ++void rb_erase(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * child, * parent; ++ int color; ++ ++ if (!node->rb_left) ++ child = node->rb_right; ++ else if (!node->rb_right) ++ child = node->rb_left; ++ else ++ { ++ struct rb_node * old = node, * left; ++ ++ node = node->rb_right; ++ while ((left = node->rb_left)) ++ node = left; ++ child = node->rb_right; ++ parent = node->rb_parent; ++ color = node->rb_color; ++ ++ if (child) ++ child->rb_parent = parent; ++ if (parent) ++ { ++ if (parent->rb_left == node) ++ parent->rb_left = child; ++ else ++ parent->rb_right = child; ++ } ++ else ++ root->rb_node = child; ++ ++ if (node->rb_parent == old) ++ parent = node; ++ node->rb_parent = old->rb_parent; ++ node->rb_color = old->rb_color; ++ node->rb_right = old->rb_right; ++ node->rb_left = old->rb_left; ++ ++ if (old->rb_parent) ++ { ++ if (old->rb_parent->rb_left == old) ++ old->rb_parent->rb_left = node; ++ else ++ old->rb_parent->rb_right = node; ++ } else ++ root->rb_node = node; ++ ++ old->rb_left->rb_parent = node; ++ if (old->rb_right) ++ old->rb_right->rb_parent = node; ++ goto color; ++ } ++ ++ parent = node->rb_parent; ++ color = node->rb_color; ++ ++ if (child) ++ child->rb_parent = parent; ++ if (parent) ++ { ++ if (parent->rb_left == node) ++ parent->rb_left = child; ++ else ++ parent->rb_right = child; ++ } ++ else ++ root->rb_node = child; ++ ++ color: ++ if (color == RB_BLACK) ++ __rb_erase_color(child, parent, root); ++} ++#endif /* Before 2.4.11 */ ++ ++ /* These routines haven't made it into 2.4 (yet) */ ++struct rb_node *rb_next(struct rb_node *node) ++{ ++ /* If we have a right-hand child, go down and then left as far ++ as we can. */ ++ if (node->rb_right) { ++ node = node->rb_right; ++ while (node->rb_left) ++ node=node->rb_left; ++ return node; ++ } ++ ++ /* No right-hand children. Everything down and left is ++ smaller than us, so any 'next' node must be in the general ++ direction of our parent. Go up the tree; any time the ++ ancestor is a right-hand child of its parent, keep going ++ up. First time it's a left-hand child of its parent, said ++ parent is our 'next' node. */ ++ while (node->rb_parent && node == node->rb_parent->rb_right) ++ node = node->rb_parent; ++ ++ return node->rb_parent; ++} ++ ++struct rb_node *rb_prev(struct rb_node *node) ++{ ++ if (node->rb_left) { ++ node = node->rb_left; ++ while (node->rb_right) ++ node=node->rb_right; ++ return node; ++ } ++ while (node->rb_parent && node == node->rb_parent->rb_left) ++ node = node->rb_parent; ++ ++ return node->rb_parent; ++} ++ ++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) ++{ ++ struct rb_node *parent = victim->rb_parent; ++ ++ /* Set the surrounding nodes to point to the replacement */ ++ if (parent) { ++ if (victim == parent->rb_left) ++ parent->rb_left = new; ++ else ++ parent->rb_right = new; ++ } else { ++ root->rb_node = new; ++ } ++ if (victim->rb_left) ++ victim->rb_left->rb_parent = new; ++ if (victim->rb_right) ++ victim->rb_right->rb_parent = new; ++ ++ /* Copy the pointers/colour from the victim to the replacement */ ++ *new = *victim; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/read.c linux/fs/jffs2/read.c +--- linux-mips-2.4.27/fs/jffs2/read.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/read.c 2004-11-19 10:25:12.121166048 +0100 +@@ -1,52 +1,29 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: read.c,v 1.13.2.2 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $ + * + */ + + #include + #include +-#include ++#include ++#include + #include ++#include + #include "nodelist.h" +-#include + + int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) + { + struct jffs2_raw_inode *ri; + size_t readlen; +- __u32 crc; ++ uint32_t crc; + unsigned char *decomprbuf = NULL; + unsigned char *readbuf = NULL; + int ret = 0; +@@ -55,35 +32,41 @@ + if (!ri) + return -ENOMEM; + +- ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri); ++ ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); + if (ret) { + jffs2_free_raw_inode(ri); +- printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret); ++ printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); + return ret; + } + if (readlen != sizeof(*ri)) { + jffs2_free_raw_inode(ri); +- printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", +- fd->raw->flash_offset & ~3, sizeof(*ri), readlen); ++ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", ++ ref_offset(fd->raw), sizeof(*ri), readlen); + return -EIO; + } + crc = crc32(0, ri, sizeof(*ri)-8); + +- D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf)); +- if (crc != ri->node_crc) { +- printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3); ++ D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", ++ ref_offset(fd->raw), je32_to_cpu(ri->node_crc), ++ crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), ++ je32_to_cpu(ri->offset), buf)); ++ if (crc != je32_to_cpu(ri->node_crc)) { ++ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ++ je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_ri; + } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ +- if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) { ++ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && ++ je32_to_cpu(ri->csize)) { + ri->dsize = ri->csize; +- ri->csize = 0; ++ ri->csize = cpu_to_je32(0); + } + +- D1(if(ofs + len > ri->dsize) { +- printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize); ++ D1(if(ofs + len > je32_to_cpu(ri->dsize)) { ++ printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", ++ len, ofs, je32_to_cpu(ri->dsize)); + ret = -EINVAL; + goto out_ri; + }); +@@ -100,18 +83,18 @@ + Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy + Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy + */ +- if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) { ++ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { + readbuf = buf; + } else { +- readbuf = kmalloc(ri->csize, GFP_KERNEL); ++ readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL); + if (!readbuf) { + ret = -ENOMEM; + goto out_ri; + } + } + if (ri->compr != JFFS2_COMPR_NONE) { +- if (len < ri->dsize) { +- decomprbuf = kmalloc(ri->dsize, GFP_KERNEL); ++ if (len < je32_to_cpu(ri->dsize)) { ++ decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL); + if (!decomprbuf) { + ret = -ENOMEM; + goto out_readbuf; +@@ -123,31 +106,35 @@ + decomprbuf = readbuf; + } + +- D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf)); +- ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf); ++ D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), ++ readbuf)); ++ ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), ++ je32_to_cpu(ri->csize), &readlen, readbuf); + +- if (!ret && readlen != ri->csize) ++ if (!ret && readlen != je32_to_cpu(ri->csize)) + ret = -EIO; + if (ret) + goto out_decomprbuf; + +- crc = crc32(0, readbuf, ri->csize); +- if (crc != ri->data_crc) { +- printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3); ++ crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); ++ if (crc != je32_to_cpu(ri->data_crc)) { ++ printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ++ je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_decomprbuf; + } + D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); + if (ri->compr != JFFS2_COMPR_NONE) { +- D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); +- ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize); ++ D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ++ je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); ++ ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); + if (ret) { + printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); + goto out_decomprbuf; + } + } + +- if (len < ri->dsize) { ++ if (len < je32_to_cpu(ri->dsize)) { + memcpy(buf, decomprbuf+ofs, len); + } + out_decomprbuf: +@@ -161,3 +148,96 @@ + + return ret; + } ++ ++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ unsigned char *buf, uint32_t offset, uint32_t len) ++{ ++ uint32_t end = offset + len; ++ struct jffs2_node_frag *frag; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n", ++ f->inocache->ino, offset, offset+len)); ++ ++ frag = jffs2_lookup_node_frag(&f->fragtree, offset); ++ ++ /* XXX FIXME: Where a single physical node actually shows up in two ++ frags, we read it twice. Don't do that. */ ++ /* Now we're pointing at the first frag which overlaps our page */ ++ while(offset < end) { ++ D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end)); ++ if (unlikely(!frag || frag->ofs > offset)) { ++ uint32_t holesize = end - offset; ++ if (frag) { ++ D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); ++ holesize = min(holesize, frag->ofs - offset); ++ D1(jffs2_print_frag_list(f)); ++ } ++ D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); ++ memset(buf, 0, holesize); ++ buf += holesize; ++ offset += holesize; ++ continue; ++ } else if (unlikely(!frag->node)) { ++ uint32_t holeend = min(end, frag->ofs + frag->size); ++ D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); ++ memset(buf, 0, holeend - offset); ++ buf += holeend - offset; ++ offset = holeend; ++ frag = frag_next(frag); ++ continue; ++ } else { ++ uint32_t readlen; ++ uint32_t fragofs; /* offset within the frag to start reading */ ++ ++ fragofs = offset - frag->ofs; ++ readlen = min(frag->size - fragofs, end - offset); ++ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", ++ frag->ofs+fragofs, frag->ofs+fragofs+readlen, ++ ref_offset(frag->node->raw), ref_flags(frag->node->raw))); ++ ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); ++ D2(printk(KERN_DEBUG "node read done\n")); ++ if (ret) { ++ D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); ++ memset(buf, 0, readlen); ++ return ret; ++ } ++ buf += readlen; ++ offset += readlen; ++ frag = frag_next(frag); ++ D2(printk(KERN_DEBUG "node read was OK. Looping\n")); ++ } ++ } ++ return 0; ++} ++ ++/* Core function to read symlink target. */ ++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) ++{ ++ char *buf; ++ int ret; ++ ++ down(&f->sem); ++ ++ if (!f->metadata) { ++ printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino); ++ up(&f->sem); ++ return ERR_PTR(-EINVAL); ++ } ++ buf = kmalloc(f->metadata->size+1, GFP_USER); ++ if (!buf) { ++ up(&f->sem); ++ return ERR_PTR(-ENOMEM); ++ } ++ buf[f->metadata->size]=0; ++ ++ ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size); ++ ++ up(&f->sem); ++ ++ if (ret) { ++ kfree(buf); ++ return ERR_PTR(ret); ++ } ++ return buf; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c +--- linux-mips-2.4.27/fs/jffs2/readinode.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/readinode.c 2004-11-19 10:25:12.123165744 +0100 +@@ -1,79 +1,122 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: readinode.c,v 1.58.2.8 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ + * + */ + +-/* Given an inode, probably with existing list of fragments, add the new node +- * to the fragment list. +- */ + #include + #include + #include ++#include ++#include + #include +-#include ++#include + #include "nodelist.h" +-#include + ++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); + +-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f) ++#if CONFIG_JFFS2_FS_DEBUG >= 1 ++static void jffs2_print_fragtree(struct rb_root *list, int permitbug) + { +- struct jffs2_node_frag *this = f->fraglist; ++ struct jffs2_node_frag *this = frag_first(list); ++ uint32_t lastofs = 0; ++ int buggy = 0; + + while(this) { + if (this->node) +- printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next); ++ printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n", ++ this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), ++ this, frag_left(this), frag_right(this), frag_parent(this)); + else +- printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next); +- this = this->next; ++ printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, ++ this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); ++ if (this->ofs != lastofs) ++ buggy = 1; ++ lastofs = this->ofs+this->size; ++ this = frag_next(this); ++ } ++ if (buggy && !permitbug) { ++ printk(KERN_CRIT "Frag tree got a hole in it\n"); ++ BUG(); + } ++} ++ ++void jffs2_print_frag_list(struct jffs2_inode_info *f) ++{ ++ jffs2_print_fragtree(&f->fragtree, 0); ++ + if (f->metadata) { +- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3); ++ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + } +-}) +- ++} + +-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) ++static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) + { +- int ret; +- D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); ++ struct jffs2_node_frag *frag; ++ int bitched = 0; + +- ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn); ++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + +- D2(jffs2_print_frag_list(f)); +- return ret; ++ struct jffs2_full_dnode *fn = frag->node; ++ if (!fn || !fn->raw) ++ continue; ++ ++ if (ref_flags(fn->raw) == REF_PRISTINE) { ++ ++ if (fn->frags > 1) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags); ++ bitched = 1; ++ } ++ /* A hole node which isn't multi-page should be garbage-collected ++ and merged anyway, so we just check for the frag size here, ++ rather than mucking around with actually reading the node ++ and checking the compression type, which is the real way ++ to tell a hole node. */ ++ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", ++ ref_offset(fn->raw)); ++ bitched = 1; ++ } ++ ++ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", ++ ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); ++ bitched = 1; ++ } ++ } ++ } ++ ++ if (bitched) { ++ struct jffs2_node_frag *thisfrag; ++ ++ printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino); ++ thisfrag = frag_first(&f->fragtree); ++ while (thisfrag) { ++ if (!thisfrag->node) { ++ printk("Frag @0x%x-0x%x; node-less hole\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs); ++ } else if (!thisfrag->node->raw) { ++ printk("Frag @0x%x-0x%x; raw-less hole\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs); ++ } else { ++ printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs, ++ ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw), ++ thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size); ++ } ++ thisfrag = frag_next(thisfrag); ++ } ++ } ++ return bitched; + } ++#endif /* D1 */ + + static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) + { +@@ -82,42 +125,38 @@ + if (!this->node->frags) { + /* The node has no valid frags left. It's totally obsoleted */ + D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", +- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size)); ++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size)); + jffs2_mark_node_obsolete(c, this->node->raw); + jffs2_free_full_dnode(this->node); + } else { +- D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n", +- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size, ++ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", ++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, + this->node->frags)); ++ mark_ref_normal(this->node->raw); + } + + } + jffs2_free_node_frag(this); + } + +-/* Doesn't set inode->i_size */ +-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn) ++/* Given an inode, probably with existing list of fragments, add the new node ++ * to the fragment list. ++ */ ++int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) + { ++ int ret; ++ struct jffs2_node_frag *newfrag; + +- struct jffs2_node_frag *this, **prev, *old; +- struct jffs2_node_frag *newfrag, *newfrag2; +- __u32 lastend = 0; +- ++ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); + + newfrag = jffs2_alloc_node_frag(); +- if (!newfrag) { ++ if (unlikely(!newfrag)) + return -ENOMEM; +- } +- +- D2(if (fn->raw) +- printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag); +- else +- printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag)); + +- prev = list; +- this = *list; ++ D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", ++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag)); + +- if (!fn->size) { ++ if (unlikely(!fn->size)) { + jffs2_free_node_frag(newfrag); + return 0; + } +@@ -126,176 +165,358 @@ + newfrag->size = fn->size; + newfrag->node = fn; + newfrag->node->frags = 1; +- newfrag->next = (void *)0xdeadbeef; ++ ++ ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); ++ if (ret) ++ return ret; ++ ++ /* If we now share a page with other nodes, mark either previous ++ or next node REF_NORMAL, as appropriate. */ ++ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { ++ struct jffs2_node_frag *prev = frag_prev(newfrag); ++ ++ mark_ref_normal(fn->raw); ++ /* If we don't start at zero there's _always_ a previous */ ++ if (prev->node) ++ mark_ref_normal(prev->node->raw); ++ } ++ ++ if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { ++ struct jffs2_node_frag *next = frag_next(newfrag); ++ ++ if (next) { ++ mark_ref_normal(fn->raw); ++ if (next->node) ++ mark_ref_normal(next->node->raw); ++ } ++ } ++ D2(if (jffs2_sanitycheck_fragtree(f)) { ++ printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n", ++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); ++ return 0; ++ }) ++ D2(jffs2_print_frag_list(f)); ++ return 0; ++} ++ ++/* Doesn't set inode->i_size */ ++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag) ++{ ++ struct jffs2_node_frag *this; ++ uint32_t lastend; + + /* Skip all the nodes which are completed before this one starts */ +- while(this && fn->ofs >= this->ofs+this->size) { +- lastend = this->ofs + this->size; ++ this = jffs2_lookup_node_frag(list, newfrag->node->ofs); + +- D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", +- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); +- prev = &this->next; +- this = this->next; ++ if (this) { ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", ++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); ++ lastend = this->ofs + this->size; ++ } else { ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n")); ++ lastend = 0; + } + + /* See if we ran off the end of the list */ +- if (!this) { ++ if (lastend <= newfrag->ofs) { + /* We did */ +- if (lastend < fn->ofs) { ++ ++ /* Check if 'this' node was on the same page as the new node. ++ If so, both 'this' and the new node get marked REF_NORMAL so ++ the GC can take a look. ++ */ ++ if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ mark_ref_normal(newfrag->node->raw); ++ } ++ ++ if (lastend < newfrag->node->ofs) { + /* ... and we need to put a hole in before the new node */ + struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag(); +- if (!holefrag) ++ if (!holefrag) { ++ jffs2_free_node_frag(newfrag); + return -ENOMEM; ++ } + holefrag->ofs = lastend; +- holefrag->size = fn->ofs - lastend; +- holefrag->next = NULL; ++ holefrag->size = newfrag->node->ofs - lastend; + holefrag->node = NULL; +- *prev = holefrag; +- prev = &holefrag->next; ++ if (this) { ++ /* By definition, the 'this' node has no right-hand child, ++ because there are no frags with offset greater than it. ++ So that's where we want to put the hole */ ++ D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this)); ++ rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); ++ } else { ++ D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag)); ++ rb_link_node(&holefrag->rb, NULL, &list->rb_node); ++ } ++ rb_insert_color(&holefrag->rb, list); ++ this = holefrag; + } +- newfrag->next = NULL; +- *prev = newfrag; ++ if (this) { ++ /* By definition, the 'this' node has no right-hand child, ++ because there are no frags with offset greater than it. ++ So that's where we want to put the hole */ ++ D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this)); ++ rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); ++ } else { ++ D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag)); ++ rb_link_node(&newfrag->rb, NULL, &list->rb_node); ++ } ++ rb_insert_color(&newfrag->rb, list); + return 0; + } + +- D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", +- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", ++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); + +- /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes, +- * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs ++ /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, ++ * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs + */ +- if (fn->ofs > this->ofs) { ++ if (newfrag->ofs > this->ofs) { + /* This node isn't completely obsoleted. The start of it remains valid */ +- if (this->ofs + this->size > fn->ofs + fn->size) { ++ ++ /* Mark the new node and the partially covered node REF_NORMAL -- let ++ the GC take a look at them */ ++ mark_ref_normal(newfrag->node->raw); ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ ++ if (this->ofs + this->size > newfrag->ofs + newfrag->size) { + /* The new node splits 'this' frag into two */ +- newfrag2 = jffs2_alloc_node_frag(); ++ struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); + if (!newfrag2) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; + } +- D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); ++ D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); + if (this->node) +- printk("phys 0x%08x\n", this->node->raw->flash_offset &~3); ++ printk("phys 0x%08x\n", ref_offset(this->node->raw)); + else + printk("hole\n"); + ) +- newfrag2->ofs = fn->ofs + fn->size; ++ ++ /* New second frag pointing to this's node */ ++ newfrag2->ofs = newfrag->ofs + newfrag->size; + newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; +- newfrag2->next = this->next; + newfrag2->node = this->node; + if (this->node) + this->node->frags++; +- newfrag->next = newfrag2; +- this->next = newfrag; ++ ++ /* Adjust size of original 'this' */ + this->size = newfrag->ofs - this->ofs; ++ ++ /* Now, we know there's no node with offset ++ greater than this->ofs but smaller than ++ newfrag2->ofs or newfrag->ofs, for obvious ++ reasons. So we can do a tree insert from ++ 'this' to insert newfrag, and a tree insert ++ from newfrag to insert newfrag2. */ ++ jffs2_fragtree_insert(newfrag, this); ++ rb_insert_color(&newfrag->rb, list); ++ ++ jffs2_fragtree_insert(newfrag2, newfrag); ++ rb_insert_color(&newfrag2->rb, list); ++ + return 0; + } + /* New node just reduces 'this' frag in size, doesn't split it */ +- this->size = fn->ofs - this->ofs; +- newfrag->next = this->next; +- this->next = newfrag; +- this = newfrag->next; ++ this->size = newfrag->ofs - this->ofs; ++ ++ /* Again, we know it lives down here in the tree */ ++ jffs2_fragtree_insert(newfrag, this); ++ rb_insert_color(&newfrag->rb, list); + } else { +- D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this)); +- *prev = newfrag; +- newfrag->next = this; ++ /* New frag starts at the same point as 'this' used to. Replace ++ it in the tree without doing a delete and insertion */ ++ D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", ++ newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, ++ this, this->ofs, this->ofs+this->size)); ++ ++ rb_replace_node(&this->rb, &newfrag->rb, list); ++ ++ if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { ++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size)); ++ jffs2_obsolete_node_frag(c, this); ++ } else { ++ this->ofs += newfrag->size; ++ this->size -= newfrag->size; ++ ++ jffs2_fragtree_insert(this, newfrag); ++ rb_insert_color(&this->rb, list); ++ return 0; + } +- /* OK, now we have newfrag added in the correct place in the list, but +- newfrag->next points to a fragment which may be overlapping it ++ } ++ /* OK, now we have newfrag added in the correct place in the tree, but ++ frag_next(newfrag) may be a fragment which is overlapped by it + */ +- while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) { +- /* 'this' frag is obsoleted. */ +- old = this; +- this = old->next; +- jffs2_obsolete_node_frag(c, old); ++ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { ++ /* 'this' frag is obsoleted completely. */ ++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size)); ++ rb_erase(&this->rb, list); ++ jffs2_obsolete_node_frag(c, this); + } + /* Now we're pointing at the first frag which isn't totally obsoleted by + the new frag */ +- newfrag->next = this; + + if (!this || newfrag->ofs + newfrag->size == this->ofs) { + return 0; + } +- /* Still some overlap */ ++ /* Still some overlap but we don't need to move it in the tree */ + this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); + this->ofs = newfrag->ofs + newfrag->size; ++ ++ /* And mark them REF_NORMAL so the GC takes a look at them */ ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ mark_ref_normal(newfrag->node->raw); ++ + return 0; + } + +-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size) ++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) + { ++ struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); ++ + D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size)); + +- while (*list) { +- if ((*list)->ofs >= size) { +- struct jffs2_node_frag *this = *list; +- *list = this->next; +- D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size)); +- jffs2_obsolete_node_frag(c, this); +- continue; +- } else if ((*list)->ofs + (*list)->size > size) { +- D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size)); +- (*list)->size = size - (*list)->ofs; +- } +- list = &(*list)->next; ++ /* We know frag->ofs <= size. That's what lookup does for us */ ++ if (frag && frag->ofs != size) { ++ if (frag->ofs+frag->size >= size) { ++ D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); ++ frag->size = size - frag->ofs; ++ } ++ frag = frag_next(frag); ++ } ++ while (frag && frag->ofs >= size) { ++ struct jffs2_node_frag *next = frag_next(frag); ++ ++ D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); ++ frag_erase(frag, list); ++ jffs2_obsolete_node_frag(c, frag); ++ frag = next; + } + } + + /* Scan the list of all nodes present for this ino, build map of versions, etc. */ + +-void jffs2_read_inode (struct inode *inode) ++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *latest_node); ++ ++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ uint32_t ino, struct jffs2_raw_inode *latest_node) + { +- struct jffs2_tmp_dnode_info *tn_list, *tn; +- struct jffs2_full_dirent *fd_list; +- struct jffs2_inode_info *f; +- struct jffs2_full_dnode *fn = NULL; +- struct jffs2_sb_info *c; +- struct jffs2_raw_inode latest_node; +- __u32 latest_mctime, mctime_ver; +- __u32 mdata_ver = 0; +- int ret; +- ssize_t retlen; ++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + +- D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); ++ retry_inocache: ++ spin_lock(&c->inocache_lock); ++ f->inocache = jffs2_get_ino_cache(c, ino); ++ ++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache)); ++ ++ if (f->inocache) { ++ /* Check its state. We may need to wait before we can use it */ ++ switch(f->inocache->state) { ++ case INO_STATE_UNCHECKED: ++ case INO_STATE_CHECKEDABSENT: ++ f->inocache->state = INO_STATE_READING; ++ break; + +- f = JFFS2_INODE_INFO(inode); +- c = JFFS2_SB_INFO(inode->i_sb); ++ case INO_STATE_CHECKING: ++ case INO_STATE_GC: ++ /* If it's in either of these states, we need ++ to wait for whoever's got it to finish and ++ put it back. */ ++ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n", ++ ino, f->inocache->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ goto retry_inocache; ++ ++ case INO_STATE_READING: ++ case INO_STATE_PRESENT: ++ /* Eep. This should never happen. It can ++ happen if Linux calls read_inode() again ++ before clear_inode() has finished though. */ ++ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); ++ /* Fail. That's probably better than allowing it to succeed */ ++ f->inocache = NULL; ++ break; + +- memset(f, 0, sizeof(*f)); +- D2(printk(KERN_DEBUG "getting inocache\n")); +- init_MUTEX(&f->sem); +- f->inocache = jffs2_get_ino_cache(c, inode->i_ino); +- D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache)); ++ default: ++ BUG(); ++ } ++ } ++ spin_unlock(&c->inocache_lock); + +- if (!f->inocache && inode->i_ino == 1) { ++ if (!f->inocache && ino == 1) { + /* Special case - no root inode on medium */ + f->inocache = jffs2_alloc_inode_cache(); + if (!f->inocache) { +- printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n"); +- make_bad_inode(inode); +- return; ++ printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n"); ++ return -ENOMEM; + } +- D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n")); ++ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n")); + memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); + f->inocache->ino = f->inocache->nlink = 1; + f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; ++ f->inocache->state = INO_STATE_READING; + jffs2_add_ino_cache(c, f->inocache); + } + if (!f->inocache) { +- printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino); +- make_bad_inode(inode); +- return; ++ printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino); ++ return -ENOENT; + } +- D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink)); +- inode->i_nlink = f->inocache->nlink; ++ ++ return jffs2_do_read_inode_internal(c, f, latest_node); ++} ++ ++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++{ ++ struct jffs2_raw_inode n; ++ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); ++ int ret; ++ ++ if (!f) ++ return -ENOMEM; ++ ++ memset(f, 0, sizeof(*f)); ++ init_MUTEX_LOCKED(&f->sem); ++ f->inocache = ic; ++ ++ ret = jffs2_do_read_inode_internal(c, f, &n); ++ if (!ret) { ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ } ++ kfree (f); ++ return ret; ++} ++ ++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *latest_node) ++{ ++ struct jffs2_tmp_dnode_info *tn_list, *tn; ++ struct jffs2_full_dirent *fd_list; ++ struct jffs2_full_dnode *fn = NULL; ++ uint32_t crc; ++ uint32_t latest_mctime, mctime_ver; ++ uint32_t mdata_ver = 0; ++ size_t retlen; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); + + /* Grab all nodes relevant to this ino */ +- ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); ++ ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + + if (ret) { +- printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret); +- make_bad_inode(inode); +- return; ++ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); ++ return ret; + } + f->dents = fd_list; + +@@ -304,205 +525,169 @@ + + fn = tn->fn; + +- if (f->metadata && tn->version > mdata_ver) { +- D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3)); ++ if (f->metadata) { ++ if (likely(tn->version >= mdata_ver)) { ++ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw))); + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + f->metadata = NULL; + + mdata_ver = 0; ++ } else { ++ /* This should never happen. */ ++ printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", ++ ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); ++ jffs2_mark_node_obsolete(c, fn->raw); ++ jffs2_free_full_dnode(fn); ++ /* Fill in latest_node from the metadata, not this one we're about to free... */ ++ fn = f->metadata; ++ goto next_tn; ++ } + } + + if (fn->size) { + jffs2_add_full_dnode_to_inode(c, f, fn); + } else { + /* Zero-sized node at end of version list. Just a metadata update */ +- D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version)); ++ D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version)); + f->metadata = fn; + mdata_ver = tn->version; + } ++ next_tn: + tn_list = tn->next; + jffs2_free_tmp_dnode_info(tn); + } ++ D1(jffs2_sanitycheck_fragtree(f)); ++ + if (!fn) { + /* No data nodes for this inode. */ +- if (inode->i_ino != 1) { +- printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino); ++ if (f->inocache->ino != 1) { ++ printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino); + if (!fd_list) { +- make_bad_inode(inode); +- return; +- } +- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n"); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); ++ return -EIO; ++ } ++ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n"); ++ } ++ latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); ++ latest_node->version = cpu_to_je32(0); ++ latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); ++ latest_node->isize = cpu_to_je32(0); ++ latest_node->gid = cpu_to_je16(0); ++ latest_node->uid = cpu_to_je16(0); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); ++ return 0; + } +- inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; +- latest_node.version = 0; +- inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; +- inode->i_nlink = f->inocache->nlink; +- inode->i_size = 0; +- } else { +- __u32 crc; + +- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node); +- if (ret || retlen != sizeof(latest_node)) { +- printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n", +- ret, (long)retlen, sizeof(latest_node)); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- +- crc = crc32(0, &latest_node, sizeof(latest_node)-8); +- if (crc != latest_node.node_crc) { +- printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- +- inode->i_mode = latest_node.mode; +- inode->i_uid = latest_node.uid; +- inode->i_gid = latest_node.gid; +- inode->i_size = latest_node.isize; +- if (S_ISREG(inode->i_mode)) +- jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize); +- inode->i_atime = latest_node.atime; +- inode->i_mtime = latest_node.mtime; +- inode->i_ctime = latest_node.ctime; +- } +- +- /* OK, now the special cases. Certain inode types should +- have only one data node, and it's kept as the metadata +- node */ +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) || +- S_ISLNK(inode->i_mode)) { +- if (f->metadata) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- if (!f->fraglist) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- /* ASSERT: f->fraglist != NULL */ +- if (f->fraglist->next) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode); +- /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- /* OK. We're happy */ +- f->metadata = f->fraglist->node; +- jffs2_free_node_frag(f->fraglist); +- f->fraglist = NULL; ++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); ++ if (ret || retlen != sizeof(*latest_node)) { ++ printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n", ++ ret, retlen, sizeof(*latest_node)); ++ /* FIXME: If this fails, there seems to be a memory leak. Find it. */ ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return ret?ret:-EIO; + } + +- inode->i_blksize = PAGE_SIZE; +- inode->i_blocks = (inode->i_size + 511) >> 9; +- +- switch (inode->i_mode & S_IFMT) { +- unsigned short rdev; +- +- case S_IFLNK: +- inode->i_op = &jffs2_symlink_inode_operations; +- /* Hack to work around broken isize in old symlink code. +- Remove this when dwmw2 comes to his senses and stops +- symlinks from being an entirely gratuitous special +- case. */ +- if (!inode->i_size) +- inode->i_size = latest_node.dsize; +- break; ++ crc = crc32(0, latest_node, sizeof(*latest_node)-8); ++ if (crc != je32_to_cpu(latest_node->node_crc)) { ++ printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } + ++ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { + case S_IFDIR: +- if (mctime_ver > latest_node.version) { ++ if (mctime_ver > je32_to_cpu(latest_node->version)) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ +- inode->i_mtime = inode->i_ctime = inode->i_atime = +- latest_mctime; ++ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); + } +- inode->i_op = &jffs2_dir_inode_operations; +- inode->i_fop = &jffs2_dir_operations; + break; + ++ + case S_IFREG: +- inode->i_op = &jffs2_file_inode_operations; +- inode->i_fop = &jffs2_file_operations; +- inode->i_mapping->a_ops = &jffs2_file_address_operations; +- inode->i_mapping->nrpages = 0; ++ /* If it was a regular file, truncate it to the latest node's isize */ ++ jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize)); + break; + ++ case S_IFLNK: ++ /* Hack to work around broken isize in old symlink code. ++ Remove this when dwmw2 comes to his senses and stops ++ symlinks from being an entirely gratuitous special ++ case. */ ++ if (!je32_to_cpu(latest_node->isize)) ++ latest_node->isize = latest_node->dsize; ++ /* fall through... */ ++ + case S_IFBLK: + case S_IFCHR: +- /* Read the device numbers from the media */ +- D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); +- if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { +- /* Eep */ +- printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; ++ /* Certain inode types should have only one data node, and it's ++ kept as the metadata node */ ++ if (f->metadata) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; + } +- +- case S_IFSOCK: +- case S_IFIFO: +- inode->i_op = &jffs2_file_inode_operations; +- init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff))); ++ if (!frag_first(&f->fragtree)) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } ++ /* ASSERT: f->fraglist != NULL */ ++ if (frag_next(frag_first(&f->fragtree))) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } ++ /* OK. We're happy */ ++ f->metadata = frag_first(&f->fragtree)->node; ++ jffs2_free_node_frag(frag_first(&f->fragtree)); ++ f->fragtree = RB_ROOT; + break; +- +- default: +- printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino); + } +- D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); ++ ++ return 0; + } + +-void jffs2_clear_inode (struct inode *inode) ++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) + { +- /* We can forget about this inode for now - drop all +- * the nodelists associated with it, etc. +- */ +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_node_frag *frag, *frags; + struct jffs2_full_dirent *fd, *fds; +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + int deleted; + +- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); +- + down(&f->sem); + deleted = f->inocache && !f->inocache->nlink; + +- frags = f->fraglist; +- fds = f->dents; + if (f->metadata) { + if (deleted) + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + } + +- while (frags) { +- frag = frags; +- frags = frag->next; +- D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0)); ++ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); + +- if (frag->node && !(--frag->node->frags)) { +- /* Not a hole, and it's the final remaining frag of this node. Free the node */ +- if (deleted) +- jffs2_mark_node_obsolete(c, frag->node->raw); ++ fds = f->dents; + +- jffs2_free_full_dnode(frag->node); +- } +- jffs2_free_node_frag(frag); +- } + while(fds) { + fd = fds; + fds = fd->next; + jffs2_free_full_dirent(fd); + } + +- up(&f->sem); +-}; ++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + ++ up(&f->sem); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/scan.c linux/fs/jffs2/scan.c +--- linux-mips-2.4.27/fs/jffs2/scan.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/scan.c 2004-11-19 10:25:12.124165592 +0100 +@@ -1,47 +1,25 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: scan.c,v 1.51.2.4 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: scan.c,v 1.106 2003/10/28 17:01:13 dwmw2 Exp $ + * + */ + #include ++#include + #include +-#include + #include + #include +-#include "nodelist.h" + #include ++#include ++#include "nodelist.h" + ++#define EMPTY_SCAN_SIZE 1024 + + #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; c->dirty_size += _x; \ +@@ -51,6 +29,10 @@ + c->free_size -= _x; c->used_size += _x; \ + jeb->free_size -= _x ; jeb->used_size += _x; \ + }while(0) ++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->unchecked_size += _x; \ ++ jeb->free_size -= _x ; jeb->unchecked_size += _x; \ ++ }while(0) + + #define noisy_printk(noise, args...) do { \ + if (*(noise)) { \ +@@ -63,39 +45,84 @@ + } while(0) + + static uint32_t pseudo_random; +-static void jffs2_rotate_lists(struct jffs2_sb_info *c); + +-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ unsigned char *buf, uint32_t buf_size); + + /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. + * Returning an error will abort the mount - bad checksums etc. should just mark the space + * as dirty. + */ +-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise); +-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs); +-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs); ++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_inode *ri, uint32_t ofs); ++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_dirent *rd, uint32_t ofs); ++ ++#define BLK_STATE_ALLFF 0 ++#define BLK_STATE_CLEAN 1 ++#define BLK_STATE_PARTDIRTY 2 ++#define BLK_STATE_CLEANMARKER 3 ++#define BLK_STATE_ALLDIRTY 4 ++#define BLK_STATE_BADBLOCK 5 + ++static inline int min_free(struct jffs2_sb_info *c) ++{ ++ uint32_t min = 2 * sizeof(struct jffs2_raw_inode); ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) ++ return c->wbuf_pagesize; ++#endif ++ return min; + ++} + int jffs2_scan_medium(struct jffs2_sb_info *c) + { + int i, ret; +- __u32 empty_blocks = 0; +- +- if (!c->blocks) { +- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n"); +- return -EINVAL; ++ uint32_t empty_blocks = 0, bad_blocks = 0; ++ unsigned char *flashbuf = NULL; ++ uint32_t buf_size = 0; ++#ifndef __ECOS ++ size_t pointlen; ++ ++ if (c->mtd->point) { ++ ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); ++ if (!ret && pointlen < c->mtd->size) { ++ /* Don't muck about if it won't let us point to the whole flash */ ++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); ++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); ++ flashbuf = NULL; ++ } ++ if (ret) ++ D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); ++ } ++#endif ++ if (!flashbuf) { ++ /* For NAND it's quicker to read a whole eraseblock at a time, ++ apparently */ ++ if (jffs2_cleanmarker_oob(c)) ++ buf_size = c->sector_size; ++ else ++ buf_size = PAGE_SIZE; ++ ++ D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); ++ flashbuf = kmalloc(buf_size, GFP_KERNEL); ++ if (!flashbuf) ++ return -ENOMEM; + } ++ + for (i=0; inr_blocks; i++) { + struct jffs2_eraseblock *jeb = &c->blocks[i]; + +- ret = jffs2_scan_eraseblock(c, jeb); ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); ++ + if (ret < 0) +- return ret; ++ goto out; + + ACCT_PARANOIA_CHECK(jeb); + + /* Now decide which list to put it on */ +- if (ret == 1) { ++ switch(ret) { ++ case BLK_STATE_ALLFF: + /* + * Empty block. Since we can't be sure it + * was entirely erased, we just queue it for erase +@@ -103,10 +130,12 @@ + * is complete. Meanwhile we still count it as empty + * for later checks. + */ +- list_add(&jeb->list, &c->erase_pending_list); + empty_blocks++; ++ list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; +- } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) { ++ break; ++ ++ case BLK_STATE_CLEANMARKER: + /* Only a CLEANMARKER node is valid */ + if (!jeb->dirty_size) { + /* It's actually free */ +@@ -118,74 +147,227 @@ + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + } +- } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) { ++ break; ++ ++ case BLK_STATE_CLEAN: + /* Full (or almost full) of clean data. Clean list */ + list_add(&jeb->list, &c->clean_list); +- } else if (jeb->used_size) { ++ break; ++ ++ case BLK_STATE_PARTDIRTY: + /* Some data, but not full. Dirty list. */ + /* Except that we want to remember the block with most free space, + and stick it in the 'nextblock' position to start writing to it. + Later when we do snapshots, this must be the most recent block, + not the one with most free space. + */ +- if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && ++ if (jeb->free_size > min_free(c) && + (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { + /* Better candidate for the next writes to go to */ +- if (c->nextblock) ++ if (c->nextblock) { ++ c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; ++ c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; ++ c->free_size -= c->nextblock->free_size; ++ c->wasted_size -= c->nextblock->wasted_size; ++ c->nextblock->free_size = c->nextblock->wasted_size = 0; ++ if (VERYDIRTY(c, c->nextblock->dirty_size)) { ++ list_add(&c->nextblock->list, &c->very_dirty_list); ++ } else { + list_add(&c->nextblock->list, &c->dirty_list); ++ } ++ } + c->nextblock = jeb; + } else { ++ jeb->dirty_size += jeb->free_size + jeb->wasted_size; ++ c->dirty_size += jeb->free_size + jeb->wasted_size; ++ c->free_size -= jeb->free_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->free_size = jeb->wasted_size = 0; ++ if (VERYDIRTY(c, jeb->dirty_size)) { ++ list_add(&jeb->list, &c->very_dirty_list); ++ } else { + list_add(&jeb->list, &c->dirty_list); + } +- } else { ++ } ++ break; ++ ++ case BLK_STATE_ALLDIRTY: + /* Nothing valid - not even a clean marker. Needs erasing. */ + /* For now we just put it on the erasing list. We'll start the erases later */ +- printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset); ++ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset)); + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; ++ break; ++ ++ case BLK_STATE_BADBLOCK: ++ D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset)); ++ list_add(&jeb->list, &c->bad_list); ++ c->bad_size += c->sector_size; ++ c->free_size -= c->sector_size; ++ bad_blocks++; ++ break; ++ default: ++ printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); ++ BUG(); + } + } +- /* Rotate the lists by some number to ensure wear levelling */ +- jffs2_rotate_lists(c); + ++ /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ ++ if (c->nextblock && (c->nextblock->dirty_size)) { ++ c->nextblock->wasted_size += c->nextblock->dirty_size; ++ c->wasted_size += c->nextblock->dirty_size; ++ c->dirty_size -= c->nextblock->dirty_size; ++ c->nextblock->dirty_size = 0; ++ } ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { ++ /* If we're going to start writing into a block which already ++ contains data, and the end of the data isn't page-aligned, ++ skip a little and align it. */ ++ ++ uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1); ++ ++ D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", ++ skip)); ++ c->nextblock->wasted_size += skip; ++ c->wasted_size += skip; ++ ++ c->nextblock->free_size -= skip; ++ c->free_size -= skip; ++ } ++#endif + if (c->nr_erasing_blocks) { +- if (!c->used_size && empty_blocks != c->nr_blocks) { ++ if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { + printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); +- return -EIO; ++ printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ++ ret = -EIO; ++ goto out; + } + jffs2_erase_pending_trigger(c); + } ++ ret = 0; ++ out: ++ if (buf_size) ++ kfree(flashbuf); ++#ifndef __ECOS ++ else ++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); ++#endif ++ return ret; ++} ++ ++static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, ++ uint32_t ofs, uint32_t len) ++{ ++ int ret; ++ size_t retlen; ++ ++ ret = jffs2_flash_read(c, ofs, len, &retlen, buf); ++ if (ret) { ++ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret)); ++ return ret; ++ } ++ if (retlen < len) { ++ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen)); ++ return -EIO; ++ } ++ D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs)); ++ D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15])); + return 0; + } + +-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { +- struct jffs2_unknown_node node; +- __u32 ofs, prevofs; +- __u32 hdr_crc, nodetype; ++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ unsigned char *buf, uint32_t buf_size) { ++ struct jffs2_unknown_node *node; ++ struct jffs2_unknown_node crcnode; ++ uint32_t ofs, prevofs; ++ uint32_t hdr_crc, buf_ofs, buf_len; + int err; + int noise = 0; ++ int wasempty = 0; ++ uint32_t empty_start = 0; ++#ifdef CONFIG_JFFS2_FS_NAND ++ int cleanmarkerfound = 0; ++#endif + + ofs = jeb->offset; + prevofs = jeb->offset - 1; + + D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); + +- err = jffs2_scan_empty(c, jeb, &ofs, &noise); +- if (err) return err; +- if (ofs == jeb->offset + c->sector_size) { ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (jffs2_cleanmarker_oob(c)) { ++ int ret = jffs2_check_nand_cleanmarker(c, jeb); ++ D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); ++ /* Even if it's not found, we still scan to see ++ if the block is empty. We use this information ++ to decide whether to erase it or not. */ ++ switch (ret) { ++ case 0: cleanmarkerfound = 1; break; ++ case 1: break; ++ case 2: return BLK_STATE_BADBLOCK; ++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ ++ default: return ret; ++ } ++ } ++#endif ++ buf_ofs = jeb->offset; ++ ++ if (!buf_size) { ++ buf_len = c->sector_size; ++ } else { ++ buf_len = EMPTY_SCAN_SIZE; ++ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); ++ if (err) ++ return err; ++ } ++ ++ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ++ ofs = 0; ++ ++ /* Scan only 4KiB of 0xFF before declaring it's empty */ ++ while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ++ ofs += 4; ++ ++ if (ofs == EMPTY_SCAN_SIZE) { ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (jffs2_cleanmarker_oob(c)) { ++ /* scan oob, take care of cleanmarker */ ++ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); ++ D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); ++ switch (ret) { ++ case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; ++ case 1: return BLK_STATE_ALLDIRTY; ++ case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */ ++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ ++ default: return ret; ++ } ++ } ++#endif + D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); +- return 1; /* special return code */ ++ return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ ++ } ++ if (ofs) { ++ D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, ++ jeb->offset + ofs)); ++ DIRTY_SPACE(ofs); + } + ++ /* Now ofs is a complete physical flash offset as it always was... */ ++ ofs += jeb->offset; ++ + noise = 10; + + while(ofs < jeb->offset + c->sector_size) { +- ssize_t retlen; +- ACCT_PARANOIA_CHECK(jeb); ++ ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ cond_resched(); + + if (ofs & 3) { + printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); +- ofs = (ofs+3)&~3; ++ ofs = PAD(ofs); + continue; + } + if (ofs == prevofs) { +@@ -196,102 +378,173 @@ + } + prevofs = ofs; + +- if (jeb->offset + c->sector_size < ofs + sizeof(node)) { +- D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node))); ++ if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), ++ jeb->offset, c->sector_size, ofs, sizeof(*node))); + DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); + break; + } + +- err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node); +- +- if (err) { +- D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err)); ++ if (buf_ofs + buf_len < ofs + sizeof(*node)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", ++ sizeof(struct jffs2_unknown_node), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) + return err; ++ buf_ofs = ofs; + } +- if (retlen < sizeof(node)) { +- D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen)); +- DIRTY_SPACE(retlen); +- ofs += retlen; +- continue; ++ ++ node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; ++ ++ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { ++ uint32_t inbuf_ofs = ofs - buf_ofs + 4; ++ uint32_t scanend; ++ ++ empty_start = ofs; ++ ofs += 4; ++ ++ /* If scanning empty space after only a cleanmarker, don't ++ bother scanning the whole block */ ++ if (unlikely(empty_start == jeb->offset + c->cleanmarker_size && ++ jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len)) ++ scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs; ++ else ++ scanend = buf_len; ++ ++ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); ++ while (inbuf_ofs < scanend) { ++ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) ++ goto emptyends; ++ ++ inbuf_ofs+=4; ++ ofs += 4; + } ++ /* Ran off end. */ ++ D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); + +- if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) { +- D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs)); +- err = jffs2_scan_empty(c, jeb, &ofs, &noise); +- if (err) return err; ++ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && ++ c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size) ++ return BLK_STATE_CLEANMARKER; ++ wasempty = 1; ++ continue; ++ } else if (wasempty) { ++ emptyends: ++ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); ++ DIRTY_SPACE(ofs-empty_start); ++ wasempty = 0; + continue; + } + +- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) { ++ if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { + printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic == JFFS2_DIRTY_BITMASK) { +- D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs)); ++ if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { ++ D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic == JFFS2_OLD_MAGIC_BITMASK) { ++ if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { + printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); + printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic != JFFS2_MAGIC_BITMASK) { ++ if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { + /* OK. We're out of possibilities. Whinge and move on */ +- noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic); ++ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", ++ JFFS2_MAGIC_BITMASK, ofs, ++ je16_to_cpu(node->magic)); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + /* We seem to have a node of sorts. Check the CRC */ +- nodetype = node.nodetype; +- node.nodetype |= JFFS2_NODE_ACCURATE; +- hdr_crc = crc32(0, &node, sizeof(node)-4); +- node.nodetype = nodetype; +- if (hdr_crc != node.hdr_crc) { ++ crcnode.magic = node->magic; ++ crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); ++ crcnode.totlen = node->totlen; ++ hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); ++ ++ if (hdr_crc != je32_to_cpu(node->hdr_crc)) { + noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", +- ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc); ++ ofs, je16_to_cpu(node->magic), ++ je16_to_cpu(node->nodetype), ++ je32_to_cpu(node->totlen), ++ je32_to_cpu(node->hdr_crc), ++ hdr_crc); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + +- if (ofs + node.totlen > jeb->offset + c->sector_size) { ++ if (ofs + je32_to_cpu(node->totlen) > ++ jeb->offset + c->sector_size) { + /* Eep. Node goes over the end of the erase block. */ + printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", +- ofs, node.totlen); ++ ofs, je32_to_cpu(node->totlen)); + printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + +- switch(node.nodetype | JFFS2_NODE_ACCURATE) { ++ if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { ++ /* Wheee. This is an obsoleted node */ ++ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); ++ continue; ++ } ++ ++ switch(je16_to_cpu(node->nodetype)) { + case JFFS2_NODETYPE_INODE: +- err = jffs2_scan_inode_node(c, jeb, &ofs); ++ if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", ++ sizeof(struct jffs2_raw_inode), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) ++ return err; ++ buf_ofs = ofs; ++ node = (void *)buf; ++ } ++ err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs); + if (err) return err; ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: +- err = jffs2_scan_dirent_node(c, jeb, &ofs); ++ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", ++ je32_to_cpu(node->totlen), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) ++ return err; ++ buf_ofs = ofs; ++ node = (void *)buf; ++ } ++ err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs); + if (err) return err; ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: +- if (node.totlen != sizeof(struct jffs2_unknown_node)) { ++ D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); ++ if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { + printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", +- ofs, node.totlen, sizeof(struct jffs2_unknown_node)); ++ ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); + DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ++ ofs += PAD(sizeof(struct jffs2_unknown_node)); + } else if (jeb->first_node) { + printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); + DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + ofs += PAD(sizeof(struct jffs2_unknown_node)); +- continue; + } else { + struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { +@@ -300,98 +553,80 @@ + } + marker_ref->next_in_ino = NULL; + marker_ref->next_phys = NULL; +- marker_ref->flash_offset = ofs; +- marker_ref->totlen = sizeof(struct jffs2_unknown_node); ++ marker_ref->flash_offset = ofs | REF_NORMAL; ++ marker_ref->__totlen = c->cleanmarker_size; + jeb->first_node = jeb->last_node = marker_ref; + +- USED_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ++ USED_SPACE(PAD(c->cleanmarker_size)); ++ ofs += PAD(c->cleanmarker_size); + } +- ofs += PAD(sizeof(struct jffs2_unknown_node)); ++ break; ++ ++ case JFFS2_NODETYPE_PADDING: ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + default: +- switch (node.nodetype & JFFS2_COMPAT_MASK) { ++ switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { + case JFFS2_FEATURE_ROCOMPAT: +- printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); ++ printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); + c->flags |= JFFS2_SB_FLAG_RO; +- if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)) ++ if (!(jffs2_is_readonly(c))) + return -EROFS; +- DIRTY_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); +- continue; ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); ++ break; + + case JFFS2_FEATURE_INCOMPAT: +- printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); ++ printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); + return -EINVAL; + + case JFFS2_FEATURE_RWCOMPAT_DELETE: +- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); +- DIRTY_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); ++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_FEATURE_RWCOMPAT_COPY: +- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); +- USED_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); ++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); ++ USED_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + } + } + } +- D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, +- jeb->free_size, jeb->dirty_size, jeb->used_size)); +- return 0; +-} + +-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */ +-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise) +-{ +- __u32 *buf; +- __u32 scanlen = (jeb->offset + c->sector_size) - *startofs; +- __u32 curofs = *startofs; +- +- buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL); +- if (!buf) { +- printk(KERN_WARNING "Scan buffer allocation failed\n"); +- return -ENOMEM; +- } +- while(scanlen) { +- ssize_t retlen; +- int ret, i; +- +- ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf); +- if(ret) { +- D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret)); +- kfree(buf); +- return ret; +- } +- if (retlen < 4) { +- D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n")); +- kfree(buf); +- return -EIO; +- } +- for (i=0; i<(retlen / 4); i++) { +- if (buf[i] != 0xffffffff) { +- curofs += i*4; +- +- noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]); +- DIRTY_SPACE(curofs - (*startofs)); +- *startofs = curofs; +- kfree(buf); +- return 0; +- } +- } +- scanlen -= retlen&~3; +- curofs += retlen&~3; +- } + +- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs)); +- kfree(buf); +- *startofs = curofs; +- return 0; ++ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, ++ jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); ++ ++ /* mark_node_obsolete can add to wasted !! */ ++ if (jeb->wasted_size) { ++ jeb->dirty_size += jeb->wasted_size; ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->wasted_size = 0; ++ } ++ ++ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size ++ && (!jeb->first_node || jeb->first_node->next_in_ino) ) ++ return BLK_STATE_CLEANMARKER; ++ ++ /* move blocks with max 4 byte dirty space to cleanlist */ ++ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { ++ c->dirty_size -= jeb->dirty_size; ++ c->wasted_size += jeb->dirty_size; ++ jeb->wasted_size += jeb->dirty_size; ++ jeb->dirty_size = 0; ++ return BLK_STATE_CLEAN; ++ } else if (jeb->used_size || jeb->unchecked_size) ++ return BLK_STATE_PARTDIRTY; ++ else ++ return BLK_STATE_ALLDIRTY; + } + +-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino) ++static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) + { + struct jffs2_inode_cache *ic; + +@@ -399,137 +634,77 @@ + if (ic) + return ic; + ++ if (ino > c->highest_ino) ++ c->highest_ino = ino; ++ + ic = jffs2_alloc_inode_cache(); + if (!ic) { + printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); + return NULL; + } + memset(ic, 0, sizeof(*ic)); +- ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL); +- if (!ic->scan) { +- printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n"); +- jffs2_free_inode_cache(ic); +- return NULL; +- } +- memset(ic->scan, 0, sizeof(*ic->scan)); ++ + ic->ino = ino; + ic->nodes = (void *)ic; + jffs2_add_ino_cache(c, ic); + if (ino == 1) +- ic->nlink=1; ++ ic->nlink = 1; + return ic; + } + +-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs) ++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_inode *ri, uint32_t ofs) + { + struct jffs2_raw_node_ref *raw; +- struct jffs2_full_dnode *fn; +- struct jffs2_tmp_dnode_info *tn, **tn_list; + struct jffs2_inode_cache *ic; +- struct jffs2_raw_inode ri; +- __u32 crc; +- __u16 oldnodetype; +- int ret; +- ssize_t retlen; +- +- D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs)); +- +- ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret); +- return ret; +- } +- if (retlen != sizeof(ri)) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs, sizeof(ri)); +- return -EIO; +- } ++ uint32_t ino = je32_to_cpu(ri->ino); + +- /* We sort of assume that the node was accurate when it was +- first written to the medium :) */ +- oldnodetype = ri.nodetype; +- ri.nodetype |= JFFS2_NODE_ACCURATE; +- crc = crc32(0, &ri, sizeof(ri)-8); +- ri.nodetype = oldnodetype; ++ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); + +- if(crc != ri.node_crc) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, ri.node_crc, crc); +- /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(4); +- *ofs += 4; +- return 0; +- } +- /* There was a bug where we wrote hole nodes out with csize/dsize +- swapped. Deal with it */ +- if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) { +- ri.dsize = ri.csize; +- ri.csize = 0; +- } ++ /* We do very little here now. Just check the ino# to which we should attribute ++ this node; we can do all the CRC checking etc. later. There's a tradeoff here -- ++ we used to scan the flash once only, reading everything we want from it into ++ memory, then building all our in-core data structures and freeing the extra ++ information. Now we allow the first part of the mount to complete a lot quicker, ++ but we have to go _back_ to the flash in order to finish the CRC checking, etc. ++ Which means that the _full_ amount of time to get to proper write mode with GC ++ operational may actually be _longer_ than before. Sucks to be me. */ + +- if (ri.csize) { +- /* Check data CRC too */ +- unsigned char *dbuf; +- __u32 crc; +- +- dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); +- if (!dbuf) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n"); +- return -ENOMEM; +- } +- ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret); +- kfree(dbuf); +- return ret; +- } +- if (retlen != ri.csize) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs+ sizeof(ri), ri.csize); +- kfree(dbuf); +- return -EIO; +- } +- crc = crc32(0, dbuf, ri.csize); +- kfree(dbuf); +- if (crc != ri.data_crc) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, ri.data_crc, crc); +- DIRTY_SPACE(PAD(ri.totlen)); +- *ofs += PAD(ri.totlen); +- return 0; +- } +- } +- +- /* Wheee. It worked */ + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); + return -ENOMEM; + } +- tn = jffs2_alloc_tmp_dnode_info(); +- if (!tn) { +- jffs2_free_raw_node_ref(raw); +- return -ENOMEM; +- } +- fn = jffs2_alloc_full_dnode(); +- if (!fn) { +- jffs2_free_tmp_dnode_info(tn); ++ ++ ic = jffs2_get_ino_cache(c, ino); ++ if (!ic) { ++ /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the ++ first node we found for this inode. Do a CRC check to protect against the former ++ case */ ++ uint32_t crc = crc32(0, ri, sizeof(*ri)-8); ++ ++ if (crc != je32_to_cpu(ri->node_crc)) { ++ printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ofs, je32_to_cpu(ri->node_crc), crc); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); + jffs2_free_raw_node_ref(raw); +- return -ENOMEM; ++ return 0; + } +- ic = jffs2_scan_make_ino_cache(c, ri.ino); ++ ic = jffs2_scan_make_ino_cache(c, ino); + if (!ic) { +- jffs2_free_full_dnode(fn); +- jffs2_free_tmp_dnode_info(tn); + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } ++ } + +- /* Build the data structures and file them for later */ +- raw->flash_offset = *ofs; +- raw->totlen = PAD(ri.totlen); ++ /* Wheee. It worked */ ++ ++ raw->flash_offset = ofs | REF_UNCHECKED; ++ raw->__totlen = PAD(je32_to_cpu(ri->totlen)); + raw->next_phys = NULL; + raw->next_in_ino = ic->nodes; ++ + ic->nodes = raw; + if (!jeb->first_node) + jeb->first_node = raw; +@@ -538,134 +713,56 @@ + jeb->last_node = raw; + + D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", +- ri.ino, ri.version, ri.offset, ri.offset+ri.dsize)); +- +- pseudo_random += ri.version; +- +- for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) { +- if ((*tn_list)->version < ri.version) +- continue; +- if ((*tn_list)->version > ri.version) +- break; +- /* Wheee. We've found another instance of the same version number. +- We should obsolete one of them. +- */ +- D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3)); +- if (!jeb->used_size) { +- D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", +- jeb->offset, raw->flash_offset & ~3)); +- ri.nodetype &= ~JFFS2_NODE_ACCURATE; +- /* Perhaps we could also mark it as such on the medium. Maybe later */ +- } +- break; +- } +- +- if (ri.nodetype & JFFS2_NODE_ACCURATE) { +- memset(fn,0,sizeof(*fn)); +- +- fn->ofs = ri.offset; +- fn->size = ri.dsize; +- fn->frags = 0; +- fn->raw = raw; +- +- tn->next = NULL; +- tn->fn = fn; +- tn->version = ri.version; ++ je32_to_cpu(ri->ino), je32_to_cpu(ri->version), ++ je32_to_cpu(ri->offset), ++ je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize))); + +- USED_SPACE(PAD(ri.totlen)); +- jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes); +- /* Make sure the one we just added is the _last_ in the list +- with this version number, so the older ones get obsoleted */ +- while (tn->next && tn->next->version == tn->version) { ++ pseudo_random += je32_to_cpu(ri->version); + +- D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n", +- fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version)); +- +- if(tn->fn != fn) +- BUG(); +- tn->fn = tn->next->fn; +- tn->next->fn = fn; +- tn = tn->next; +- } +- } else { +- jffs2_free_full_dnode(fn); +- jffs2_free_tmp_dnode_info(tn); +- raw->flash_offset |= 1; +- DIRTY_SPACE(PAD(ri.totlen)); +- } +- *ofs += PAD(ri.totlen); ++ UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); + return 0; + } + +-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs) ++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_dirent *rd, uint32_t ofs) + { + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; + struct jffs2_inode_cache *ic; +- struct jffs2_raw_dirent rd; +- __u16 oldnodetype; +- int ret; +- __u32 crc; +- ssize_t retlen; +- +- D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs)); ++ uint32_t crc; + +- ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret); +- return ret; +- } +- if (retlen != sizeof(rd)) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs, sizeof(rd)); +- return -EIO; +- } ++ D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); + +- /* We sort of assume that the node was accurate when it was +- first written to the medium :) */ +- oldnodetype = rd.nodetype; +- rd.nodetype |= JFFS2_NODE_ACCURATE; +- crc = crc32(0, &rd, sizeof(rd)-8); +- rd.nodetype = oldnodetype; ++ /* We don't get here unless the node is still valid, so we don't have to ++ mask in the ACCURATE bit any more. */ ++ crc = crc32(0, rd, sizeof(*rd)-8); + +- if (crc != rd.node_crc) { ++ if (crc != je32_to_cpu(rd->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, rd.node_crc, crc); +- /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(4); +- *ofs += 4; ++ ofs, je32_to_cpu(rd->node_crc), crc); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + +- pseudo_random += rd.version; ++ pseudo_random += je32_to_cpu(rd->version); + +- fd = jffs2_alloc_full_dirent(rd.nsize+1); ++ fd = jffs2_alloc_full_dirent(rd->nsize+1); + if (!fd) { + return -ENOMEM; +-} +- ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]); +- if (ret) { +- jffs2_free_full_dirent(fd); +- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", +- *ofs + sizeof(rd), ret); +- return ret; +- } +- if (retlen != rd.nsize) { +- jffs2_free_full_dirent(fd); +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs + sizeof(rd), rd.nsize); +- return -EIO; + } +- crc = crc32(0, fd->name, rd.nsize); +- if (crc != rd.name_crc) { ++ memcpy(&fd->name, rd->name, rd->nsize); ++ fd->name[rd->nsize] = 0; ++ ++ crc = crc32(0, fd->name, rd->nsize); ++ if (crc != je32_to_cpu(rd->name_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, rd.name_crc, crc); +- fd->name[rd.nsize]=0; +- D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino)); ++ ofs, je32_to_cpu(rd->name_crc), crc); ++ D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); + jffs2_free_full_dirent(fd); + /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(PAD(rd.totlen)); +- *ofs += PAD(rd.totlen); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + raw = jffs2_alloc_raw_node_ref(); +@@ -674,15 +771,15 @@ + printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); + return -ENOMEM; + } +- ic = jffs2_scan_make_ino_cache(c, rd.pino); ++ ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); + if (!ic) { + jffs2_free_full_dirent(fd); + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } + +- raw->totlen = PAD(rd.totlen); +- raw->flash_offset = *ofs; ++ raw->__totlen = PAD(je32_to_cpu(rd->totlen)); ++ raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = ic->nodes; + ic->nodes = raw; +@@ -692,24 +789,15 @@ + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + +- if (rd.nodetype & JFFS2_NODE_ACCURATE) { + fd->raw = raw; + fd->next = NULL; +- fd->version = rd.version; +- fd->ino = rd.ino; +- fd->name[rd.nsize]=0; +- fd->nhash = full_name_hash(fd->name, rd.nsize); +- fd->type = rd.type; +- +- USED_SPACE(PAD(rd.totlen)); +- jffs2_add_fd_to_list(c, fd, &ic->scan->dents); +- } else { +- raw->flash_offset |= 1; +- jffs2_free_full_dirent(fd); ++ fd->version = je32_to_cpu(rd->version); ++ fd->ino = je32_to_cpu(rd->ino); ++ fd->nhash = full_name_hash(fd->name, rd->nsize); ++ fd->type = rd->type; ++ USED_SPACE(PAD(je32_to_cpu(rd->totlen))); ++ jffs2_add_fd_to_list(c, fd, &ic->scan_dents); + +- DIRTY_SPACE(PAD(rd.totlen)); +- } +- *ofs += PAD(rd.totlen); + return 0; + } + +@@ -731,26 +819,90 @@ + struct list_head *n = head->next; + + list_del(head); +- while(count--) ++ while(count--) { + n = n->next; ++ } + list_add(head, n); + } + +-static void jffs2_rotate_lists(struct jffs2_sb_info *c) ++void jffs2_rotate_lists(struct jffs2_sb_info *c) + { + uint32_t x; ++ uint32_t rotateby; + + x = count_list(&c->clean_list); +- if (x) +- rotate_list((&c->clean_list), pseudo_random % x); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby)); ++ ++ rotate_list((&c->clean_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n", ++ list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty clean_list\n")); ++ } ++ ++ x = count_list(&c->very_dirty_list); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby)); ++ ++ rotate_list((&c->very_dirty_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n", ++ list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n")); ++ } + + x = count_list(&c->dirty_list); +- if (x) +- rotate_list((&c->dirty_list), pseudo_random % x); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby)); ++ ++ rotate_list((&c->dirty_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n", ++ list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n")); ++ } ++ ++ x = count_list(&c->erasable_list); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby)); + +- if (c->nr_erasing_blocks) +- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks); ++ rotate_list((&c->erasable_list), rotateby); + +- if (c->nr_free_blocks) /* Not that it should ever be zero */ +- rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks); ++ D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n", ++ list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n")); ++ } ++ ++ if (c->nr_erasing_blocks) { ++ rotateby = pseudo_random % c->nr_erasing_blocks; ++ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby)); ++ ++ rotate_list((&c->erase_pending_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n", ++ list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n")); ++ } ++ ++ if (c->nr_free_blocks) { ++ rotateby = pseudo_random % c->nr_free_blocks; ++ D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby)); ++ ++ rotate_list((&c->free_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n", ++ list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty free_list\n")); ++ } + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/super-v24.c linux/fs/jffs2/super-v24.c +--- linux-mips-2.4.27/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/super-v24.c 2004-11-19 10:25:12.126165288 +0100 +@@ -0,0 +1,167 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: super-v24.c,v 1.75 2003/10/06 12:52:29 dwmw2 Exp $ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "nodelist.h" ++ ++#ifndef MTD_BLOCK_MAJOR ++#define MTD_BLOCK_MAJOR 31 ++#endif ++ ++static void jffs2_put_super (struct super_block *); ++ ++static struct super_operations jffs2_super_operations = ++{ ++ .read_inode = jffs2_read_inode, ++ .put_super = jffs2_put_super, ++ .write_super = jffs2_write_super, ++ .statfs = jffs2_statfs, ++ .remount_fs = jffs2_remount_fs, ++ .clear_inode = jffs2_clear_inode, ++ .dirty_inode = jffs2_dirty_inode, ++}; ++ ++ ++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) ++{ ++ struct jffs2_sb_info *c; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); ++ ++ if (major(sb->s_dev) != MTD_BLOCK_MAJOR) { ++ if (!silent) ++ printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); ++ return NULL; ++ } ++ ++ c = JFFS2_SB_INFO(sb); ++ memset(c, 0, sizeof(*c)); ++ ++ sb->s_op = &jffs2_super_operations; ++ ++ c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); ++ if (!c->mtd) { ++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); ++ return NULL; ++ } ++ ++ ret = jffs2_do_fill_super(sb, data, silent); ++ if (ret) { ++ put_mtd_device(c->mtd); ++ return NULL; ++ } ++ ++ return sb; ++} ++ ++static void jffs2_put_super (struct super_block *sb) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ ++ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); ++ ++ ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_stop_garbage_collect_thread(c); ++ down(&c->alloc_sem); ++ jffs2_flush_wbuf_pad(c); ++ up(&c->alloc_sem); ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ jffs2_nand_flash_cleanup(c); ++ kfree(c->inocache_list); ++ if (c->mtd->sync) ++ c->mtd->sync(c->mtd); ++ put_mtd_device(c->mtd); ++ ++ D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); ++} ++ ++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); ++ ++static int __init init_jffs2_fs(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "JFFS2 version 2.2." ++#ifdef CONFIG_FS_JFFS2_NAND ++ " (NAND)" ++#endif ++ " (C) 2001-2003 Red Hat, Inc.\n"); ++ ++#ifdef JFFS2_OUT_OF_KERNEL ++ /* sanity checks. Could we do these at compile time? */ ++ if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { ++ printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", ++ sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); ++ return -EIO; ++ } ++ ++ if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { ++ printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", ++ sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); ++ return -EIO; ++ } ++#endif ++ ret = jffs2_zlib_init(); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); ++ goto out; ++ } ++ ret = jffs2_create_slab_caches(); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); ++ goto out_zlib; ++ } ++ ret = register_filesystem(&jffs2_fs_type); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); ++ goto out_slab; ++ } ++ return 0; ++ ++ out_slab: ++ jffs2_destroy_slab_caches(); ++ out_zlib: ++ jffs2_zlib_exit(); ++ out: ++ ++ return ret; ++} ++ ++static void __exit exit_jffs2_fs(void) ++{ ++ jffs2_destroy_slab_caches(); ++ jffs2_zlib_exit(); ++ unregister_filesystem(&jffs2_fs_type); ++} ++ ++module_init(init_jffs2_fs); ++module_exit(exit_jffs2_fs); ++ ++MODULE_DESCRIPTION("The Journalling Flash File System, v2"); ++MODULE_AUTHOR("Red Hat, Inc."); ++MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for ++ // the sake of this tag. It's Free Software. +diff -Nurb linux-mips-2.4.27/fs/jffs2/super.c linux/fs/jffs2/super.c +--- linux-mips-2.4.27/fs/jffs2/super.c 2003-01-11 18:53:17.000000000 +0100 ++++ linux/fs/jffs2/super.c 2004-11-19 10:25:12.127165136 +0100 +@@ -1,291 +1,257 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: super.c,v 1.48.2.3 2002/10/11 09:04:44 dwmw2 Exp $ ++ * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $ + * + */ + + #include + #include + #include +-#include + #include + #include + #include + #include ++#include + #include + #include + #include +-#include ++#include ++#include + #include "nodelist.h" + +-#ifndef MTD_BLOCK_MAJOR +-#define MTD_BLOCK_MAJOR 31 +-#endif ++static void jffs2_put_super(struct super_block *); ++ ++static kmem_cache_t *jffs2_inode_cachep; ++ ++static struct inode *jffs2_alloc_inode(struct super_block *sb) ++{ ++ struct jffs2_inode_info *ei; ++ ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++static void jffs2_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); ++} ++ ++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ++{ ++ struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; + +-extern void jffs2_read_inode (struct inode *); +-void jffs2_put_super (struct super_block *); +-void jffs2_write_super (struct super_block *); +-static int jffs2_statfs (struct super_block *, struct statfs *); +-int jffs2_remount_fs (struct super_block *, int *, char *); +-extern void jffs2_clear_inode (struct inode *); ++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == ++ SLAB_CTOR_CONSTRUCTOR) { ++ init_MUTEX_LOCKED(&ei->sem); ++ inode_init_once(&ei->vfs_inode); ++ } ++} + + static struct super_operations jffs2_super_operations = + { +- read_inode: jffs2_read_inode, +-// delete_inode: jffs2_delete_inode, +- put_super: jffs2_put_super, +- write_super: jffs2_write_super, +- statfs: jffs2_statfs, +- remount_fs: jffs2_remount_fs, +- clear_inode: jffs2_clear_inode ++ .alloc_inode = jffs2_alloc_inode, ++ .destroy_inode =jffs2_destroy_inode, ++ .read_inode = jffs2_read_inode, ++ .put_super = jffs2_put_super, ++ .write_super = jffs2_write_super, ++ .statfs = jffs2_statfs, ++ .remount_fs = jffs2_remount_fs, ++ .clear_inode = jffs2_clear_inode, ++ .dirty_inode = jffs2_dirty_inode, + }; + +-static int jffs2_statfs(struct super_block *sb, struct statfs *buf) ++static int jffs2_sb_compare(struct super_block *sb, void *data) + { ++ struct jffs2_sb_info *p = data; + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- unsigned long avail; + +- buf->f_type = JFFS2_SUPER_MAGIC; +- buf->f_bsize = 1 << PAGE_SHIFT; +- buf->f_blocks = c->flash_size >> PAGE_SHIFT; +- buf->f_files = 0; +- buf->f_ffree = 0; +- buf->f_namelen = JFFS2_MAX_NAME_LEN; +- +- spin_lock_bh(&c->erase_completion_lock); +- +- avail = c->dirty_size + c->free_size; +- if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE) +- avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE; +- else +- avail = 0; +- +- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; +- +-#if CONFIG_JFFS2_FS_DEBUG > 0 +- printk(KERN_DEBUG "STATFS:\n"); +- printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); +- printk(KERN_DEBUG "used_size: %08x\n", c->used_size); +- printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); +- printk(KERN_DEBUG "free_size: %08x\n", c->free_size); +- printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); +- printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); +- printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); +- +- if (c->nextblock) { +- printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset); +- } else { +- printk(KERN_DEBUG "nextblock: NULL\n"); +- } +- if (c->gcblock) { +- printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset); ++ /* The superblocks are considered to be equivalent if the underlying MTD ++ device is the same one */ ++ if (c->mtd == p->mtd) { ++ D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); ++ return 1; + } else { +- printk(KERN_DEBUG "gcblock: NULL\n"); ++ D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", ++ c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); ++ return 0; + } +- if (list_empty(&c->clean_list)) { +- printk(KERN_DEBUG "clean_list: empty\n"); +- } else { +- struct list_head *this; ++} + +- list_for_each(this, &c->clean_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->dirty_list)) { +- printk(KERN_DEBUG "dirty_list: empty\n"); +- } else { +- struct list_head *this; ++static int jffs2_sb_set(struct super_block *sb, void *data) ++{ ++ struct jffs2_sb_info *p = data; + +- list_for_each(this, &c->dirty_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->erasing_list)) { +- printk(KERN_DEBUG "erasing_list: empty\n"); +- } else { +- struct list_head *this; ++ /* For persistence of NFS exports etc. we use the same s_dev ++ each time we mount the device, don't just use an anonymous ++ device */ ++ sb->s_fs_info = p; ++ p->os_priv = sb; ++ sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); + +- list_for_each(this, &c->erasing_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->erase_pending_list)) { +- printk(KERN_DEBUG "erase_pending_list: empty\n"); +- } else { +- struct list_head *this; ++ return 0; ++} + +- list_for_each(this, &c->erase_pending_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->free_list)) { +- printk(KERN_DEBUG "free_list: empty\n"); +- } else { +- struct list_head *this; ++static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data, struct mtd_info *mtd) ++{ ++ struct super_block *sb; ++ struct jffs2_sb_info *c; ++ int ret; + +- list_for_each(this, &c->free_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "free_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->bad_list)) { +- printk(KERN_DEBUG "bad_list: empty\n"); +- } else { +- struct list_head *this; ++ c = kmalloc(sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return ERR_PTR(-ENOMEM); ++ memset(c, 0, sizeof(*c)); ++ c->mtd = mtd; + +- list_for_each(this, &c->bad_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->bad_used_list)) { +- printk(KERN_DEBUG "bad_used_list: empty\n"); +- } else { +- struct list_head *this; ++ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); ++ ++ if (IS_ERR(sb)) ++ goto out_put; + +- list_for_each(this, &c->bad_used_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset); ++ if (sb->s_root) { ++ /* New mountpoint for JFFS2 which is already mounted */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", ++ mtd->index, mtd->name)); ++ goto out_put; + } ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", ++ mtd->index, mtd->name)); ++ ++ sb->s_op = &jffs2_super_operations; ++ ++ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); ++ ++ if (ret) { ++ /* Failure case... */ ++ up_write(&sb->s_umount); ++ deactivate_super(sb); ++ return ERR_PTR(ret); + } +-#endif /* CONFIG_JFFS2_FS_DEBUG */ + +- spin_unlock_bh(&c->erase_completion_lock); ++ sb->s_flags |= MS_ACTIVE; ++ return sb; + ++ out_put: ++ kfree(c); ++ put_mtd_device(mtd); + +- return 0; ++ return sb; + } + +-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) ++static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data, int mtdnr) + { +- struct jffs2_sb_info *c; +- struct inode *root_i; +- int i; +- +- D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); ++ struct mtd_info *mtd; + +- if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { +- if (!silent) +- printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); +- return NULL; ++ mtd = get_mtd_device(NULL, mtdnr); ++ if (!mtd) { ++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); ++ return ERR_PTR(-EINVAL); + } + +- c = JFFS2_SB_INFO(sb); +- memset(c, 0, sizeof(*c)); ++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); ++} + +- c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); +- if (!c->mtd) { +- D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev))); +- return NULL; ++static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ int err; ++ struct nameidata nd; ++ int mtdnr; ++ ++ if (!dev_name) ++ return ERR_PTR(-EINVAL); ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); ++ ++ /* The preferred way of mounting in future; especially when ++ CONFIG_BLK_DEV is implemented - we specify the underlying ++ MTD device by number or by name, so that we don't require ++ block device support to be present in the kernel. */ ++ ++ /* FIXME: How to do the root fs this way? */ ++ ++ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { ++ /* Probably mounting without the blkdev crap */ ++ if (dev_name[3] == ':') { ++ struct mtd_info *mtd; ++ ++ /* Mount by MTD device name */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); ++ for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { ++ mtd = get_mtd_device(NULL, mtdnr); ++ if (mtd) { ++ if (!strcmp(mtd->name, dev_name+4)) ++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); ++ put_mtd_device(mtd); + } +- c->sector_size = c->mtd->erasesize; +- c->free_size = c->flash_size = c->mtd->size; +- c->nr_blocks = c->mtd->size / c->mtd->erasesize; +- c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); +- if (!c->blocks) +- goto out_mtd; +- for (i=0; inr_blocks; i++) { +- INIT_LIST_HEAD(&c->blocks[i].list); +- c->blocks[i].offset = i * c->sector_size; +- c->blocks[i].free_size = c->sector_size; +- c->blocks[i].dirty_size = 0; +- c->blocks[i].used_size = 0; +- c->blocks[i].first_node = NULL; +- c->blocks[i].last_node = NULL; +- } +- +- spin_lock_init(&c->nodelist_lock); +- init_MUTEX(&c->alloc_sem); +- init_waitqueue_head(&c->erase_wait); +- spin_lock_init(&c->erase_completion_lock); +- spin_lock_init(&c->inocache_lock); +- +- INIT_LIST_HEAD(&c->clean_list); +- INIT_LIST_HEAD(&c->dirty_list); +- INIT_LIST_HEAD(&c->erasing_list); +- INIT_LIST_HEAD(&c->erase_pending_list); +- INIT_LIST_HEAD(&c->erase_complete_list); +- INIT_LIST_HEAD(&c->free_list); +- INIT_LIST_HEAD(&c->bad_list); +- INIT_LIST_HEAD(&c->bad_used_list); +- c->highest_ino = 1; +- +- if (jffs2_build_filesystem(c)) { +- D1(printk(KERN_DEBUG "build_fs failed\n")); +- goto out_nodes; + } ++ printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); ++ } else if (isdigit(dev_name[3])) { ++ /* Mount by MTD device number name */ ++ char *endptr; + +- sb->s_op = &jffs2_super_operations; ++ mtdnr = simple_strtoul(dev_name+3, &endptr, 0); ++ if (!*endptr) { ++ /* It was a valid number */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); ++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); ++ } ++ } ++ } + +- D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n")); +- root_i = iget(sb, 1); +- if (is_bad_inode(root_i)) { +- D1(printk(KERN_WARNING "get root inode failed\n")); +- goto out_nodes; ++ /* Try the old way - the hack where we allowed users to mount ++ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ ++ ++ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", ++ err, nd.dentry->d_inode)); ++ ++ if (err) ++ return ERR_PTR(err); ++ ++ err = -EINVAL; ++ ++ if (!S_ISBLK(nd.dentry->d_inode->i_mode)) ++ goto out; ++ ++ if (nd.mnt->mnt_flags & MNT_NODEV) { ++ err = -EACCES; ++ goto out; + } + +- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n")); +- sb->s_root = d_alloc_root(root_i); +- if (!sb->s_root) +- goto out_root_i; ++ if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { ++ if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */ ++ printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", ++ dev_name); ++ goto out; ++ } + +-#if LINUX_VERSION_CODE >= 0x20403 +- sb->s_maxbytes = 0xFFFFFFFF; +-#endif +- sb->s_blocksize = PAGE_CACHE_SIZE; +- sb->s_blocksize_bits = PAGE_CACHE_SHIFT; +- sb->s_magic = JFFS2_SUPER_MAGIC; +- if (!(sb->s_flags & MS_RDONLY)) +- jffs2_start_garbage_collect_thread(c); +- return sb; ++ mtdnr = iminor(nd.dentry->d_inode); ++ path_release(&nd); + +- out_root_i: +- iput(root_i); +- out_nodes: +- jffs2_free_ino_caches(c); +- jffs2_free_raw_node_refs(c); +- kfree(c->blocks); +- out_mtd: +- put_mtd_device(c->mtd); +- return NULL; ++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); ++ ++out: ++ path_release(&nd); ++ return ERR_PTR(err); + } + +-void jffs2_put_super (struct super_block *sb) ++static void jffs2_put_super (struct super_block *sb) + { + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + +@@ -293,74 +259,53 @@ + + if (!(sb->s_flags & MS_RDONLY)) + jffs2_stop_garbage_collect_thread(c); ++ down(&c->alloc_sem); ++ jffs2_flush_wbuf_pad(c); ++ up(&c->alloc_sem); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + kfree(c->blocks); ++ jffs2_nand_flash_cleanup(c); ++ kfree(c->inocache_list); + if (c->mtd->sync) + c->mtd->sync(c->mtd); +- put_mtd_device(c->mtd); + + D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); + } + +-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) ++static void jffs2_kill_sb(struct super_block *sb) + { + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- +- if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) +- return -EROFS; +- +- /* We stop if it was running, then restart if it needs to. +- This also catches the case where it was stopped and this +- is just a remount to restart it */ +- if (!(sb->s_flags & MS_RDONLY)) +- jffs2_stop_garbage_collect_thread(c); +- +- if (!(*flags & MS_RDONLY)) +- jffs2_start_garbage_collect_thread(c); +- +- sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY); +- +- return 0; +-} +- +-void jffs2_write_super (struct super_block *sb) +-{ +- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- sb->s_dirt = 0; +- +- if (sb->s_flags & MS_RDONLY) +- return; +- +- jffs2_garbage_collect_trigger(c); +- jffs2_erase_pending_blocks(c); +- jffs2_mark_erased_blocks(c); ++ generic_shutdown_super(sb); ++ put_mtd_device(c->mtd); ++ kfree(c); + } + +- +-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); ++static struct file_system_type jffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "jffs2", ++ .get_sb = jffs2_get_sb, ++ .kill_sb = jffs2_kill_sb, ++}; + + static int __init init_jffs2_fs(void) + { + int ret; + +- printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n"); +- +-#ifdef JFFS2_OUT_OF_KERNEL +- /* sanity checks. Could we do these at compile time? */ +- if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { +- printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", +- sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); +- return -EIO; +- } +- +- if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { +- printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", +- sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); +- return -EIO; +- } ++ printk(KERN_INFO "JFFS2 version 2.2." ++#ifdef CONFIG_FS_JFFS2_NAND ++ " (NAND)" + #endif ++ " (C) 2001-2003 Red Hat, Inc.\n"); + ++ jffs2_inode_cachep = kmem_cache_create("jffs2_i", ++ sizeof(struct jffs2_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ jffs2_i_init_once, NULL); ++ if (!jffs2_inode_cachep) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); ++ return -ENOMEM; ++ } + ret = jffs2_zlib_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); +@@ -388,9 +333,10 @@ + + static void __exit exit_jffs2_fs(void) + { ++ unregister_filesystem(&jffs2_fs_type); + jffs2_destroy_slab_caches(); + jffs2_zlib_exit(); +- unregister_filesystem(&jffs2_fs_type); ++ kmem_cache_destroy(jffs2_inode_cachep); + } + + module_init(init_jffs2_fs); +diff -Nurb linux-mips-2.4.27/fs/jffs2/symlink.c linux/fs/jffs2/symlink.c +--- linux-mips-2.4.27/fs/jffs2/symlink.c 2002-06-27 00:36:20.000000000 +0200 ++++ linux/fs/jffs2/symlink.c 2004-11-19 10:25:12.129164832 +0100 +@@ -3,35 +3,11 @@ + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $ ++ * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $ + * + */ + +@@ -39,7 +15,6 @@ + #include + #include + #include +-#include + #include "nodelist.h" + + int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen); +@@ -47,45 +22,17 @@ + + struct inode_operations jffs2_symlink_inode_operations = + { +- readlink: jffs2_readlink, +- follow_link: jffs2_follow_link, +- setattr: jffs2_setattr ++ .readlink = jffs2_readlink, ++ .follow_link = jffs2_follow_link, ++ .setattr = jffs2_setattr + }; + +-static char *jffs2_getlink(struct dentry *dentry) +-{ +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); +- char *buf; +- int ret; +- +- down(&f->sem); +- if (!f->metadata) { +- up(&f->sem); +- printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino); +- return ERR_PTR(-EINVAL); +- } +- buf = kmalloc(f->metadata->size+1, GFP_USER); +- if (!buf) { +- up(&f->sem); +- return ERR_PTR(-ENOMEM); +- } +- buf[f->metadata->size]=0; +- +- ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size); +- up(&f->sem); +- if (ret) { +- kfree(buf); +- return ERR_PTR(ret); +- } +- return buf; +- +-} + int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen) + { + unsigned char *kbuf; + int ret; + +- kbuf = jffs2_getlink(dentry); ++ kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + +@@ -99,7 +46,7 @@ + unsigned char *buf; + int ret; + +- buf = jffs2_getlink(dentry); ++ buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + + if (IS_ERR(buf)) + return PTR_ERR(buf); +diff -Nurb linux-mips-2.4.27/fs/jffs2/wbuf.c linux/fs/jffs2/wbuf.c +--- linux-mips-2.4.27/fs/jffs2/wbuf.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/wbuf.c 2004-11-19 10:25:12.131164528 +0100 +@@ -0,0 +1,1156 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: wbuf.c,v 1.58 2003/11/02 09:51:10 dwmw2 Exp $ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "nodelist.h" ++ ++/* For testing write failures */ ++#undef BREAKME ++#undef BREAKMEHEADER ++ ++#ifdef BREAKME ++static unsigned char *brokenbuf; ++#endif ++ ++/* max. erase failures before we mark a block bad */ ++#define MAX_ERASE_FAILURES 5 ++ ++/* two seconds timeout for timed wbuf-flushing */ ++#define WBUF_FLUSH_TIMEOUT 2 * HZ ++ ++struct jffs2_inodirty { ++ uint32_t ino; ++ struct jffs2_inodirty *next; ++}; ++ ++static struct jffs2_inodirty inodirty_nomem; ++ ++static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ struct jffs2_inodirty *this = c->wbuf_inodes; ++ ++ /* If a malloc failed, consider _everything_ dirty */ ++ if (this == &inodirty_nomem) ++ return 1; ++ ++ /* If ino == 0, _any_ non-GC writes mean 'yes' */ ++ if (this && !ino) ++ return 1; ++ ++ /* Look to see if the inode in question is pending in the wbuf */ ++ while (this) { ++ if (this->ino == ino) ++ return 1; ++ this = this->next; ++ } ++ return 0; ++} ++ ++static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) ++{ ++ struct jffs2_inodirty *this; ++ ++ this = c->wbuf_inodes; ++ ++ if (this != &inodirty_nomem) { ++ while (this) { ++ struct jffs2_inodirty *next = this->next; ++ kfree(this); ++ this = next; ++ } ++ } ++ c->wbuf_inodes = NULL; ++} ++ ++static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ struct jffs2_inodirty *new; ++ ++ /* Mark the superblock dirty so that kupdated will flush... */ ++ OFNI_BS_2SFFJ(c)->s_dirt = 1; ++ ++ if (jffs2_wbuf_pending_for_ino(c, ino)) ++ return; ++ ++ new = kmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) { ++ D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n")); ++ jffs2_clear_wbuf_ino_list(c); ++ c->wbuf_inodes = &inodirty_nomem; ++ return; ++ } ++ new->ino = ino; ++ new->next = c->wbuf_inodes; ++ c->wbuf_inodes = new; ++ return; ++} ++ ++static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) ++{ ++ struct list_head *this, *next; ++ static int n; ++ ++ if (list_empty(&c->erasable_pending_wbuf_list)) ++ return; ++ ++ list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ ++ D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); ++ list_del(this); ++ if ((jiffies + (n++)) & 127) { ++ /* Most of the time, we just erase it immediately. Otherwise we ++ spend ages scanning it on mount, etc. */ ++ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); ++ list_add_tail(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } else { ++ /* Sometimes, however, we leave it elsewhere so it doesn't get ++ immediately reused, and we spread the load a bit. */ ++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_list); ++ } ++ } ++} ++ ++/* Recover from failure to write wbuf. Recover the nodes up to the ++ * wbuf, not the one which we were starting to try to write. */ ++ ++static void jffs2_wbuf_recover(struct jffs2_sb_info *c) ++{ ++ struct jffs2_eraseblock *jeb, *new_jeb; ++ struct jffs2_raw_node_ref **first_raw, **raw; ++ size_t retlen; ++ int ret; ++ unsigned char *buf; ++ uint32_t start, end, ofs, len; ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; ++ ++ D1(printk("About to refile bad block at %08x\n", jeb->offset)); ++ ++ D2(jffs2_dump_block_lists(c)); ++ /* File the existing block on the bad_used_list.... */ ++ if (c->nextblock == jeb) ++ c->nextblock = NULL; ++ else /* Not sure this should ever happen... need more coffee */ ++ list_del(&jeb->list); ++ if (jeb->first_node) { ++ D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); ++ list_add(&jeb->list, &c->bad_used_list); ++ } else { ++ BUG(); ++ /* It has to have had some nodes or we couldn't be here */ ++ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); ++ list_add(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } ++ D2(jffs2_dump_block_lists(c)); ++ ++ /* Adjust its size counts accordingly */ ++ c->wasted_size += jeb->free_size; ++ c->free_size -= jeb->free_size; ++ jeb->wasted_size += jeb->free_size; ++ jeb->free_size = 0; ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ /* Find the first node to be recovered, by skipping over every ++ node which ends before the wbuf starts, or which is obsolete. */ ++ first_raw = &jeb->first_node; ++ while (*first_raw && ++ (ref_obsolete(*first_raw) || ++ (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { ++ D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ++ ref_offset(*first_raw), ref_flags(*first_raw), ++ (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), ++ c->wbuf_ofs)); ++ first_raw = &(*first_raw)->next_phys; ++ } ++ ++ if (!*first_raw) { ++ /* All nodes were obsolete. Nothing to recover. */ ++ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); ++ spin_unlock(&c->erase_completion_lock); ++ return; ++ } ++ ++ start = ref_offset(*first_raw); ++ end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); ++ ++ /* Find the last node to be recovered */ ++ raw = first_raw; ++ while ((*raw)) { ++ if (!ref_obsolete(*raw)) ++ end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); ++ ++ raw = &(*raw)->next_phys; ++ } ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); ++ ++ buf = NULL; ++ if (start < c->wbuf_ofs) { ++ /* First affected node was already partially written. ++ * Attempt to reread the old data into our buffer. */ ++ ++ buf = kmalloc(end - start, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); ++ ++ goto read_failed; ++ } ++ ++ /* Do the read... */ ++ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); ++ if (ret == -EIO && retlen == c->wbuf_ofs - start) { ++ /* ECC recovered */ ++ ret = 0; ++ } ++ if (ret || retlen != c->wbuf_ofs - start) { ++ printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); ++ ++ kfree(buf); ++ buf = NULL; ++ read_failed: ++ first_raw = &(*first_raw)->next_phys; ++ /* If this was the only node to be recovered, give up */ ++ if (!(*first_raw)) ++ return; ++ ++ /* It wasn't. Go on and try to recover nodes complete in the wbuf */ ++ start = ref_offset(*first_raw); ++ } else { ++ /* Read succeeded. Copy the remaining data from the wbuf */ ++ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); ++ } ++ } ++ /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. ++ Either 'buf' contains the data, or we find it in the wbuf */ ++ ++ ++ /* ... and get an allocation of space from a shiny new block instead */ ++ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); ++ if (ret) { ++ printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); ++ if (buf) ++ kfree(buf); ++ return; ++ } ++ if (end-start >= c->wbuf_pagesize) { ++ /* Need to do another write immediately. This, btw, ++ means that we'll be writing from 'buf' and not from ++ the wbuf. Since if we're writing from the wbuf there ++ won't be more than a wbuf full of data, now will ++ there? :) */ ++ ++ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); ++ ++ D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", ++ towrite, ofs)); ++ ++#ifdef BREAKMEHEADER ++ static int breakme; ++ if (breakme++ == 20) { ++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); ++ breakme = 0; ++ c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, ++ brokenbuf, NULL, c->oobinfo); ++ ret = -EIO; ++ } else ++#endif ++ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, ++ buf, NULL, c->oobinfo); ++ ++ if (ret || retlen != towrite) { ++ /* Argh. We tried. Really we did. */ ++ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); ++ kfree(buf); ++ ++ if (retlen) { ++ struct jffs2_raw_node_ref *raw2; ++ ++ raw2 = jffs2_alloc_raw_node_ref(); ++ if (!raw2) ++ return; ++ ++ raw2->flash_offset = ofs | REF_OBSOLETE; ++ raw2->__totlen = ref_totlen(c, jeb, *first_raw); ++ raw2->next_phys = NULL; ++ raw2->next_in_ino = NULL; ++ ++ jffs2_add_physical_node_ref(c, raw2); ++ } ++ return; ++ } ++ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); ++ ++ c->wbuf_len = (end - start) - towrite; ++ c->wbuf_ofs = ofs + towrite; ++ memcpy(c->wbuf, buf + towrite, c->wbuf_len); ++ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ ++ ++ kfree(buf); ++ } else { ++ /* OK, now we're left with the dregs in whichever buffer we're using */ ++ if (buf) { ++ memcpy(c->wbuf, buf, end-start); ++ kfree(buf); ++ } else { ++ memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); ++ } ++ c->wbuf_ofs = ofs; ++ c->wbuf_len = end - start; ++ } ++ ++ /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ ++ new_jeb = &c->blocks[ofs / c->sector_size]; ++ ++ spin_lock(&c->erase_completion_lock); ++ if (new_jeb->first_node) { ++ /* Odd, but possible with ST flash later maybe */ ++ new_jeb->last_node->next_phys = *first_raw; ++ } else { ++ new_jeb->first_node = *first_raw; ++ } ++ ++ raw = first_raw; ++ while (*raw) { ++ uint32_t rawlen = ref_totlen(c, jeb, *raw); ++ ++ D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", ++ rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); ++ ++ if (ref_obsolete(*raw)) { ++ /* Shouldn't really happen much */ ++ new_jeb->dirty_size += rawlen; ++ new_jeb->free_size -= rawlen; ++ c->dirty_size += rawlen; ++ } else { ++ new_jeb->used_size += rawlen; ++ new_jeb->free_size -= rawlen; ++ jeb->dirty_size += rawlen; ++ jeb->used_size -= rawlen; ++ c->dirty_size += rawlen; ++ } ++ c->free_size -= rawlen; ++ (*raw)->flash_offset = ofs | ref_flags(*raw); ++ ofs += rawlen; ++ new_jeb->last_node = *raw; ++ ++ raw = &(*raw)->next_phys; ++ } ++ ++ /* Fix up the original jeb now it's on the bad_list */ ++ *first_raw = NULL; ++ if (first_raw == &jeb->first_node) { ++ jeb->last_node = NULL; ++ D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); ++ list_del(&jeb->list); ++ list_add(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } ++ else ++ jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ ACCT_SANITY_CHECK(c,new_jeb); ++ D1(ACCT_PARANOIA_CHECK(new_jeb)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); ++} ++ ++/* Meaning of pad argument: ++ 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. ++ 1: Pad, do not adjust nextblock free_size ++ 2: Pad, adjust nextblock free_size ++*/ ++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ++{ ++ int ret; ++ size_t retlen; ++ ++ /* Nothing to do if not NAND flash. In particular, we shouldn't ++ del_timer() the timer we never initialised. */ ++ if (jffs2_can_mark_obsolete(c)) ++ return 0; ++ ++ if (!down_trylock(&c->alloc_sem)) { ++ up(&c->alloc_sem); ++ printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); ++ BUG(); ++ } ++ ++ if(!c->wbuf || !c->wbuf_len) ++ return 0; ++ ++ /* claim remaining space on the page ++ this happens, if we have a change to a new block, ++ or if fsync forces us to flush the writebuffer. ++ if we have a switch to next page, we will not have ++ enough remaining space for this. ++ */ ++ if (pad) { ++ c->wbuf_len = PAD(c->wbuf_len); ++ ++ if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { ++ struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); ++ padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); ++ padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); ++ padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); ++ } ++ } ++ /* else jffs2_flash_writev has actually filled in the rest of the ++ buffer for us, and will deal with the node refs etc. later. */ ++ ++#ifdef BREAKME ++ static int breakme; ++ if (breakme++ == 20) { ++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); ++ breakme = 0; ++ c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, ++ &retlen, brokenbuf, NULL, c->oobinfo); ++ ret = -EIO; ++ } else ++#endif ++ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); ++ ++ ++ if (ret || retlen != c->wbuf_pagesize) { ++ if (ret) ++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); ++ else { ++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", ++ retlen, c->wbuf_pagesize); ++ ret = -EIO; ++ } ++ ++ jffs2_wbuf_recover(c); ++ ++ return ret; ++ } ++ ++ /* Adjusting free size of next block only, if it's called from fsync ! */ ++ if (pad == 2) { ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n")); ++ spin_lock(&c->erase_completion_lock); ++ if (!c->nextblock) ++ BUG(); ++ /* wbuf_pagesize - wbuf_len is the amount of space that's to be ++ padded. If there is less free space in the block than that, ++ something screwed up */ ++ if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) { ++ printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", ++ c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); ++ printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", ++ c->nextblock->offset, c->nextblock->free_size); ++ BUG(); ++ } ++ c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len); ++ c->free_size -= (c->wbuf_pagesize - c->wbuf_len); ++ c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len); ++ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); ++ spin_unlock(&c->erase_completion_lock); ++ } ++ ++ /* Stick any now-obsoleted blocks on the erase_pending_list */ ++ spin_lock(&c->erase_completion_lock); ++ jffs2_refile_wbuf_blocks(c); ++ jffs2_clear_wbuf_ino_list(c); ++ spin_unlock(&c->erase_completion_lock); ++ ++ memset(c->wbuf,0xff,c->wbuf_pagesize); ++ /* adjust write buffer offset, else we get a non contiguous write bug */ ++ c->wbuf_ofs += c->wbuf_pagesize; ++ c->wbuf_len = 0; ++ return 0; ++} ++ ++/* Trigger garbage collection to flush the write-buffer. ++ If ino arg is zero, do it if _any_ real (i.e. not GC) writes are ++ outstanding. If ino arg non-zero, do it only if a write for the ++ given inode is outstanding. */ ++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ uint32_t old_wbuf_ofs; ++ uint32_t old_wbuf_len; ++ int ret = 0; ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); ++ ++ down(&c->alloc_sem); ++ if (!jffs2_wbuf_pending_for_ino(c, ino)) { ++ D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); ++ up(&c->alloc_sem); ++ return 0; ++ } ++ ++ old_wbuf_ofs = c->wbuf_ofs; ++ old_wbuf_len = c->wbuf_len; ++ ++ if (c->unchecked_size) { ++ /* GC won't make any progress for a while */ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); ++ ret = __jffs2_flush_wbuf(c, 2); ++ } else while (old_wbuf_len && ++ old_wbuf_ofs == c->wbuf_ofs) { ++ ++ up(&c->alloc_sem); ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ++ ++ ret = jffs2_garbage_collect_pass(c); ++ if (ret) { ++ /* GC failed. Flush it with padding instead */ ++ down(&c->alloc_sem); ++ ret = __jffs2_flush_wbuf(c, 2); ++ break; ++ } ++ down(&c->alloc_sem); ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); ++ ++ up(&c->alloc_sem); ++ return ret; ++} ++ ++/* Pad write-buffer to end and write it, wasting space. */ ++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) ++{ ++ return __jffs2_flush_wbuf(c, 1); ++} ++ ++ ++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) ++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) ++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) ++{ ++ struct iovec outvecs[3]; ++ uint32_t totlen = 0; ++ uint32_t split_ofs = 0; ++ uint32_t old_totlen; ++ int ret, splitvec = -1; ++ int invec, outvec; ++ size_t wbuf_retlen; ++ unsigned char *wbuf_ptr; ++ size_t donelen = 0; ++ uint32_t outvec_to = to; ++ ++ /* If not NAND flash, don't bother */ ++ if (!c->wbuf) ++ return jffs2_flash_direct_writev(c, invecs, count, to, retlen); ++ ++ /* If wbuf_ofs is not initialized, set it to target address */ ++ if (c->wbuf_ofs == 0xFFFFFFFF) { ++ c->wbuf_ofs = PAGE_DIV(to); ++ c->wbuf_len = PAGE_MOD(to); ++ memset(c->wbuf,0xff,c->wbuf_pagesize); ++ } ++ ++ /* Sanity checks on target address. ++ It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), ++ and it's permitted to write at the beginning of a new ++ erase block. Anything else, and you die. ++ New block starts at xxx000c (0-b = block header) ++ */ ++ if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) { ++ /* It's a write to a new block */ ++ if (c->wbuf_len) { ++ D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); ++ ret = jffs2_flush_wbuf_pad(c); ++ if (ret) { ++ /* the underlying layer has to check wbuf_len to do the cleanup */ ++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); ++ *retlen = 0; ++ return ret; ++ } ++ } ++ /* set pointer to new block */ ++ c->wbuf_ofs = PAGE_DIV(to); ++ c->wbuf_len = PAGE_MOD(to); ++ } ++ ++ if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { ++ /* We're not writing immediately after the writebuffer. Bad. */ ++ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); ++ if (c->wbuf_len) ++ printk(KERN_CRIT "wbuf was previously %08x-%08x\n", ++ c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); ++ BUG(); ++ } ++ ++ /* Note outvecs[3] above. We know count is never greater than 2 */ ++ if (count > 2) { ++ printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); ++ BUG(); ++ } ++ ++ invec = 0; ++ outvec = 0; ++ ++ ++ /* Fill writebuffer first, if already in use */ ++ if (c->wbuf_len) { ++ uint32_t invec_ofs = 0; ++ ++ /* adjust alignment offset */ ++ if (c->wbuf_len != PAGE_MOD(to)) { ++ c->wbuf_len = PAGE_MOD(to); ++ /* take care of alignment to next page */ ++ if (!c->wbuf_len) ++ c->wbuf_len = c->wbuf_pagesize; ++ } ++ ++ while(c->wbuf_len < c->wbuf_pagesize) { ++ uint32_t thislen; ++ ++ if (invec == count) ++ goto alldone; ++ ++ thislen = c->wbuf_pagesize - c->wbuf_len; ++ ++ if (thislen >= invecs[invec].iov_len) ++ thislen = invecs[invec].iov_len; ++ ++ invec_ofs = thislen; ++ ++ memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); ++ c->wbuf_len += thislen; ++ donelen += thislen; ++ /* Get next invec, if actual did not fill the buffer */ ++ if (c->wbuf_len < c->wbuf_pagesize) ++ invec++; ++ } ++ ++ /* write buffer is full, flush buffer */ ++ ret = __jffs2_flush_wbuf(c, 0); ++ if (ret) { ++ /* the underlying layer has to check wbuf_len to do the cleanup */ ++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); ++ /* Retlen zero to make sure our caller doesn't mark the space dirty. ++ We've already done everything that's necessary */ ++ *retlen = 0; ++ return ret; ++ } ++ outvec_to += donelen; ++ c->wbuf_ofs = outvec_to; ++ ++ /* All invecs done ? */ ++ if (invec == count) ++ goto alldone; ++ ++ /* Set up the first outvec, containing the remainder of the ++ invec we partially used */ ++ if (invecs[invec].iov_len > invec_ofs) { ++ outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; ++ totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; ++ if (totlen > c->wbuf_pagesize) { ++ splitvec = outvec; ++ split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); ++ } ++ outvec++; ++ } ++ invec++; ++ } ++ ++ /* OK, now we've flushed the wbuf and the start of the bits ++ we have been asked to write, now to write the rest.... */ ++ ++ /* totlen holds the amount of data still to be written */ ++ old_totlen = totlen; ++ for ( ; invec < count; invec++,outvec++ ) { ++ outvecs[outvec].iov_base = invecs[invec].iov_base; ++ totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; ++ if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { ++ splitvec = outvec; ++ split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); ++ old_totlen = totlen; ++ } ++ } ++ ++ /* Now the outvecs array holds all the remaining data to write */ ++ /* Up to splitvec,split_ofs is to be written immediately. The rest ++ goes into the (now-empty) wbuf */ ++ ++ if (splitvec != -1) { ++ uint32_t remainder; ++ int ret; ++ ++ remainder = outvecs[splitvec].iov_len - split_ofs; ++ outvecs[splitvec].iov_len = split_ofs; ++ ++ /* We did cross a page boundary, so we write some now */ ++ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); ++ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { ++ /* At this point we have no problem, ++ c->wbuf is empty. ++ */ ++ *retlen = donelen; ++ return ret; ++ } ++ ++ donelen += wbuf_retlen; ++ c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); ++ ++ if (remainder) { ++ outvecs[splitvec].iov_base += split_ofs; ++ outvecs[splitvec].iov_len = remainder; ++ } else { ++ splitvec++; ++ } ++ ++ } else { ++ splitvec = 0; ++ } ++ ++ /* Now splitvec points to the start of the bits we have to copy ++ into the wbuf */ ++ wbuf_ptr = c->wbuf; ++ ++ for ( ; splitvec < outvec; splitvec++) { ++ /* Don't copy the wbuf into itself */ ++ if (outvecs[splitvec].iov_base == c->wbuf) ++ continue; ++ memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); ++ wbuf_ptr += outvecs[splitvec].iov_len; ++ donelen += outvecs[splitvec].iov_len; ++ } ++ c->wbuf_len = wbuf_ptr - c->wbuf; ++ ++ /* If there's a remainder in the wbuf and it's a non-GC write, ++ remember that the wbuf affects this ino */ ++alldone: ++ *retlen = donelen; ++ ++ if (c->wbuf_len && ino) ++ jffs2_wbuf_dirties_inode(c, ino); ++ ++ return 0; ++} ++ ++/* ++ * This is the entry for flash write. ++ * Check, if we work on NAND FLASH, if so build an iovec and write it via vritev ++*/ ++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) ++{ ++ struct iovec vecs[1]; ++ ++ if (jffs2_can_mark_obsolete(c)) ++ return c->mtd->write(c->mtd, ofs, len, retlen, buf); ++ ++ vecs[0].iov_base = (unsigned char *) buf; ++ vecs[0].iov_len = len; ++ return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); ++} ++ ++/* ++ Handle readback from writebuffer and ECC failure return ++*/ ++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) ++{ ++ loff_t orbf = 0, owbf = 0, lwbf = 0; ++ int ret; ++ ++ /* Read flash */ ++ if (!jffs2_can_mark_obsolete(c)) { ++ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); ++ ++ if ( (ret == -EIO) && (*retlen == len) ) { ++ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", ++ len, ofs); ++ /* ++ * We have the raw data without ECC correction in the buffer, maybe ++ * we are lucky and all data or parts are correct. We check the node. ++ * If data are corrupted node check will sort it out. ++ * We keep this block, it will fail on write or erase and the we ++ * mark it bad. Or should we do that now? But we should give him a chance. ++ * Maybe we had a system crash or power loss before the ecc write or ++ * a erase was completed. ++ * So we return success. :) ++ */ ++ ret = 0; ++ } ++ } else ++ return c->mtd->read(c->mtd, ofs, len, retlen, buf); ++ ++ /* if no writebuffer available or write buffer empty, return */ ++ if (!c->wbuf_pagesize || !c->wbuf_len) ++ return ret; ++ ++ /* if we read in a different block, return */ ++ if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) ++ return ret; ++ ++ if (ofs >= c->wbuf_ofs) { ++ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ ++ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ ++ return ret; ++ lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ ++ if (lwbf > len) ++ lwbf = len; ++ } else { ++ orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ ++ if (orbf > len) /* is write beyond write buffer ? */ ++ return ret; ++ lwbf = len - orbf; /* number of bytes to copy */ ++ if (lwbf > c->wbuf_len) ++ lwbf = c->wbuf_len; ++ } ++ if (lwbf > 0) ++ memcpy(buf+orbf,c->wbuf+owbf,lwbf); ++ ++ return ret; ++} ++ ++/* ++ * Check, if the out of band area is empty ++ */ ++int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) ++{ ++ unsigned char *buf; ++ int ret = 0; ++ int i,len,page; ++ size_t retlen; ++ int oob_size; ++ ++ oob_size = c->mtd->oobsize; ++ ++ /* allocate a buffer for all oob data in this sector */ ++ len = 4 * oob_size; ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); ++ return -ENOMEM; ++ } ++ /* ++ * if mode = 0, we scan for a total empty oob area, else we have ++ * to take care of the cleanmarker in the first page of the block ++ */ ++ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ goto out; ++ } ++ ++ if (retlen < len) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " ++ "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* Special check for first two pages */ ++ for (page = 0; page < 2 * oob_size; page += oob_size) { ++ /* Check for bad block marker */ ++ if (buf[page+c->badblock_pos] != 0xff) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset)); ++ /* Return 2 for bad and 3 for failed block ++ bad goes to list_bad and failed to list_erase */ ++ ret = (!page) ? 2 : 3; ++ goto out; ++ } ++ for(i = 0; i < oob_size ; i++) { ++ /* Yeah, we know about the cleanmarker. */ ++ if (mode && i >= c->fsdata_pos && ++ i < c->fsdata_pos+c->fsdata_len) ++ continue; ++ ++ if (buf[page+i] != 0xFF) { ++ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", ++ buf[page+i], page+i, jeb->offset)); ++ ret = 1; ++ goto out; ++ } ++ } ++ /* only the first page can contain a cleanmarker !*/ ++ mode = 0; ++ } ++ ++ /* we know, we are aligned :) */ ++ for (; page < len; page += sizeof(long)) { ++ unsigned long dat = *(unsigned long *)(&buf[page]); ++ if(dat != -1) { ++ ret = 1; ++ goto out; ++ } ++ } ++ ++out: ++ kfree(buf); ++ ++ return ret; ++} ++ ++/* ++* Scan for a valid cleanmarker and for bad blocks ++* For virtual blocks (concatenated physical blocks) check the cleanmarker ++* only in the first page of the first physical block, but scan for bad blocks in all ++* physical blocks ++*/ ++int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_unknown_node n; ++ unsigned char buf[32]; ++ unsigned char *p; ++ int ret, i, cnt, retval = 0; ++ size_t retlen, offset; ++ int oob_size; ++ ++ offset = jeb->offset; ++ oob_size = c->mtd->oobsize; ++ ++ /* Loop through the physical blocks */ ++ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { ++ /* ++ * We read oob data from page 0 and 1 of the block. ++ * page 0 contains cleanmarker and badblock info ++ * page 1 contains failure count of this block ++ */ ++ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); ++ ++ if (ret) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ return ret; ++ } ++ if (retlen < (oob_size << 1)) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); ++ return -EIO; ++ } ++ ++ /* Check for bad block marker */ ++ if (buf[c->badblock_pos] != 0xff) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n", ++ jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos)); ++ return 2; ++ } ++ ++ /* Check for failure counter in the second page */ ++ if (buf[c->badblock_pos + oob_size] != 0xff) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size])); ++ return 3; ++ } ++ ++ /* Check cleanmarker only on the first physical block */ ++ if (!cnt) { ++ n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); ++ n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); ++ n.totlen = cpu_to_je32 (8); ++ p = (unsigned char *) &n; ++ ++ for (i = 0; i < c->fsdata_len; i++) { ++ if (buf[c->fsdata_pos + i] != p[i]) { ++ retval = 1; ++ } ++ } ++ D1(if (retval == 1) { ++ printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); ++ printk(KERN_WARNING "OOB at %08x was ", offset); ++ for (i=0; i < oob_size; i++) { ++ printk("%02x ", buf[i]); ++ } ++ printk("\n"); ++ }) ++ } ++ offset += c->mtd->erasesize; ++ } ++ return retval; ++} ++ ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_unknown_node n; ++ int ret; ++ size_t retlen; ++ ++ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ++ n.totlen = cpu_to_je32(8); ++ ++ ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); ++ return ret; ++ } ++ if (retlen != c->fsdata_len) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); ++ return ret; ++ } ++ return 0; ++} ++ ++/* ++ * We try to get the failure count of this block. ++ */ ++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { ++ ++ unsigned char buf[16]; ++ int ret; ++ size_t retlen; ++ int oob_size; ++ ++ oob_size = c->mtd->oobsize; ++ ++ ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ return ret; ++ } ++ ++ if (retlen < oob_size) { ++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); ++ return -EIO; ++ } ++ ++ jeb->bad_count = buf[c->badblock_pos]; ++ return 0; ++} ++ ++/* ++ * On NAND we try to mark this block bad. We try to write how often ++ * the block was erased and mark it finaly bad, if the count ++ * is > MAX_ERASE_FAILURES. We read this information on mount ! ++ * jeb->bad_count contains the count before this erase. ++ * Don't care about failures. This block remains on the erase-pending ++ * or badblock list as long as nobody manipulates the flash with ++ * a bootloader or something like that. ++ */ ++ ++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ unsigned char buf = 0x0; ++ int ret; ++ size_t retlen; ++ ++ /* if the count is < max, we try to write the counter to the 2nd page oob area */ ++ if( ++jeb->bad_count < MAX_ERASE_FAILURES) { ++ buf = (unsigned char)jeb->bad_count; ++ c->badblock_pos += c->mtd->oobblock; ++ } ++ ++ ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); ++ return ret; ++ } ++ if (retlen != 1) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen)); ++ return ret; ++ } ++ return 0; ++} ++ ++#define JFFS2_OOB_ECCPOS0 0 ++#define JFFS2_OOB_ECCPOS1 1 ++#define JFFS2_OOB_ECCPOS2 2 ++#define JFFS2_OOB_ECCPOS3 3 ++#define JFFS2_OOB_ECCPOS4 6 ++#define JFFS2_OOB_ECCPOS5 7 ++ ++#define NAND_JFFS2_OOB8_FSDAPOS 6 ++#define NAND_JFFS2_OOB16_FSDAPOS 8 ++#define NAND_JFFS2_OOB8_FSDALEN 2 ++#define NAND_JFFS2_OOB16_FSDALEN 8 ++ ++static struct nand_oobinfo jffs2_oobinfo_swecc = { ++ .useecc = 1, ++ .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, ++ JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} ++}; ++ ++static struct nand_oobinfo jffs2_oobinfo_docecc = { ++ .useecc = 1, ++ .eccpos = {0,1,2,3,4,5} ++}; ++ ++ ++ ++int jffs2_nand_flash_setup(struct jffs2_sb_info *c) ++{ ++ /* Cleanmarker is out-of-band, so inline size zero */ ++ c->cleanmarker_size = 0; ++ ++ /* Initialise write buffer */ ++ c->wbuf_pagesize = c->mtd->oobblock; ++ c->wbuf_ofs = 0xFFFFFFFF; ++ ++ /* FIXME: If we had a generic way of describing the hardware's ++ use of OOB area, we could perhaps make this generic too. */ ++ switch(c->mtd->ecctype) { ++ case MTD_ECC_SW: ++ D1(printk(KERN_DEBUG "JFFS2 using software ECC\n")); ++ c->oobinfo = &jffs2_oobinfo_swecc; ++ if (c->mtd->oobsize == 8) { ++ c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS; ++ c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN; ++ } else { ++ c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS; ++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; ++ } ++ c->badblock_pos = NAND_BADBLOCK_POS; ++ break; ++ ++ case MTD_ECC_RS_DiskOnChip: ++ D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n")); ++ c->oobinfo = &jffs2_oobinfo_docecc; ++ c->fsdata_pos = 6; ++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; ++ c->badblock_pos = 15; ++ break; ++ ++ default: ++ printk("JFFS2 doesn't yet know how to handle ECC type %d\n", ++ c->mtd->ecctype); ++ return -EINVAL; ++ } ++ ++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); ++ if (!c->wbuf) ++ return -ENOMEM; ++ ++#ifdef BREAKME ++ if (!brokenbuf) ++ brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); ++ if (!brokenbuf) { ++ kfree(c->wbuf); ++ return -ENOMEM; ++ } ++ memset(brokenbuf, 0xdb, c->wbuf_pagesize); ++#endif ++ return 0; ++} ++ ++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) ++{ ++ kfree(c->wbuf); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/write.c linux/fs/jffs2/write.c +--- linux-mips-2.4.27/fs/jffs2/write.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/write.c 2004-11-19 10:25:12.132164376 +0100 +@@ -1,154 +1,70 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: write.c,v 1.30.2.2 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: write.c,v 1.80 2004/01/27 13:21:50 dwmw2 Exp $ + * + */ + + #include + #include +-#include ++#include ++#include ++#include + #include + #include "nodelist.h" +-#include + +-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, +- fill in the raw_inode while you're at it. */ +-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) ++ ++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) + { +- struct inode *inode; +- struct super_block *sb = dir_i->i_sb; + struct jffs2_inode_cache *ic; +- struct jffs2_sb_info *c; +- struct jffs2_inode_info *f; +- +- D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); +- +- c = JFFS2_SB_INFO(sb); +- memset(ri, 0, sizeof(*ri)); + + ic = jffs2_alloc_inode_cache(); + if (!ic) { +- return ERR_PTR(-ENOMEM); ++ return -ENOMEM; + } +- memset(ic, 0, sizeof(*ic)); +- +- inode = new_inode(sb); + +- if (!inode) { +- jffs2_free_inode_cache(ic); +- return ERR_PTR(-ENOMEM); +- } +- +- /* Alloc jffs2_inode_info when that's split in 2.5 */ ++ memset(ic, 0, sizeof(*ic)); + +- f = JFFS2_INODE_INFO(inode); +- memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); + f->inocache = ic; +- inode->i_nlink = f->inocache->nlink = 1; ++ f->inocache->nlink = 1; + f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; +- f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino; +- D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino)); +- jffs2_add_ino_cache(c, f->inocache); ++ f->inocache->ino = ++c->highest_ino; ++ f->inocache->state = INO_STATE_PRESENT; + +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = PAD(sizeof(*ri)); +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- ri->mode = mode; +- f->highest_version = ri->version = 1; +- ri->uid = current->fsuid; +- if (dir_i->i_mode & S_ISGID) { +- ri->gid = dir_i->i_gid; +- if (S_ISDIR(mode)) +- ri->mode |= S_ISGID; +- } else { +- ri->gid = current->fsgid; +- } +- inode->i_mode = ri->mode; +- inode->i_gid = ri->gid; +- inode->i_uid = ri->uid; +- inode->i_atime = inode->i_ctime = inode->i_mtime = +- ri->atime = ri->mtime = ri->ctime = CURRENT_TIME; +- inode->i_blksize = PAGE_SIZE; +- inode->i_blocks = 0; +- inode->i_size = 0; ++ ri->ino = cpu_to_je32(f->inocache->ino); + +- insert_inode_hash(inode); ++ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ++ jffs2_add_ino_cache(c, f->inocache); + +- return inode; +-} ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ri->mode = cpu_to_jemode(mode); + +-/* This ought to be in core MTD code. All registered MTD devices +- without writev should have this put in place. Bug the MTD +- maintainer */ +-static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) +-{ +- unsigned long i; +- size_t totlen = 0, thislen; +- int ret = 0; ++ f->highest_version = 1; ++ ri->version = cpu_to_je32(f->highest_version); + +- for (i=0; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); +- totlen += thislen; +- if (ret || thislen != vecs[i].iov_len) +- break; +- to += vecs[i].iov_len; +- } +- if (retlen) +- *retlen = totlen; +- return ret; +-} +- +- +-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) +-{ +- if (mtd->writev) +- return mtd->writev(mtd,vecs,count,to,retlen); +- else +- return mtd_fake_writev(mtd, vecs, count, to, retlen); ++ return 0; + } + +-static void writecheck(struct mtd_info *mtd, __u32 ofs) ++#if CONFIG_JFFS2_FS_DEBUG > 0 ++static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) + { + unsigned char buf[16]; +- ssize_t retlen; ++ size_t retlen; + int ret, i; + +- ret = mtd->read(mtd, ofs, 16, &retlen, buf); +- if (ret && retlen != 16) { +- D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen)); ++ ret = jffs2_flash_read(c, ofs, 16, &retlen, buf); ++ if (ret || (retlen != 16)) { ++ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen)); + return; + } + ret = 0; +@@ -157,32 +73,31 @@ + ret = 1; + } + if (ret) { +- printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs); ++ printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); + printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + ofs, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + } +- +- ++#endif + + + /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, + write it to the flash, link it into the existing inode/fragment list */ + +-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen) ++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) + + { +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dnode *fn; +- ssize_t retlen; ++ size_t retlen; + struct iovec vecs[2]; + int ret; ++ int retried = 0; ++ unsigned long cnt = 2; + +- D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { ++ D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { + printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n"); + BUG(); + } +@@ -192,10 +107,10 @@ + vecs[1].iov_base = (unsigned char *)data; + vecs[1].iov_len = datalen; + +- writecheck(c->mtd, flash_ofs); ++ D1(writecheck(c, flash_ofs)); + +- if (ri->totlen != sizeof(*ri) + datalen) { +- printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen); ++ if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { ++ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); + } + raw = jffs2_alloc_raw_node_ref(); + if (!raw) +@@ -206,19 +121,28 @@ + jffs2_free_raw_node_ref(raw); + return ERR_PTR(-ENOMEM); + } +- raw->flash_offset = flash_ofs; +- raw->totlen = PAD(ri->totlen); +- raw->next_phys = NULL; + +- fn->ofs = ri->offset; +- fn->size = ri->dsize; ++ fn->ofs = je32_to_cpu(ri->offset); ++ fn->size = je32_to_cpu(ri->dsize); + fn->frags = 0; ++ ++ /* check number of valid vecs */ ++ if (!datalen || !data) ++ cnt = 1; ++ retry: + fn->raw = raw; + +- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen); ++ raw->flash_offset = flash_ofs; ++ raw->__totlen = PAD(sizeof(*ri)+datalen); ++ raw->next_phys = NULL; ++ ++ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, ++ (alloc_mode==ALLOC_GC)?0:f->inocache->ino); ++ + if (ret || (retlen != sizeof(*ri) + datalen)) { +- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", ++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + sizeof(*ri)+datalen, flash_ofs, ret, retlen); ++ + /* Mark the space as dirtied */ + if (retlen) { + /* Doesn't belong to any inode */ +@@ -229,48 +153,96 @@ + seem corrupted, in which case the scan would skip over + any node we write before the original intended end of + this node */ +- jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1); ++ raw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); + jffs2_free_raw_node_ref(raw); + } ++ if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ if (alloc_mode == ALLOC_GC) { ++ ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); ++ } else { ++ /* Locking pain */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode); ++ down(&f->sem); ++ } ++ ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(raw); ++ } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dnode(fn); +- if (writelen) +- *writelen = retlen; + return ERR_PTR(ret?ret:-EIO); + } + /* Mark the space used */ +- jffs2_add_physical_node_ref(c, raw, retlen, 0); ++ /* If node covers at least a whole page, or if it starts at the ++ beginning of a page and runs to the end of the file, or if ++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. ++ */ ++ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || ++ ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && ++ (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { ++ raw->flash_offset |= REF_PRISTINE; ++ } else { ++ raw->flash_offset |= REF_NORMAL; ++ } ++ jffs2_add_physical_node_ref(c, raw); + + /* Link into per-inode list */ + raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; + +- D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen)); +- if (writelen) +- *writelen = retlen; ++ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", ++ flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), ++ je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), ++ je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); ++ ++ if (retried) { ++ ACCT_SANITY_CHECK(c,NULL); ++ } + +- f->inocache->nodes = raw; + return fn; + } + +-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen) ++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) + { +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; +- ssize_t retlen; ++ size_t retlen; + struct iovec vecs[2]; ++ int retried = 0; + int ret; + +- D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc)); +- writecheck(c->mtd, flash_ofs); ++ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", ++ je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), ++ je32_to_cpu(rd->name_crc))); ++ D1(writecheck(c, flash_ofs)); + +- D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { ++ D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { + printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); + BUG(); + } +@@ -291,44 +263,414 @@ + jffs2_free_raw_node_ref(raw); + return ERR_PTR(-ENOMEM); + } +- raw->flash_offset = flash_ofs; +- raw->totlen = PAD(rd->totlen); +- raw->next_in_ino = f->inocache->nodes; +- f->inocache->nodes = raw; +- raw->next_phys = NULL; + +- fd->version = rd->version; +- fd->ino = rd->ino; ++ fd->version = je32_to_cpu(rd->version); ++ fd->ino = je32_to_cpu(rd->ino); + fd->nhash = full_name_hash(name, strlen(name)); + fd->type = rd->type; + memcpy(fd->name, name, namelen); + fd->name[namelen]=0; ++ ++ retry: + fd->raw = raw; + +- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen); ++ raw->flash_offset = flash_ofs; ++ raw->__totlen = PAD(sizeof(*rd)+namelen); ++ raw->next_phys = NULL; ++ ++ ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, ++ (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); + if (ret || (retlen != sizeof(*rd) + namelen)) { +- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", ++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + sizeof(*rd)+namelen, flash_ofs, ret, retlen); + /* Mark the space as dirtied */ + if (retlen) { +- jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1); ++ raw->next_in_ino = NULL; ++ raw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); + jffs2_free_raw_node_ref(raw); + } ++ if (!retried && (raw = jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ if (alloc_mode == ALLOC_GC) { ++ ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); ++ } else { ++ /* Locking pain */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode); ++ down(&f->sem); ++ } + ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(raw); ++ } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dirent(fd); +- if (writelen) +- *writelen = retlen; + return ERR_PTR(ret?ret:-EIO); + } + /* Mark the space used */ +- jffs2_add_physical_node_ref(c, raw, retlen, 0); +- if (writelen) +- *writelen = retlen; ++ raw->flash_offset |= REF_PRISTINE; ++ jffs2_add_physical_node_ref(c, raw); + ++ raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; ++ ++ if (retried) { ++ ACCT_SANITY_CHECK(c,NULL); ++ } ++ + return fd; + } ++ ++/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that ++ we don't have to go digging in struct inode or its equivalent. It should set: ++ mode, uid, gid, (starting)isize, atime, ctime, mtime */ ++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *ri, unsigned char *buf, ++ uint32_t offset, uint32_t writelen, uint32_t *retlen) ++{ ++ int ret = 0; ++ uint32_t writtenlen = 0; ++ ++ D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n", ++ f->inocache->ino, offset, writelen)); ++ ++ while(writelen) { ++ struct jffs2_full_dnode *fn; ++ unsigned char *comprbuf = NULL; ++ unsigned char comprtype = JFFS2_COMPR_NONE; ++ uint32_t phys_ofs, alloclen; ++ uint32_t datalen, cdatalen; ++ int retried = 0; ++ ++ retry: ++ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); ++ break; ++ } ++ down(&f->sem); ++ datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); ++ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); ++ ++ comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen); ++ ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri->ino = cpu_to_je32(f->inocache->ino); ++ ri->version = cpu_to_je32(++f->highest_version); ++ ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen)); ++ ri->offset = cpu_to_je32(offset); ++ ri->csize = cpu_to_je32(cdatalen); ++ ri->dsize = cpu_to_je32(datalen); ++ ri->compr = comprtype; ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); ++ ++ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); ++ ++ jffs2_free_comprbuf(comprbuf, buf); ++ ++ if (IS_ERR(fn)) { ++ ret = PTR_ERR(fn); ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ if (!retried) { ++ /* Write error to be retried */ ++ retried = 1; ++ D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); ++ goto retry; ++ } ++ break; ++ } ++ ret = jffs2_add_full_dnode_to_inode(c, f, fn); ++ if (f->metadata) { ++ jffs2_mark_node_obsolete(c, f->metadata->raw); ++ jffs2_free_full_dnode(f->metadata); ++ f->metadata = NULL; ++ } ++ if (ret) { ++ /* Eep */ ++ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); ++ jffs2_mark_node_obsolete(c, fn->raw); ++ jffs2_free_full_dnode(fn); ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ break; ++ } ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ if (!datalen) { ++ printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); ++ ret = -EIO; ++ break; ++ } ++ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); ++ writtenlen += datalen; ++ offset += datalen; ++ writelen -= datalen; ++ buf += datalen; ++ } ++ *retlen = writtenlen; ++ return ret; ++} ++ ++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dnode *fn; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ /* Try to reserve enough space for both node and dirent. ++ * Just the node will do for now, though ++ */ ++ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); ++ D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); ++ if (ret) { ++ up(&f->sem); ++ return ret; ++ } ++ ++ ri->data_crc = cpu_to_je32(0); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ ++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); ++ ++ D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", ++ jemode_to_cpu(ri->mode))); ++ ++ if (IS_ERR(fn)) { ++ D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); ++ /* Eeek. Wave bye bye */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ return PTR_ERR(fn); ++ } ++ /* No data here. Only a metadata node, which will be ++ obsoleted by the first data write ++ */ ++ f->metadata = fn; ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ ++ if (ret) { ++ /* Eep. */ ++ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); ++ return ret; ++ } ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) { ++ /* Argh. Now we treat it like a normal delete */ ++ jffs2_complete_reservation(c); ++ return -ENOMEM; ++ } ++ ++ down(&dir_f->sem); ++ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = ri->ino; ++ rd->mctime = ri->ctime; ++ rd->nsize = namelen; ++ rd->type = DT_REG; ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ /* dirent failed to write. Delete the inode normally ++ as if it were the final unlink() */ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* Link the fd into the inode's list, obsoleting an old ++ one if necessary. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ ++ return 0; ++} ++ ++ ++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, ++ const char *name, int namelen, struct jffs2_inode_info *dead_f) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) ++ return -ENOMEM; ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); ++ if (ret) { ++ jffs2_free_raw_dirent(rd); ++ return ret; ++ } ++ ++ down(&dir_f->sem); ++ ++ /* Build a deletion node */ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(0); ++ rd->mctime = cpu_to_je32(get_seconds()); ++ rd->nsize = namelen; ++ rd->type = DT_UNKNOWN; ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* File it. This will mark the old one obsolete. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ up(&dir_f->sem); ++ ++ /* dead_f is NULL if this was a rename not a real unlink */ ++ /* Also catch the !f->inocache case, where there was a dirent ++ pointing to an inode which didn't exist. */ ++ if (dead_f && dead_f->inocache) { ++ ++ down(&dead_f->sem); ++ ++ while (dead_f->dents) { ++ /* There can be only deleted ones */ ++ fd = dead_f->dents; ++ ++ dead_f->dents = fd->next; ++ ++ if (fd->ino) { ++ printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", ++ dead_f->inocache->ino, fd->name, fd->ino); ++ } else { ++ D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); ++ } ++ jffs2_mark_node_obsolete(c, fd->raw); ++ jffs2_free_full_dirent(fd); ++ } ++ ++ dead_f->inocache->nlink--; ++ /* NB: Caller must set inode nlink if appropriate */ ++ up(&dead_f->sem); ++ } ++ ++ jffs2_complete_reservation(c); ++ ++ return 0; ++} ++ ++ ++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) ++ return -ENOMEM; ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ jffs2_free_raw_dirent(rd); ++ return ret; ++ } ++ ++ down(&dir_f->sem); ++ ++ /* Build a deletion node */ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(ino); ++ rd->mctime = cpu_to_je32(get_seconds()); ++ rd->nsize = namelen; ++ ++ rd->type = type; ++ ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* File it. This will mark the old one obsolete. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ ++ return 0; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/writev.c linux/fs/jffs2/writev.c +--- linux-mips-2.4.27/fs/jffs2/writev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/writev.c 2004-11-19 10:25:12.134164072 +0100 +@@ -0,0 +1,50 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $ ++ * ++ */ ++ ++#include ++#include ++#include "nodelist.h" ++ ++/* This ought to be in core MTD code. All registered MTD devices ++ without writev should have this put in place. Bug the MTD ++ maintainer */ ++static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen) ++{ ++ unsigned long i; ++ size_t totlen = 0, thislen; ++ int ret = 0; ++ ++ for (i=0; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); ++ totlen += thislen; ++ if (ret || thislen != vecs[i].iov_len) ++ break; ++ to += vecs[i].iov_len; ++ } ++ if (retlen) ++ *retlen = totlen; ++ return ret; ++} ++ ++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen) ++{ ++ if (c->mtd->writev) ++ return c->mtd->writev(c->mtd, vecs, count, to, retlen); ++ else ++ return mtd_fake_writev(c->mtd, vecs, count, to, retlen); ++} ++ +diff -Nurb linux-mips-2.4.27/include/linux/jffs2.h linux/include/linux/jffs2.h +--- linux-mips-2.4.27/include/linux/jffs2.h 2002-06-27 00:36:46.000000000 +0200 ++++ linux/include/linux/jffs2.h 2004-11-19 10:25:12.139163312 +0100 +@@ -1,50 +1,30 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse ++ * Created by David Woodhouse + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in the ++ * jffs2 directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $ ++ * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $ + * + */ + + #ifndef __LINUX_JFFS2_H__ + #define __LINUX_JFFS2_H__ + +-#include ++/* You must include something which defines the C99 uintXX_t types. ++ We don't do it from here because this file is used in too many ++ different environments. */ ++ + #define JFFS2_SUPER_MAGIC 0x72b6 + + /* Values we may expect to find in the 'magic' field */ + #define JFFS2_OLD_MAGIC_BITMASK 0x1984 + #define JFFS2_MAGIC_BITMASK 0x1985 +-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */ ++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ + #define JFFS2_EMPTY_BITMASK 0xffff + #define JFFS2_DIRTY_BITMASK 0x0000 + +@@ -78,16 +58,12 @@ + #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) + #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) + #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) ++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) + + // Maybe later... + //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) + //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) + +-/* Same as the non_ECC versions, but with extra space for real +- * ECC instead of just the checksum. For use on NAND flash +- */ +-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5) +-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6) + + #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at + mount time, don't wait for it to +@@ -96,31 +72,79 @@ + compression type */ + + ++/* These can go once we've made sure we've caught all uses without ++ byteswapping */ ++ ++typedef struct { ++ uint32_t v32; ++} __attribute__((packed)) jint32_t; ++ ++typedef struct { ++ uint32_t m; ++} __attribute__((packed)) jmode_t; ++ ++typedef struct { ++ uint16_t v16; ++} __attribute__((packed)) jint16_t; ++ ++#define JFFS2_NATIVE_ENDIAN ++ ++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from ++ whatever OS we're actually running on here too. */ ++ ++#if defined(JFFS2_NATIVE_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){x}) ++#define cpu_to_je32(x) ((jint32_t){x}) ++#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) ++ ++#define je16_to_cpu(x) ((x).v16) ++#define je32_to_cpu(x) ((x).v32) ++#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) ++#elif defined(JFFS2_BIG_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) ++#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) ++#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) ++ ++#define je16_to_cpu(x) (be16_to_cpu(x.v16)) ++#define je32_to_cpu(x) (be32_to_cpu(x.v32)) ++#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) ++#elif defined(JFFS2_LITTLE_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) ++#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) ++#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) ++ ++#define je16_to_cpu(x) (le16_to_cpu(x.v16)) ++#define je32_to_cpu(x) (le32_to_cpu(x.v32)) ++#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) ++#else ++#error wibble ++#endif ++ + struct jffs2_unknown_node + { + /* All start like this */ +- __u16 magic; +- __u16 nodetype; +- __u32 totlen; /* So we can skip over nodes we don't grok */ +- __u32 hdr_crc; ++ jint16_t magic; ++ jint16_t nodetype; ++ jint32_t totlen; /* So we can skip over nodes we don't grok */ ++ jint32_t hdr_crc; + } __attribute__((packed)); + + struct jffs2_raw_dirent + { +- __u16 magic; +- __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */ +- __u32 totlen; +- __u32 hdr_crc; +- __u32 pino; +- __u32 version; +- __u32 ino; /* == zero for unlink */ +- __u32 mctime; +- __u8 nsize; +- __u8 type; +- __u8 unused[2]; +- __u32 node_crc; +- __u32 name_crc; +- __u8 name[0]; ++ jint16_t magic; ++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t pino; ++ jint32_t version; ++ jint32_t ino; /* == zero for unlink */ ++ jint32_t mctime; ++ uint8_t nsize; ++ uint8_t type; ++ uint8_t unused[2]; ++ jint32_t node_crc; ++ jint32_t name_crc; ++ uint8_t name[0]; + } __attribute__((packed)); + + /* The JFFS2 raw inode structure: Used for storage on physical media. */ +@@ -131,28 +155,28 @@ + */ + struct jffs2_raw_inode + { +- __u16 magic; /* A constant magic number. */ +- __u16 nodetype; /* == JFFS_NODETYPE_INODE */ +- __u32 totlen; /* Total length of this node (inc data, etc.) */ +- __u32 hdr_crc; +- __u32 ino; /* Inode number. */ +- __u32 version; /* Version number. */ +- __u32 mode; /* The file's type or mode. */ +- __u16 uid; /* The file's owner. */ +- __u16 gid; /* The file's group. */ +- __u32 isize; /* Total resultant size of this inode (used for truncations) */ +- __u32 atime; /* Last access time. */ +- __u32 mtime; /* Last modification time. */ +- __u32 ctime; /* Change time. */ +- __u32 offset; /* Where to begin to write. */ +- __u32 csize; /* (Compressed) data size */ +- __u32 dsize; /* Size of the node's data. (after decompression) */ +- __u8 compr; /* Compression algorithm used */ +- __u8 usercompr; /* Compression algorithm requested by the user */ +- __u16 flags; /* See JFFS2_INO_FLAG_* */ +- __u32 data_crc; /* CRC for the (compressed) data. */ +- __u32 node_crc; /* CRC for the raw inode (excluding data) */ +-// __u8 data[dsize]; ++ jint16_t magic; /* A constant magic number. */ ++ jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ ++ jint32_t totlen; /* Total length of this node (inc data, etc.) */ ++ jint32_t hdr_crc; ++ jint32_t ino; /* Inode number. */ ++ jint32_t version; /* Version number. */ ++ jmode_t mode; /* The file's type or mode. */ ++ jint16_t uid; /* The file's owner. */ ++ jint16_t gid; /* The file's group. */ ++ jint32_t isize; /* Total resultant size of this inode (used for truncations) */ ++ jint32_t atime; /* Last access time. */ ++ jint32_t mtime; /* Last modification time. */ ++ jint32_t ctime; /* Change time. */ ++ jint32_t offset; /* Where to begin to write. */ ++ jint32_t csize; /* (Compressed) data size */ ++ jint32_t dsize; /* Size of the node's data. (after decompression) */ ++ uint8_t compr; /* Compression algorithm used */ ++ uint8_t usercompr; /* Compression algorithm requested by the user */ ++ jint16_t flags; /* See JFFS2_INO_FLAG_* */ ++ jint32_t data_crc; /* CRC for the (compressed) data. */ ++ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ ++ uint8_t data[0]; + } __attribute__((packed)); + + union jffs2_node_union { +diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_i.h linux/include/linux/jffs2_fs_i.h +--- linux-mips-2.4.27/include/linux/jffs2_fs_i.h 2001-10-19 03:25:03.000000000 +0200 ++++ linux/include/linux/jffs2_fs_i.h 2004-11-19 10:25:12.141163008 +0100 +@@ -1,22 +1,12 @@ +-/* $Id: jffs2_fs_i.h,v 1.8 2001/04/18 13:05:28 dwmw2 Exp $ */ ++/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ + + #ifndef _JFFS2_FS_I + #define _JFFS2_FS_I + +-/* Include the pipe_inode_info at the beginning so that we can still +- use the storage space in the inode when we have a pipe inode. +- This sucks. +-*/ +- +-#undef THISSUCKS /* Only for 2.2 */ +-#ifdef THISSUCKS +-#include +-#endif ++#include ++#include + + struct jffs2_inode_info { +-#ifdef THISSUCKS +- struct pipe_inode_info pipecrap; +-#endif + /* We need an internal semaphore similar to inode->i_sem. + Unfortunately, we can't used the existing one, because + either the GC would deadlock, or we'd have to release it +@@ -26,10 +16,10 @@ + struct semaphore sem; + + /* The highest (datanode) version number used for this ino */ +- __u32 highest_version; ++ uint32_t highest_version; + + /* List of data fragments which make up the file */ +- struct jffs2_node_frag *fraglist; ++ struct rb_root fragtree; + + /* There may be one datanode which isn't referenced by any of the + above fragments, if it contains a metadata update but no actual +@@ -44,19 +34,13 @@ + /* Some stuff we just have to keep in-core at all times, for each inode. */ + struct jffs2_inode_cache *inocache; + +- /* Keep a pointer to the last physical node in the list. We don't +- use the doubly-linked lists because we don't want to increase +- the memory usage that much. This is simpler */ +- // struct jffs2_raw_node_ref *lastnode; +- __u16 flags; +- __u8 usercompr; +-}; +- +-#ifdef JFFS2_OUT_OF_KERNEL +-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u) +-#else +-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i) ++ uint16_t flags; ++ uint8_t usercompr; ++#if !defined (__ECOS) ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++ struct inode vfs_inode; + #endif ++#endif ++}; + + #endif /* _JFFS2_FS_I */ +- +diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_sb.h linux/include/linux/jffs2_fs_sb.h +--- linux-mips-2.4.27/include/linux/jffs2_fs_sb.h 2002-06-27 00:36:46.000000000 +0200 ++++ linux/include/linux/jffs2_fs_sb.h 2004-11-19 10:25:12.142162856 +0100 +@@ -1,19 +1,22 @@ +-/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */ ++/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ + + #ifndef _JFFS2_FS_SB + #define _JFFS2_FS_SB + + #include + #include ++#include + #include + #include ++#include ++#include + #include + +-#define INOCACHE_HASHSIZE 1 +- + #define JFFS2_SB_FLAG_RO 1 + #define JFFS2_SB_FLAG_MOUNTING 2 + ++struct jffs2_inodirty; ++ + /* A struct for the overall file system control. Pointers to + jffs2_sb_info structs are named `c' in the source code. + Nee jffs_control +@@ -21,36 +24,46 @@ + struct jffs2_sb_info { + struct mtd_info *mtd; + +- __u32 highest_ino; ++ uint32_t highest_ino; ++ uint32_t checked_ino; ++ + unsigned int flags; +- spinlock_t nodelist_lock; + +- // pid_t thread_pid; /* GC thread's PID */ + struct task_struct *gc_task; /* GC task struct */ + struct semaphore gc_thread_start; /* GC thread start mutex */ + struct completion gc_thread_exit; /* GC thread exit completion port */ +- // __u32 gc_minfree_threshold; /* GC trigger thresholds */ +- // __u32 gc_maxdirty_threshold; + + struct semaphore alloc_sem; /* Used to protect all the following + fields, and also to protect against + out-of-order writing of nodes. + And GC. + */ +- __u32 flash_size; +- __u32 used_size; +- __u32 dirty_size; +- __u32 free_size; +- __u32 erasing_size; +- __u32 bad_size; +- __u32 sector_size; +- // __u32 min_free_size; +- // __u32 max_chunk_size; ++ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER ++ (i.e. zero for OOB CLEANMARKER */ ++ ++ uint32_t flash_size; ++ uint32_t used_size; ++ uint32_t dirty_size; ++ uint32_t wasted_size; ++ uint32_t free_size; ++ uint32_t erasing_size; ++ uint32_t bad_size; ++ uint32_t sector_size; ++ uint32_t unchecked_size; ++ ++ uint32_t nr_free_blocks; ++ uint32_t nr_erasing_blocks; ++ ++ /* Number of free blocks there must be before we... */ ++ uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ ++ uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ ++ uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ ++ uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ ++ uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + +- __u32 nr_free_blocks; +- __u32 nr_erasing_blocks; ++ uint32_t nospc_dirty_size; + +- __u32 nr_blocks; ++ uint32_t nr_blocks; + struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks + * from the offset (blocks[ofs / sector_size]) */ + struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ +@@ -58,9 +71,12 @@ + struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ + + struct list_head clean_list; /* Blocks 100% full of clean data */ ++ struct list_head very_dirty_list; /* Blocks with lots of dirty space */ + struct list_head dirty_list; /* Blocks with some dirty space */ ++ struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ ++ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ + struct list_head erasing_list; /* Blocks which are currently erasing */ +- struct list_head erase_pending_list; /* Blocks which need erasing */ ++ struct list_head erase_pending_list; /* Blocks which need erasing now */ + struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ + struct list_head free_list; /* Blocks which are free and ready to be used */ + struct list_head bad_list; /* Bad blocks. */ +@@ -69,16 +85,33 @@ + spinlock_t erase_completion_lock; /* Protect free_list and erasing_list + against erase completion handler */ + wait_queue_head_t erase_wait; /* For waiting for erases to complete */ +- struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE]; ++ ++ wait_queue_head_t inocache_wq; ++ struct jffs2_inode_cache **inocache_list; + spinlock_t inocache_lock; +-}; + +-#ifdef JFFS2_OUT_OF_KERNEL +-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u) +-#else +-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb) ++ /* Sem to allow jffs2_garbage_collect_deletion_dirent to ++ drop the erase_completion_lock while it's holding a pointer ++ to an obsoleted node. I don't like this. Alternatives welcomed. */ ++ struct semaphore erase_free_sem; ++ ++#ifdef CONFIG_JFFS2_FS_NAND ++ /* Write-behind buffer for NAND flash */ ++ unsigned char *wbuf; ++ uint32_t wbuf_ofs; ++ uint32_t wbuf_len; ++ uint32_t wbuf_pagesize; ++ struct jffs2_inodirty *wbuf_inodes; ++ ++ /* Information about out-of-band area usage... */ ++ struct nand_oobinfo *oobinfo; ++ uint32_t badblock_pos; ++ uint32_t fsdata_pos; ++ uint32_t fsdata_len; + #endif + +-#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++ /* OS-private pointer for getting back to master superblock info */ ++ void *os_priv; ++}; + + #endif /* _JFFS2_FB_SB */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/blktrans.h linux/include/linux/mtd/blktrans.h +--- linux-mips-2.4.27/include/linux/mtd/blktrans.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/blktrans.h 2004-11-19 10:25:12.037178816 +0100 +@@ -0,0 +1,72 @@ ++/* ++ * $Id: blktrans.h,v 1.5 2003/06/23 12:00:08 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#ifndef __MTD_TRANS_H__ ++#define __MTD_TRANS_H__ ++ ++#include ++ ++struct hd_geometry; ++struct mtd_info; ++struct mtd_blktrans_ops; ++struct file; ++struct inode; ++ ++struct mtd_blktrans_dev { ++ struct mtd_blktrans_ops *tr; ++ struct list_head list; ++ struct mtd_info *mtd; ++ struct semaphore sem; ++ int devnum; ++ int blksize; ++ unsigned long size; ++ int readonly; ++ void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ ++}; ++ ++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ ++ ++struct mtd_blktrans_ops { ++ char *name; ++ int major; ++ int part_bits; ++ ++ /* Access functions */ ++ int (*readsect)(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ int (*writesect)(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ ++ /* Block layer ioctls */ ++ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo); ++ int (*flush)(struct mtd_blktrans_dev *dev); ++ ++ /* Called with mtd_table_mutex held; no race with add/remove */ ++ int (*open)(struct mtd_blktrans_dev *dev); ++ int (*release)(struct mtd_blktrans_dev *dev); ++ ++ /* Called on {de,}registration and on subsequent addition/removal ++ of devices, with mtd_table_mutex held. */ ++ void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd); ++ void (*remove_dev)(struct mtd_blktrans_dev *dev); ++ ++ struct list_head devs; ++ struct list_head list; ++ struct module *owner; ++ ++ struct mtd_blkcore_priv *blkcore_priv; ++}; ++ ++extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr); ++extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr); ++extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); ++extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); ++ ++ ++#endif /* __MTD_TRANS_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h +--- linux-mips-2.4.27/include/linux/mtd/cfi.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/cfi.h 2004-11-19 10:25:12.038178664 +0100 +@@ -1,13 +1,14 @@ + + /* Common Flash Interface structures + * See http://support.intel.com/design/flash/technote/index.htm +- * $Id: cfi.h,v 1.32 2002/09/05 05:15:32 acurtis Exp $ ++ * $Id: cfi.h,v 1.38 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #ifndef __MTD_CFI_H__ + #define __MTD_CFI_H__ + + #include ++#include + #include + #include + #include +@@ -260,7 +261,8 @@ + __u8 pri[3]; + __u8 MajorVersion; + __u8 MinorVersion; +- __u32 FeatureSupport; ++ __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature ++ block follows - FIXME - not currently supported */ + __u8 SuspendCmdSupport; + __u16 BlkStatusRegMask; + __u8 VccOptimal; +@@ -271,6 +273,25 @@ + __u8 UserProtRegSize; + } __attribute__((packed)); + ++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */ ++ ++struct cfi_pri_amdstd { ++ __u8 pri[3]; ++ __u8 MajorVersion; ++ __u8 MinorVersion; ++ __u8 SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ ++ __u8 EraseSuspend; ++ __u8 BlkProt; ++ __u8 TmpBlkUnprotect; ++ __u8 BlkProtUnprot; ++ __u8 SimultaneousOps; ++ __u8 BurstMode; ++ __u8 PageMode; ++ __u8 VppMin; ++ __u8 VppMax; ++ __u8 TopBottom; ++} __attribute__((packed)); ++ + struct cfi_pri_query { + __u8 NumFields; + __u32 ProtField[1]; /* Not host ordered */ +@@ -314,8 +335,6 @@ + struct flchip chips[0]; /* per-chip data structure for each chip */ + }; + +-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */ +- + /* + * Returns the command address according to the given geometry. + */ +@@ -387,13 +406,13 @@ + static inline cfi_word cfi_read(struct map_info *map, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (cfi_buswidth_is_2()) { +- return map->read16(map, addr); ++ return map_read16(map, addr); + } else if (cfi_buswidth_is_4()) { +- return map->read32(map, addr); ++ return map_read32(map, addr); + } else if (cfi_buswidth_is_8()) { +- return map->read64(map, addr); ++ return map_read64(map, addr); + } else { + return 0; + } +@@ -406,13 +425,13 @@ + static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- map->write8(map, val, addr); ++ map_write8(map, val, addr); + } else if (cfi_buswidth_is_2()) { +- map->write16(map, val, addr); ++ map_write16(map, val, addr); + } else if (cfi_buswidth_is_4()) { +- map->write32(map, val, addr); ++ map_write32(map, val, addr); + } else if (cfi_buswidth_is_8()) { +- map->write64(map, val, addr); ++ map_write64(map, val, addr); + } + } + +@@ -443,13 +462,13 @@ + static inline __u8 cfi_read_query(struct map_info *map, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (cfi_buswidth_is_2()) { +- return cfi16_to_cpu(map->read16(map, addr)); ++ return cfi16_to_cpu(map_read16(map, addr)); + } else if (cfi_buswidth_is_4()) { +- return cfi32_to_cpu(map->read32(map, addr)); ++ return cfi32_to_cpu(map_read32(map, addr)); + } else if (cfi_buswidth_is_8()) { +- return cfi64_to_cpu(map->read64(map, addr)); ++ return cfi64_to_cpu(map_read64(map, addr)); + } else { + return 0; + } +@@ -479,5 +498,19 @@ + spin_unlock_bh(mutex); + } + ++struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, ++ const char* name); ++ ++struct cfi_fixup { ++ __u16 mfr; ++ __u16 id; ++ void (*fixup)(struct map_info *map, void* param); ++ void* param; ++}; ++ ++#define CFI_MFR_ANY 0xffff ++#define CFI_ID_ANY 0xffff ++ ++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups); + + #endif /* __MTD_CFI_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h +--- linux-mips-2.4.27/include/linux/mtd/compatmac.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/compatmac.h 2004-11-19 10:25:12.041178208 +0100 +@@ -1,573 +1,152 @@ +- + /* +- * mtd/include/compatmac.h +- * +- * $Id: compatmac.h,v 1.45 2003/01/24 15:50:57 dwmw2 Exp $ ++ * $Id: compatmac.h,v 1.63 2003/11/14 19:50:04 thayne Exp $ + * + * Extensions and omissions from the normal 'linux/compatmac.h' + * files. hopefully this will end up empty as the 'real' one + * becomes fully-featured. + */ + +- +-/* First, include the parts which the kernel is good enough to provide +- * to us +- */ +- + #ifndef __LINUX_MTD_COMPATMAC_H__ + #define __LINUX_MTD_COMPATMAC_H__ + +-#include +-#include +-#ifndef LINUX_VERSION_CODE + #include +-#endif +- +-#ifndef VERSION_CODE +-# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) +-#endif +-#ifndef KERNEL_VERSION +-# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c) +-#endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) +-# error "This kernel is too old: not supported by this file" +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +-#include /* used later in this header */ +- +-#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +-#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) +- +-typedef struct wait_queue * wait_queue_head_t; +- +-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL} +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue +-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX +-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED +- +-/* from sysdep-2.1.h */ +-# include +-# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1) +-# define verify_area_20 verify_area +-# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0) +-# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n)) +-# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n)) +-# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0) +-# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n)) +-# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n)) +-//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0) +-# define Put_user(val,add) (put_user((val),(add)), 0) +-# define __PUT_USER(val,add) PUT_USER((val),(add)) +-# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add)) +-# define GET_USER(dest,add) ((dest)=get_user((add)), 0) +-# define __GET_USER(dest,add) GET_USER((dest),(add)) +-# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add)) +- +-#define ioremap(offset,size) vremap(offset,size) +-#define iounmap(adr) /* */ +- +-#define EXPORT_SYMBOL(s) /* */ +-#define EXPORT_SYMBOL_NOVERS(s) /* */ +- +-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */ +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10) +- +-# include +-# ifdef __LITTLE_ENDIAN +-# define cpu_to_le16(x) (x) +-# define cpu_to_le32(x) (x) +-# define cpu_to_be16(x) htons((x)) +-# define cpu_to_be32(x) htonl((x)) +-# else +-# define cpu_to_be16(x) (x) +-# define cpu_to_be32(x) (x) +- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);} +- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) | +- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} +-# endif +- +-# define le16_to_cpu(x) cpu_to_le16(x) +-# define le32_to_cpu(x) cpu_to_le32(x) +-# define be16_to_cpu(x) cpu_to_be16(x) +-# define be32_to_cpu(x) cpu_to_be32(x) +- +-#endif +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43) +-# define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) +-# define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) +-# define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) +-# define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) +- +- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);} +- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);} +- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);} +- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);} +- +-# define le16_to_cpup(x) cpu_to_le16p(x) +-# define le32_to_cpup(x) cpu_to_le32p(x) +-# define be16_to_cpup(x) cpu_to_be16p(x) +-# define be32_to_cpup(x) cpu_to_be32p(x) +- +-# define le16_to_cpus(x) cpu_to_le16s(x) +-# define le32_to_cpus(x) cpu_to_le32s(x) +-# define be16_to_cpus(x) cpu_to_be16s(x) +-# define be32_to_cpus(x) cpu_to_be32s(x) +-#endif +- +-// from 2.2, linux/types.h +-#ifndef __BIT_TYPES_DEFINED__ +-#define __BIT_TYPES_DEFINED__ +- +-typedef __u8 u_int8_t; +-typedef __s8 int8_t; +-typedef __u16 u_int16_t; +-typedef __s16 int16_t; +-typedef __u32 u_int32_t; +-typedef __s32 int32_t; +- +-#endif /* !(__BIT_TYPES_DEFINED__) */ +- +-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +- typedef struct { } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { } +-#else +- typedef struct { int gcc_is_buggy; } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +-#endif +- +-#define spin_lock_init(lock) do { } while(0) +-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */ +-#define spin_trylock(lock) (1) +-#define spin_unlock_wait(lock) do { } while(0) +-#define spin_unlock(lock) do { } while(0) +-#define spin_lock_irq(lock) cli() +-#define spin_unlock_irq(lock) sti() +- +-#define spin_lock_irqsave(lock, flags) \ +- do { save_flags(flags); cli(); } while (0) +-#define spin_unlock_irqrestore(lock, flags) \ +- restore_flags(flags) +- +-// Doesn't work when tqueue.h is included. +-// #define queue_task queue_task_irq_off +-#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer) +-#define signal_pending(current) (current->signal & ~current->blocked) +-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +-#define time_after(t1,t2) (((long)t1-t2) > 0) +- +-#else +- #include +-#endif // LINUX_VERSION_CODE < 0x020100 +- +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +-#include +-#endif +- +-/* Modularization issues */ +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18) +-# define __USE_OLD_SYMTAB__ +-# define EXPORT_NO_SYMBOLS register_symtab(NULL); +-# define REGISTER_SYMTAB(tab) register_symtab(tab) +-#else +-# define REGISTER_SYMTAB(tab) /* nothing */ +-#endif +- +-#ifdef __USE_OLD_SYMTAB__ +-# define __MODULE_STRING(s) /* nothing */ +-# define MODULE_PARM(v,t) /* nothing */ +-# define MODULE_PARM_DESC(v,t) /* nothing */ +-# define MODULE_AUTHOR(n) /* nothing */ +-# define MODULE_DESCRIPTION(d) /* nothing */ +-# define MODULE_SUPPORTED_DEVICE(n) /* nothing */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) ++#error "This kernel is too old: not supported by this file" + #endif + +-/* +- * "select" changed in 2.1.23. The implementation is twin, but this +- * header is new +- */ +-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22) +-# include +-#else +-# define __USE_OLD_SELECT__ +-#endif ++ /* O(1) scheduler stuff. */ + +-/* Other change in the fops are solved using pseudo-types */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +-# define lseek_t long long +-# define lseek_off_t long long +-#else +-# define lseek_t int +-# define lseek_off_t off_t +-#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__) ++#include ++static inline void __recalc_sigpending(void) ++{ ++ recalc_sigpending(current); ++} ++#undef recalc_sigpending ++#define recalc_sigpending() __recalc_sigpending () + +-/* changed the prototype of read/write */ ++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) + +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__) +-# define count_t unsigned long +-# define read_write_t long +-#else +-# define count_t int +-# define read_write_t int + #endif + + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31) +-# define release_t void +-# define release_return(x) return +-#else +-# define release_t int +-# define release_return(x) return (x) +-#endif +- +-#if LINUX_VERSION_CODE < 0x20300 +-#define __exit +-#endif +-#if LINUX_VERSION_CODE < 0x20200 +-#define __init +-#else +-#include +-#endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0) +-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0) +-#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q ++#ifndef yield ++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32) +-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0) +-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn) +-#define blk_init_queue(q, rq) do {q = rq;} while(0) ++#ifndef minor ++#define major(d) (MAJOR(to_kdev_t(d))) ++#define minor(d) (MINOR(to_kdev_t(d))) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-#ifdef CONFIG_MODULES +-#define __MOD_INC_USE_COUNT(mod) \ +- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) +-#define __MOD_DEC_USE_COUNT(mod) \ +- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) +-#else +-#define __MOD_INC_USE_COUNT(mod) +-#define __MOD_DEC_USE_COUNT(mod) +-#endif ++#ifndef mk_kdev ++#define mk_kdev(ma,mi) MKDEV(ma,mi) ++#define kdev_t_to_nr(x) (x) + #endif + ++#define need_resched() (current->need_resched) ++#define cond_resched() do { if need_resched() { yield(); } } while(0) + +-#ifndef HAVE_INTER_MODULE +-static inline void *inter_module_get(char *x) {return NULL;} +-static inline void *inter_module_get_request(char *x, char *y) {return NULL;} +-static inline void inter_module_put(const char *x) {} +-static inline void inter_module_register(const char *x, struct module *y, const void *z) {} +-static inline void inter_module_unregister(const char *x) {} ++#endif /* < 2.4.20 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) ++#define iminor(i) minor((i)->i_rdev) ++#define imajor(i) major((i)->i_rdev) ++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) ++#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) ++#define old_valid_dev(d) (1) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue ++#include + ++#ifdef __rh_config_h__ ++#define sigmask_lock sighand->siglock ++#define sig sighand + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- +-static inline int try_inc_mod_count(struct module *mod) ++static inline void __daemonize_modvers(void) + { +-#ifdef CONFIG_MODULES +- if (mod) +- __MOD_INC_USE_COUNT(mod); +-#endif +- return 1; +-} +-#endif ++ daemonize(); + ++ spin_lock_irq(¤t->sigmask_lock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sigmask_lock); ++} ++#undef daemonize ++#define daemonize(fmt, ...) do { \ ++ snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \ ++ __daemonize_modvers(); \ ++ } while(0) + +-/* Yes, I'm aware that it's a fairly ugly hack. +- Until the __constant_* macros appear in Linus' own kernels, this is +- the way it has to be done. +- DW 19/1/00 +- */ +- +-#include +- +-#ifndef __constant_cpu_to_le16 +- +-#ifdef __BIG_ENDIAN +-#define __constant_cpu_to_le64(x) ___swab64((x)) +-#define __constant_le64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_le32(x) ___swab32((x)) +-#define __constant_le32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_le16(x) ___swab16((x)) +-#define __constant_le16_to_cpu(x) ___swab16((x)) +-#define __constant_cpu_to_be64(x) ((__u64)(x)) +-#define __constant_be64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_be32(x) ((__u32)(x)) +-#define __constant_be32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_be16(x) ((__u16)(x)) +-#define __constant_be16_to_cpu(x) ((__u16)(x)) +-#else +-#ifdef __LITTLE_ENDIAN +-#define __constant_cpu_to_le64(x) ((__u64)(x)) +-#define __constant_le64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_le32(x) ((__u32)(x)) +-#define __constant_le32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_le16(x) ((__u16)(x)) +-#define __constant_le16_to_cpu(x) ((__u16)(x)) +-#define __constant_cpu_to_be64(x) ___swab64((x)) +-#define __constant_be64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_be32(x) ___swab32((x)) +-#define __constant_be32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_be16(x) ___swab16((x)) +-#define __constant_be16_to_cpu(x) ___swab16((x)) +-#else +-#error No (recognised) endianness defined (unless it,s PDP) +-#endif /* __LITTLE_ENDIAN */ +-#endif /* __BIG_ENDIAN */ +- +-#endif /* ifndef __constant_cpu_to_le16 */ +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- #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 +- +-#ifndef THIS_MODULE +-#ifdef MODULE +-#define THIS_MODULE (&__this_module) +-#else +-#define THIS_MODULE (NULL) +-#endif +-#endif +- +-#if LINUX_VERSION_CODE < 0x20300 +-#include +-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0) +-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0) +-#else +-#include +-#include +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +-#define set_current_state(state_value) \ +- do { current->state = (state_value); } while (0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-static inline int invalidate_device(kdev_t dev, int do_sync) { ++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) ++{ ++ unsigned long flags; ++ unsigned long ret; + +- if (do_sync) +- fsync_dev(dev); ++ spin_lock_irqsave(¤t->sigmask_lock, flags); ++ ret = dequeue_signal(mask, info); ++ spin_unlock_irqrestore(¤t->sigmask_lock, flags); + +- invalidate_buffers(dev); +- return 0; ++ return ret; + } +-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) +-static inline int invalidate_device(kdev_t dev, int do_sync) { +- struct super_block *sb = get_super(dev); +- int res = 0; + +- if (do_sync) +- fsync_dev(dev); +- +- if (sb) +- res = invalidate_inodes(sb); ++static inline int allow_signal(int sig) ++{ ++ if (sig < 1 || sig > _NSIG) ++ return -EINVAL; + +- invalidate_buffers(dev); +- return res; ++ spin_lock_irq(¤t->sigmask_lock); ++ sigdelset(¤t->blocked, sig); ++ recalc_sigpending(); ++ /* Make sure the kernel neither eats it now converts to SIGKILL */ ++ current->sig->action[sig-1].sa.sa_handler = (void *)2; ++ spin_unlock_irq(¤t->sigmask_lock); ++ return 0; + } +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +-#undef min +-#undef max +-#undef min_t +-#undef max_t +-/* +- * min()/max() macros that also do +- * strict type-checking.. See the +- * "unnecessary" pointer comparison. +- */ +-#define min(x,y) ({ \ +- const typeof(x) _x = (x); \ +- const typeof(y) _y = (y); \ +- (void) (&_x == &_y); \ +- _x < _y ? _x : _y; }) +- +-#define max(x,y) ({ \ +- const typeof(x) _x = (x); \ +- const typeof(y) _y = (y); \ +- (void) (&_x == &_y); \ +- _x > _y ? _x : _y; }) +- +-/* +- * ..and if you can't take the strict +- * types, you can specify one yourself. +- * +- * Or not use min/max at all, of course. +- */ +-#define min_t(type,x,y) \ +- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +-#define max_t(type,x,y) \ +- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7) +-struct completion { +- struct semaphore s; +-}; +- +-#define complete(c) up(&(c)->s) +-#define wait_for_completion(c) down(&(c)->s) +-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s); +- +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) +-/* This came later */ +-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \ +- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__)) ++static inline int disallow_signal(int sig) ++{ ++ if (sig < 1 || sig > _NSIG) ++ return -EINVAL; + +-#include ++ spin_lock_irq(¤t->sigmask_lock); ++ sigaddset(¤t->blocked, sig); ++ recalc_sigpending(); + +-static inline void add_gendisk(struct gendisk *gp) +-{ +- gp->next = gendisk_head; +- gendisk_head = gp; ++ current->sig->action[sig-1].sa.sa_handler = SIG_DFL; ++ spin_unlock_irq(¤t->sigmask_lock); ++ return 0; + } + +-static inline void del_gendisk(struct gendisk *gp) +-{ +- struct gendisk *gd, **gdp; ++#undef sighand ++#undef sigmask_lock + +- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) +- if (*gdp == gp) { +- gd = *gdp; *gdp = gd->next; +- break; +- } +-} ++#define PF_FREEZE 0 ++#define refrigerator(x) do { ; } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE) ++ /* Module bits */ + +-#define module_init(func) \ +-mod_init_t init_module(void) { \ +- return func(); \ +-} + +-#define module_exit(func) \ +-mod_exit_t cleanup_module(void) { \ +- return func(); \ +-} ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) ++#define try_module_get(m) try_inc_mod_count(m) ++#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0) ++#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0) ++#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \ +- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__)) +-#define MODULE_LICENSE(x) /* */ +-#endif + +-/* Removed for 2.4.21 kernel. This really should have been renamed +- when it was changed -- this is a PITA */ +-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) +-#include +-static inline void __recalc_sigpending(void) +-{ +- recalc_sigpending(current); +-} +-#undef recalc_sigpending +-#define recalc_sigpending() __recalc_sigpending () +-#endif ++ /* Random filesystem stuff, only for JFFS2 really */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) + #define parent_ino(d) ((d)->d_parent->d_inode->i_ino) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) +-#define need_resched() (current->need_resched) +-#define cond_resched() do { if need_resched() schedule(); } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +-#ifndef yield +-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) +-#endif +-#ifndef minor +-#define major(d) (MAJOR(to_kdev_t(d))) +-#define minor(d) (MINOR(to_kdev_t(d))) +-#endif +-#ifndef mk_kdev +-#define mk_kdev(ma,mi) MKDEV(ma,mi) +-#define kdev_t_to_nr(x) (x) +-#endif +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +- /* Is this right? */ +-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0) +-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL) +-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) +-#define rq_data_dir(x) ((x)->cmd) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +- +-#define IS_REQ_CMD(req) (1) +- +-#define QUEUE_LOCK(q) (&io_request_lock) +- +-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req)) +- +-#else /* > 2.5.0 */ +- +-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD) +- +-#define QUEUE_LOCK(q) ((q)->queue_lock) +- +-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock)) +- +-#endif +- +-/* Removed cos it broke stuff. Where is this required anyway? +- * #ifndef QUEUE_EMPTY +- * #define QUEUE_EMPTY (!CURRENT) +- * #endif +- */ +-#if LINUX_VERSION_CODE < 0x20300 +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) +-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si) +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) +-#else +-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE)) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12) + #define PageUptodate(x) Page_Uptodate(x) + #endif +@@ -580,4 +159,31 @@ + #define generic_file_readonly_mmap generic_file_mmap + #endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70) ++ ++#include ++#include ++ ++static inline char *strlcpy(char *dest, const char *src, int len) ++{ ++ dest[len-1] = 0; ++ return strncpy(dest, src, len-1); ++} ++ ++static inline int do_old_request_module(const char *mod) ++{ ++ return request_module(mod); ++} ++#undef request_module ++#define request_module(fmt, ...) \ ++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); }) ++ ++#endif /* 2.5.70 */ ++ ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++#endif ++ + #endif /* __LINUX_MTD_COMPATMAC_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/doc2000.h linux/include/linux/mtd/doc2000.h +--- linux-mips-2.4.27/include/linux/mtd/doc2000.h 2001-11-05 21:16:30.000000000 +0100 ++++ linux/include/linux/mtd/doc2000.h 2004-11-19 10:25:12.044177752 +0100 +@@ -1,13 +1,21 @@ +- +-/* Linux driver for Disk-On-Chip 2000 */ +-/* (c) 1999 Machine Vision Holdings, Inc. */ +-/* Author: David Woodhouse */ +-/* $Id: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */ ++/* ++ * Linux driver for Disk-On-Chip devices ++ * ++ * Copyright (C) 1999 Machine Vision Holdings, Inc. ++ * Copyright (C) 2001-2003 David Woodhouse ++ * Copyright (C) 2002-2003 Greg Ungerer ++ * Copyright (C) 2002-2003 SnapGear Inc ++ * ++ * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $ ++ * ++ * Released under GPL ++ */ + + #ifndef __MTD_DOC2000_H__ + #define __MTD_DOC2000_H__ + + #include ++#include + + #define DoC_Sig1 0 + #define DoC_Sig2 1 +@@ -38,18 +46,47 @@ + #define DoC_Mil_CDSN_IO 0x0800 + #define DoC_2k_CDSN_IO 0x1800 + ++#define DoC_Mplus_NOP 0x1002 ++#define DoC_Mplus_AliasResolution 0x1004 ++#define DoC_Mplus_DOCControl 0x1006 ++#define DoC_Mplus_AccessStatus 0x1008 ++#define DoC_Mplus_DeviceSelect 0x1008 ++#define DoC_Mplus_Configuration 0x100a ++#define DoC_Mplus_OutputControl 0x100c ++#define DoC_Mplus_FlashControl 0x1020 ++#define DoC_Mplus_FlashSelect 0x1022 ++#define DoC_Mplus_FlashCmd 0x1024 ++#define DoC_Mplus_FlashAddress 0x1026 ++#define DoC_Mplus_FlashData0 0x1028 ++#define DoC_Mplus_FlashData1 0x1029 ++#define DoC_Mplus_ReadPipeInit 0x102a ++#define DoC_Mplus_LastDataRead 0x102c ++#define DoC_Mplus_LastDataRead1 0x102d ++#define DoC_Mplus_WritePipeTerm 0x102e ++#define DoC_Mplus_ECCSyndrome0 0x1040 ++#define DoC_Mplus_ECCSyndrome1 0x1041 ++#define DoC_Mplus_ECCSyndrome2 0x1042 ++#define DoC_Mplus_ECCSyndrome3 0x1043 ++#define DoC_Mplus_ECCSyndrome4 0x1044 ++#define DoC_Mplus_ECCSyndrome5 0x1045 ++#define DoC_Mplus_ECCConf 0x1046 ++#define DoC_Mplus_Toggle 0x1046 ++#define DoC_Mplus_DownloadStatus 0x1074 ++#define DoC_Mplus_CtrlConfirm 0x1076 ++#define DoC_Mplus_Power 0x1fff ++ + /* How to access the device? + * On ARM, it'll be mmap'd directly with 32-bit wide accesses. + * On PPC, it's mmap'd and 16-bit wide. + * Others use readb/writeb + */ + #if defined(__arm__) +-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2)))) +-#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) ++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)))) ++#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) + #define DOC_IOREMAP_LEN 0x8000 + #elif defined(__ppc__) +-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1)))) +-#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) ++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)))) ++#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) + #define DOC_IOREMAP_LEN 0x4000 + #else + #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) +@@ -71,13 +108,21 @@ + #define DOC_MODE_RESERVED1 2 + #define DOC_MODE_RESERVED2 3 + +-#define DOC_MODE_MDWREN 4 + #define DOC_MODE_CLR_ERR 0x80 ++#define DOC_MODE_RST_LAT 0x10 ++#define DOC_MODE_BDECT 0x08 ++#define DOC_MODE_MDWREN 0x04 + + #define DOC_ChipID_Doc2k 0x20 ++#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */ + #define DOC_ChipID_DocMil 0x30 ++#define DOC_ChipID_DocMilPlus32 0x40 ++#define DOC_ChipID_DocMilPlus16 0x41 + + #define CDSN_CTRL_FR_B 0x80 ++#define CDSN_CTRL_FR_B0 0x40 ++#define CDSN_CTRL_FR_B1 0x80 ++ + #define CDSN_CTRL_ECC_IO 0x20 + #define CDSN_CTRL_FLASH_IO 0x10 + #define CDSN_CTRL_WP 0x08 +@@ -93,6 +138,10 @@ + #define DOC_ECC_RESV 0x02 + #define DOC_ECC_IGNORE 0x01 + ++#define DOC_FLASH_CE 0x80 ++#define DOC_FLASH_WP 0x40 ++#define DOC_FLASH_BANK 0x02 ++ + /* We have to also set the reserved bit 1 for enable */ + #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV) + #define DOC_ECC_DIS (DOC_ECC_RESV) +@@ -107,9 +156,12 @@ + #define MAX_FLOORS 4 + #define MAX_CHIPS 4 + +-#define MAX_FLOORS_MIL 4 ++#define MAX_FLOORS_MIL 1 + #define MAX_CHIPS_MIL 1 + ++#define MAX_FLOORS_MPLUS 2 ++#define MAX_CHIPS_MPLUS 1 ++ + #define ADDR_COLUMN 1 + #define ADDR_PAGE 2 + #define ADDR_COLUMN_PAGE 3 +@@ -118,7 +170,7 @@ + unsigned long physadr; + unsigned long virtadr; + unsigned long totlen; +- char ChipID; /* Type of DiskOnChip */ ++ unsigned char ChipID; /* Type of DiskOnChip */ + int ioreg; + + unsigned long mfr; /* Flash IDs - only one type of flash per device */ +@@ -126,6 +178,7 @@ + int chipshift; + char page256; + char pageadrlen; ++ char interleave; /* Internal interleaving - Millennium Plus style */ + unsigned long erasesize; + + int curfloor; +diff -Nurb linux-mips-2.4.27/include/linux/mtd/flashchip.h linux/include/linux/mtd/flashchip.h +--- linux-mips-2.4.27/include/linux/mtd/flashchip.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/flashchip.h 2004-11-19 10:25:12.045177600 +0100 +@@ -6,7 +6,7 @@ + * + * (C) 2000 Red Hat. GPLd. + * +- * $Id: flashchip.h,v 1.8 2002/10/21 13:20:52 jocke Exp $ ++ * $Id: flashchip.h,v 1.10 2004/01/27 10:16:20 dvrabel Exp $ + * + */ + +@@ -58,6 +58,11 @@ + int ref_point_counter; + flstate_t state; + flstate_t oldstate; ++ ++ int write_suspended:1; ++ int erase_suspended:1; ++ unsigned long in_progress_block_addr; ++ + spinlock_t *mutex; + spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */ + wait_queue_head_t wq; /* Wait on here when we're waiting for the chip +diff -Nurb linux-mips-2.4.27/include/linux/mtd/gen_probe.h linux/include/linux/mtd/gen_probe.h +--- linux-mips-2.4.27/include/linux/mtd/gen_probe.h 2001-11-05 21:16:30.000000000 +0100 ++++ linux/include/linux/mtd/gen_probe.h 2004-11-19 10:25:12.048177144 +0100 +@@ -1,7 +1,7 @@ + /* + * (C) 2001, 2001 Red Hat, Inc. + * GPL'd +- * $Id: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $ ++ * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #ifndef __LINUX_MTD_GEN_PROBE_H__ +@@ -10,12 +10,12 @@ + #include + #include + #include ++#include + + struct chip_probe { + char *name; + int (*probe_chip)(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); +- ++ unsigned long *chip_map, struct cfi_private *cfi); + }; + + struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp); +diff -Nurb linux-mips-2.4.27/include/linux/mtd/inftl.h linux/include/linux/mtd/inftl.h +--- linux-mips-2.4.27/include/linux/mtd/inftl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/inftl.h 2004-11-19 10:25:12.051176688 +0100 +@@ -0,0 +1,129 @@ ++/* ++ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer ++ * ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * $Id: inftl.h,v 1.4 2003/06/29 16:15:46 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_INFTL_H__ ++#define __MTD_INFTL_H__ ++ ++#include ++#include ++#include ++ ++#define OSAK_VERSION 0x5120 ++#define PERCENTUSED 98 ++ ++#define SECTORSIZE 512 ++ ++#ifndef INFTL_MAJOR ++#define INFTL_MAJOR 94 ++#endif ++#define INFTL_PARTN_BITS 4 ++ ++/* Block Control Information */ ++ ++struct inftl_bci { ++ __u8 ECCsig[6]; ++ __u8 Status; ++ __u8 Status1; ++} __attribute__((packed)); ++ ++struct inftl_unithead1 { ++ __u16 virtualUnitNo; ++ __u16 prevUnitNo; ++ __u8 ANAC; ++ __u8 NACs; ++ __u8 parityPerField; ++ __u8 discarded; ++} __attribute__((packed)); ++ ++struct inftl_unithead2 { ++ __u8 parityPerField; ++ __u8 ANAC; ++ __u16 prevUnitNo; ++ __u16 virtualUnitNo; ++ __u8 NACs; ++ __u8 discarded; ++} __attribute__((packed)); ++ ++struct inftl_unittail { ++ __u8 Reserved[4]; ++ __u16 EraseMark; ++ __u16 EraseMark1; ++} __attribute__((packed)); ++ ++union inftl_uci { ++ struct inftl_unithead1 a; ++ struct inftl_unithead2 b; ++ struct inftl_unittail c; ++}; ++ ++struct inftl_oob { ++ struct inftl_bci b; ++ union inftl_uci u; ++}; ++ ++ ++/* INFTL Media Header */ ++ ++struct INFTLPartition { ++ __u32 virtualUnits; ++ __u32 firstUnit; ++ __u32 lastUnit; ++ __u32 flags; ++ __u32 spareUnits; ++ __u32 Reserved0; ++ __u32 Reserved1; ++} __attribute__((packed)); ++ ++struct INFTLMediaHeader { ++ char bootRecordID[8]; ++ __u32 NoOfBootImageBlocks; ++ __u32 NoOfBinaryPartitions; ++ __u32 NoOfBDTLPartitions; ++ __u32 BlockMultiplierBits; ++ __u32 FormatFlags; ++ __u32 OsakVersion; ++ __u32 PercentUsed; ++ struct INFTLPartition Partitions[4]; ++} __attribute__((packed)); ++ ++/* Partition flag types */ ++#define INFTL_BINARY 0x20000000 ++#define INFTL_BDTL 0x40000000 ++#define INFTL_LAST 0x80000000 ++ ++ ++#ifdef __KERNEL__ ++ ++struct INFTLrecord { ++ struct mtd_blktrans_dev mbd; ++ __u16 MediaUnit, SpareMediaUnit; ++ __u32 EraseSize; ++ struct INFTLMediaHeader MediaHdr; ++ int usecount; ++ unsigned char heads; ++ unsigned char sectors; ++ unsigned short cylinders; ++ __u16 numvunits; ++ __u16 firstEUN; ++ __u16 lastEUN; ++ __u16 numfreeEUNs; ++ __u16 LastFreeEUN; /* To speed up finding a free EUN */ ++ int head,sect,cyl; ++ __u16 *PUtable; /* Physical Unit Table */ ++ __u16 *VUtable; /* Virtual Unit Table */ ++ unsigned int nb_blocks; /* number of physical blocks */ ++ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ ++ struct erase_info instr; ++}; ++ ++int INFTL_mount(struct INFTLrecord *s); ++int INFTL_formatblock(struct INFTLrecord *s, int block); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* __MTD_INFTL_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/jedec.h linux/include/linux/mtd/jedec.h +--- linux-mips-2.4.27/include/linux/mtd/jedec.h 2001-12-02 12:35:00.000000000 +0100 ++++ linux/include/linux/mtd/jedec.h 2004-11-19 10:25:12.052176536 +0100 +@@ -7,14 +7,13 @@ + * + * See the AMD flash databook for information on how to operate the interface. + * +- * $Id: jedec.h,v 1.2 2001/11/06 14:37:36 dwmw2 Exp $ ++ * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $ + */ + + #ifndef __LINUX_MTD_JEDEC_H__ + #define __LINUX_MTD_JEDEC_H__ + + #include +-#include + + #define MAX_JEDEC_CHIPS 16 + +diff -Nurb linux-mips-2.4.27/include/linux/mtd/map.h linux/include/linux/mtd/map.h +--- linux-mips-2.4.27/include/linux/mtd/map.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/map.h 2004-11-19 10:25:12.054176232 +0100 +@@ -1,14 +1,15 @@ + + /* Overhauled routines for dealing with different mmap regions of flash */ +-/* $Id: map.h,v 1.29 2002/10/21 13:20:52 jocke Exp $ */ ++/* $Id: map.h,v 1.34 2003/05/28 12:42:22 dwmw2 Exp $ */ + + #ifndef __LINUX_MTD_MAP_H__ + #define __LINUX_MTD_MAP_H__ + + #include + #include +-#include +-#include ++#include ++#include ++#include + + /* The map stuff is very simple. You fill in your struct map_info with + a handful of routines for accessing the device, making sure they handle +@@ -29,39 +30,44 @@ + struct map_info { + char *name; + unsigned long size; ++ unsigned long phys; ++#define NO_XIP (-1UL) ++ ++ unsigned long virt; ++ void *cached; ++ + int buswidth; /* in octets */ +- __u8 (*read8)(struct map_info *, unsigned long); +- __u16 (*read16)(struct map_info *, unsigned long); +- __u32 (*read32)(struct map_info *, unsigned long); +- __u64 (*read64)(struct map_info *, unsigned long); ++ ++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS ++ u8 (*read8)(struct map_info *, unsigned long); ++ u16 (*read16)(struct map_info *, unsigned long); ++ u32 (*read32)(struct map_info *, unsigned long); ++ u64 (*read64)(struct map_info *, unsigned long); + /* If it returned a 'long' I'd call it readl. + * It doesn't. + * I won't. + * dwmw2 */ + + void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t); +- void (*write8)(struct map_info *, __u8, unsigned long); +- void (*write16)(struct map_info *, __u16, unsigned long); +- void (*write32)(struct map_info *, __u32, unsigned long); +- void (*write64)(struct map_info *, __u64, unsigned long); ++ void (*write8)(struct map_info *, u8, unsigned long); ++ void (*write16)(struct map_info *, u16, unsigned long); ++ void (*write32)(struct map_info *, u32, unsigned long); ++ void (*write64)(struct map_info *, u64, unsigned long); + void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t); + +- u_char * (*point) (struct map_info *, loff_t, size_t); +- void (*unpoint) (struct map_info *, u_char *, loff_t, size_t); +- ++ /* We can perhaps put in 'point' and 'unpoint' methods, if we really ++ want to enable XIP for non-linear mappings. Not yet though. */ ++#endif ++ /* set_vpp() must handle being reentered -- enable, enable, disable ++ must leave it enabled. */ + void (*set_vpp)(struct map_info *, int); +- /* We put these two here rather than a single void *map_priv, +- because we want mappers to be able to have quickly-accessible +- cache for the 'currently-mapped page' without the _extra_ +- redirection that would be necessary. If you need more than +- two longs, turn the second into a pointer. dwmw2 */ ++ + unsigned long map_priv_1; + unsigned long map_priv_2; + void *fldrv_priv; + struct mtd_chip_driver *fldrv; + }; + +- + struct mtd_chip_driver { + struct mtd_info *(*probe)(struct map_info *map); + void (*destroy)(struct mtd_info *); +@@ -74,26 +80,93 @@ + void unregister_mtd_chip_driver(struct mtd_chip_driver *); + + struct mtd_info *do_map_probe(const char *name, struct map_info *map); ++void map_destroy(struct mtd_info *mtd); ++ ++#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) ++#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) ++ ++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS ++#define map_read8(map, ofs) (map)->read8(map, ofs) ++#define map_read16(map, ofs) (map)->read16(map, ofs) ++#define map_read32(map, ofs) (map)->read32(map, ofs) ++#define map_read64(map, ofs) (map)->read64(map, ofs) ++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len) ++#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs) ++#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs) ++#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs) ++#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs) ++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len) + ++extern void simple_map_init(struct map_info *); ++#define map_is_linear(map) (map->phys != NO_XIP) + +-/* +- * Destroy an MTD device which was created for a map device. +- * Make sure the MTD device is already unregistered before calling this +- */ +-static inline void map_destroy(struct mtd_info *mtd) +-{ +- struct map_info *map = mtd->priv; +- +- if (map->fldrv->destroy) +- map->fldrv->destroy(mtd); +-#ifdef CONFIG_MODULES +- if (map->fldrv->module) +- __MOD_DEC_USE_COUNT(map->fldrv->module); ++#else ++static inline u8 map_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->virt + ofs); ++} ++ ++static inline u16 map_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->virt + ofs); ++} ++ ++static inline u32 map_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->virt + ofs); ++} ++ ++static inline u64 map_read64(struct map_info *map, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++ return 0; ++#else ++ return __raw_readll(map->virt + ofs); + #endif +- kfree(mtd); + } + +-#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) +-#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) ++static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs) ++{ ++ __raw_writeb(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs) ++{ ++ __raw_writew(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs) ++{ ++ __raw_writel(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++#else ++ __raw_writell(datum, map->virt + ofs); ++ mb(); ++#endif /* CFI_B8 */ ++} ++ ++static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->virt + from, len); ++} ++ ++static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->virt + to, from, len); ++} ++ ++#define simple_map_init(map) do { } while (0) ++#define map_is_linear(map) (1) ++ ++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */ + + #endif /* __LINUX_MTD_MAP_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h +--- linux-mips-2.4.27/include/linux/mtd/mtd.h 2003-08-13 19:19:29.000000000 +0200 ++++ linux/include/linux/mtd/mtd.h 2004-11-19 10:25:12.055176080 +0100 +@@ -1,5 +1,10 @@ +- +-/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */ ++/* ++ * $Id: mtd.h,v 1.46 2003/07/11 07:36:21 dwmw2 Exp $ ++ * ++ * Copyright (C) 1999-2003 David Woodhouse et al. ++ * ++ * Released under GPL ++ */ + + #ifndef __MTD_MTD_H__ + #define __MTD_MTD_H__ +@@ -9,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -26,7 +30,6 @@ + unsigned char *ptr; + }; + +- + #define MTD_CHAR_MAJOR 90 + #define MTD_BLOCK_MAJOR 31 + #define MAX_MTD_DEVICES 16 +@@ -93,18 +96,23 @@ + #define MEMUNLOCK _IOW('M', 6, struct erase_info_user) + #define MEMGETREGIONCOUNT _IOR('M', 7, int) + #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +-#define MEMREADDATA _IOWR('M', 9, struct mtd_oob_buf) +-#define MEMWRITEDATA _IOWR('M', 10, struct mtd_oob_buf) ++#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) ++ ++struct nand_oobinfo { ++ int useecc; ++ int eccpos[6]; ++}; ++ + + #ifndef __KERNEL__ + + typedef struct mtd_info_user mtd_info_t; + typedef struct erase_info_user erase_info_t; + typedef struct region_info_user region_info_t; ++typedef struct nand_oobinfo nand_oobinfo_t; + + /* User-space ioctl definitions */ + +- + #else /* __KERNEL__ */ + + +@@ -150,10 +158,14 @@ + u_int32_t ecctype; + u_int32_t eccsize; + ++ + // Kernel-only stuff starts here. + char *name; + int index; + ++ // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) ++ struct nand_oobinfo oobinfo; ++ + /* Data for variable erase regions. If numeraseregions is zero, + * it means that the whole device has erasesize as given above. + */ +@@ -163,7 +175,6 @@ + /* This really shouldn't be here. It can go away in 2.5 */ + u_int32_t bank_size; + +- struct module *module; + int (*erase) (struct mtd_info *mtd, struct erase_info *instr); + + /* This stuff for eXecute-In-Place */ +@@ -176,8 +187,8 @@ + int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + +- int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); +- int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++ int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); ++ int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + + int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); +@@ -201,10 +212,10 @@ + */ + int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen); + int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, +- size_t *retlen, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); + int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, +- size_t *retlen, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + + /* Sync */ + void (*sync) (struct mtd_info *mtd); +@@ -218,6 +229,9 @@ + void (*resume) (struct mtd_info *mtd); + + void *priv; ++ ++ struct module *owner; ++ int usecount; + }; + + +@@ -226,31 +240,15 @@ + extern int add_mtd_device(struct mtd_info *mtd); + extern int del_mtd_device (struct mtd_info *mtd); + +-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num); +- +-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) +-{ +- struct mtd_info *ret; ++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + +- ret = __get_mtd_device(mtd, num); +- +- if (ret && ret->module && !try_inc_mod_count(ret->module)) +- return NULL; +- +- return ret; +-} +- +-static inline void put_mtd_device(struct mtd_info *mtd) +-{ +- if (mtd->module) +- __MOD_DEC_USE_COUNT(mtd->module); +-} ++extern void put_mtd_device(struct mtd_info *mtd); + + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); + void (*remove)(struct mtd_info *mtd); +- struct mtd_notifier *next; ++ struct list_head list; + }; + + +@@ -263,7 +261,6 @@ + int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen); + +-#ifndef MTDC + #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) + #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) + #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) +@@ -276,7 +273,6 @@ + #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) + #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) + #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) +-#endif /* MTDC */ + + /* + * Debugging macro and defines +@@ -293,7 +289,8 @@ + printk(KERN_INFO args); \ + } while(0) + #else /* CONFIG_MTD_DEBUG */ +-#define DEBUG(n, args...) ++#define DEBUG(n, args...) do { } while(0) ++ + #endif /* CONFIG_MTD_DEBUG */ + + #endif /* __KERNEL__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand.h linux/include/linux/mtd/nand.h +--- linux-mips-2.4.27/include/linux/mtd/nand.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/nand.h 2004-11-19 10:25:12.057175776 +0100 +@@ -2,10 +2,10 @@ + * linux/include/linux/mtd/nand.h + * + * Copyright (c) 2000 David Woodhouse +- * Steven J. Hill ++ * Steven J. Hill + * Thomas Gleixner + * +- * $Id: nand.h,v 1.19 2002/12/02 21:48:17 gleixner Exp $ ++ * $Id: nand.h,v 1.31 2003/07/11 15:07:02 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -49,12 +49,14 @@ + #define __LINUX_MTD_NAND_H + + #include +-#include ++#include ++#include + ++struct mtd_info; + /* + * Searches for a NAND device + */ +-extern int nand_scan (struct mtd_info *mtd); ++extern int nand_scan (struct mtd_info *mtd, int max_chips); + + /* + * Constants for hardware specific CLE/ALE/NCE function +@@ -65,6 +67,8 @@ + #define NAND_CTL_CLRCLE 4 + #define NAND_CTL_SETALE 5 + #define NAND_CTL_CLRALE 6 ++#define NAND_CTL_SETWP 7 ++#define NAND_CTL_CLRWP 8 + + /* + * Standard NAND flash commands +@@ -160,24 +164,33 @@ + struct nand_chip { + unsigned long IO_ADDR_R; + unsigned long IO_ADDR_W; +- void (*hwcontrol)(int cmd); +- int (*dev_ready)(void); ++ ++ u_char (*read_byte)(struct mtd_info *mtd); ++ void (*write_byte)(struct mtd_info *mtd, u_char byte); ++ ++ void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); ++ void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); ++ int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); ++ void (*select_chip)(struct mtd_info *mtd, int chip); ++ int (*block_bad)(struct mtd_info *mtd, unsigned long pos); ++ void (*hwcontrol)(struct mtd_info *mtd, int cmd); ++ int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); +- void (*calculate_ecc)(const u_char *dat, u_char *ecc_code); +- int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc); +- void (*enable_hwecc)(int mode); ++ void (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); ++ int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++ void (*enable_hwecc)(struct mtd_info *mtd, int mode); + int eccmode; + int eccsize; + int chip_delay; ++ int chipshift; + spinlock_t chip_lock; + wait_queue_head_t wq; + nand_state_t state; + int page_shift; + u_char *data_buf; + u_char *data_poi; +- u_char *data_cache; +- int cache_page; ++ void *priv; + }; + + /* +@@ -241,34 +254,4 @@ + */ + #define NAND_BADBLOCK_POS 5 + +-#define NAND_NONE_OOB 0 +-#define NAND_JFFS2_OOB 1 +-#define NAND_YAFFS_OOB 2 +- +-#define NAND_NOOB_ECCPOS0 0 +-#define NAND_NOOB_ECCPOS1 1 +-#define NAND_NOOB_ECCPOS2 2 +-#define NAND_NOOB_ECCPOS3 3 +-#define NAND_NOOB_ECCPOS4 6 +-#define NAND_NOOB_ECCPOS5 7 +- +-#define NAND_JFFS2_OOB_ECCPOS0 0 +-#define NAND_JFFS2_OOB_ECCPOS1 1 +-#define NAND_JFFS2_OOB_ECCPOS2 2 +-#define NAND_JFFS2_OOB_ECCPOS3 3 +-#define NAND_JFFS2_OOB_ECCPOS4 6 +-#define NAND_JFFS2_OOB_ECCPOS5 7 +- +-#define NAND_YAFFS_OOB_ECCPOS0 8 +-#define NAND_YAFFS_OOB_ECCPOS1 9 +-#define NAND_YAFFS_OOB_ECCPOS2 10 +-#define NAND_YAFFS_OOB_ECCPOS3 13 +-#define NAND_YAFFS_OOB_ECCPOS4 14 +-#define NAND_YAFFS_OOB_ECCPOS5 15 +- +-#define NAND_JFFS2_OOB8_FSDAPOS 6 +-#define NAND_JFFS2_OOB16_FSDAPOS 8 +-#define NAND_JFFS2_OOB8_FSDALEN 2 +-#define NAND_JFFS2_OOB16_FSDALEN 8 +- + #endif /* __LINUX_MTD_NAND_H */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand_ecc.h linux/include/linux/mtd/nand_ecc.h +--- linux-mips-2.4.27/include/linux/mtd/nand_ecc.h 2001-08-22 05:25:12.000000000 +0200 ++++ linux/include/linux/mtd/nand_ecc.h 2004-11-19 10:25:12.058175624 +0100 +@@ -1,9 +1,9 @@ + /* + * drivers/mtd/nand_ecc.h + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * +- * $Id: nand_ecc.h,v 1.1 2000/10/12 00:57:15 sjhill Exp $ ++ * $Id: nand_ecc.h,v 1.3 2003/07/01 23:31:15 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -12,17 +12,19 @@ + * This file is the header for the ECC algorithm. + */ + +-/* +- * Creates non-inverted ECC code from line parity +- */ +-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code); ++#ifndef __MTD_NAND_ECC_H__ ++#define __MTD_NAND_ECC_H__ ++ ++struct mtd_info; + + /* + * Calculate 3 byte ECC code for 256 byte block + */ +-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); ++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + + /* + * Detect and correct a 1 bit error for 256 byte block + */ +-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++ ++#endif /* __MTD_NAND_ECC_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nftl.h linux/include/linux/mtd/nftl.h +--- linux-mips-2.4.27/include/linux/mtd/nftl.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/nftl.h 2004-11-19 10:25:12.060175320 +0100 +@@ -1,15 +1,14 @@ +- +-/* Defines for NAND Flash Translation Layer */ +-/* (c) 1999 Machine Vision Holdings, Inc. */ +-/* Author: David Woodhouse */ +-/* $Id: nftl.h,v 1.11 2002/06/18 13:54:24 dwmw2 Exp $ */ ++/* ++ * $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $ ++ * ++ * (C) 1999-2003 David Woodhouse ++ */ + + #ifndef __MTD_NFTL_H__ + #define __MTD_NFTL_H__ + +-#ifndef __BOOT__ + #include +-#endif ++#include + + /* Block Control Information */ + +@@ -84,8 +83,7 @@ + #define BLOCK_RESERVED 0xfffc /* bios block or bad block */ + + struct NFTLrecord { +- struct mtd_info *mtd; +- struct semaphore mutex; ++ struct mtd_blktrans_dev mbd; + __u16 MediaUnit, SpareMediaUnit; + __u32 EraseSize; + struct NFTLMediaHeader MediaHdr; +@@ -97,7 +95,6 @@ + __u16 lastEUN; /* should be suppressed */ + __u16 numfreeEUNs; + __u16 LastFreeEUN; /* To speed up finding a free EUN */ +- __u32 nr_sects; + int head,sect,cyl; + __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ + __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ +@@ -114,7 +111,7 @@ + #endif + + #define MAX_NFTLS 16 +-#define MAX_SECTORS_PER_UNIT 32 ++#define MAX_SECTORS_PER_UNIT 64 + #define NFTL_PARTN_BITS 4 + + #endif /* __KERNEL__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h +--- linux-mips-2.4.27/include/linux/mtd/partitions.h 2002-06-27 00:36:47.000000000 +0200 ++++ linux/include/linux/mtd/partitions.h 2004-11-19 10:25:12.061175168 +0100 +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id: partitions.h,v 1.8 2002/03/08 16:34:36 rkaiser Exp $ ++ * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $ + */ + + #ifndef MTD_PARTITIONS_H +@@ -41,6 +41,7 @@ + u_int32_t size; /* partition size */ + u_int32_t offset; /* offset within the master MTD space */ + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ ++ struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/ + struct mtd_info **mtdp; /* pointer to store the MTD object */ + }; + +@@ -49,8 +50,27 @@ + #define MTDPART_SIZ_FULL (0) + + +-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); ++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); + int del_mtd_partitions(struct mtd_info *); + ++/* ++ * Functions dealing with the various ways of partitioning the space ++ */ ++ ++struct mtd_part_parser { ++ struct list_head list; ++ struct module *owner; ++ const char *name; ++ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long); ++}; ++ ++extern struct mtd_part_parser *get_partition_parser(const char *name); ++extern int register_mtd_parser(struct mtd_part_parser *parser); ++extern int deregister_mtd_parser(struct mtd_part_parser *parser); ++extern int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin); ++ ++#define put_partition_parser(p) do { module_put((p)->owner); } while(0) ++ + #endif + +diff -Nurb linux-mips-2.4.27/include/linux/mtd/physmap.h linux/include/linux/mtd/physmap.h +--- linux-mips-2.4.27/include/linux/mtd/physmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/physmap.h 2004-11-19 10:25:12.062175016 +0100 +@@ -0,0 +1,59 @@ ++/* ++ * For boards with physically mapped flash and using ++ * drivers/mtd/maps/physmap.c mapping driver. ++ * ++ * Copyright (C) 2003 MontaVista Software Inc. ++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#ifndef __LINUX_MTD_PHYSMAP__ ++ ++#include ++ ++#if defined(CONFIG_MTD_PHYSMAP) ++ ++#include ++#include ++#include ++ ++/* ++ * The map_info for physmap. Board can override size, buswidth, phys, ++ * (*set_vpp)(), etc in their initial setup routine. ++ */ ++extern struct map_info physmap_map; ++ ++/* ++ * Board needs to specify the exact mapping during their setup time. ++ */ ++static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) ) ++{ ++ physmap_map.phys = addr; ++ physmap_map.size = size; ++ physmap_map.buswidth = buswidth; ++ physmap_map.set_vpp = set_vpp; ++} ++ ++#if defined(CONFIG_MTD_PARTITIONS) ++ ++/* ++ * Machines that wish to do flash partition may want to call this function in ++ * their setup routine. ++ * ++ * physmap_set_partitions(mypartitions, num_parts); ++ * ++ * Note that one can always override this hard-coded partition with ++ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS). ++ */ ++void physmap_set_partitions(struct mtd_partition *parts, int num_parts); ++ ++#endif /* defined(CONFIG_MTD_PARTITIONS) */ ++#endif /* defined(CONFIG_MTD) */ ++ ++#endif /* __LINUX_MTD_PHYSMAP__ */ ++ +diff -Nurb linux-mips-2.4.27/include/linux/rbtree-24.h linux/include/linux/rbtree-24.h +--- linux-mips-2.4.27/include/linux/rbtree-24.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/rbtree-24.h 2004-11-19 10:25:12.143162704 +0100 +@@ -0,0 +1,133 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ linux/include/linux/rbtree.h ++ ++ To use rbtrees you'll have to implement your own insert and search cores. ++ This will avoid us to use callbacks and to drop drammatically performances. ++ I know it's not the cleaner way, but in C (not in C++) to get ++ performances and genericity... ++ ++ Some example of insert and search follows here. The search is a plain ++ normal search over an ordered tree. The insert instead must be implemented ++ int two steps: as first thing the code must insert the element in ++ order as a red leaf in the tree, then the support library function ++ rb_insert_color() must be called. Such function will do the ++ not trivial work to rebalance the rbtree if necessary. ++ ++----------------------------------------------------------------------- ++static inline struct page * rb_search_page_cache(struct inode * inode, ++ unsigned long offset) ++{ ++ rb_node_t * n = inode->i_rb_page_cache.rb_node; ++ struct page * page; ++ ++ while (n) ++ { ++ page = rb_entry(n, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ n = n->rb_left; ++ else if (offset > page->offset) ++ n = n->rb_right; ++ else ++ return page; ++ } ++ return NULL; ++} ++ ++static inline struct page * __rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ rb_node_t * node) ++{ ++ rb_node_t ** p = &inode->i_rb_page_cache.rb_node; ++ rb_node_t * parent = NULL; ++ struct page * page; ++ ++ while (*p) ++ { ++ parent = *p; ++ page = rb_entry(parent, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ p = &(*p)->rb_left; ++ else if (offset > page->offset) ++ p = &(*p)->rb_right; ++ else ++ return page; ++ } ++ ++ rb_link_node(node, parent, p); ++ ++ return NULL; ++} ++ ++static inline struct page * rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ rb_node_t * node) ++{ ++ struct page * ret; ++ if ((ret = __rb_insert_page_cache(inode, offset, node))) ++ goto out; ++ rb_insert_color(node, &inode->i_rb_page_cache); ++ out: ++ return ret; ++} ++----------------------------------------------------------------------- ++*/ ++ ++#ifndef _LINUX_RBTREE_H ++#define _LINUX_RBTREE_H ++ ++#include ++#include ++ ++typedef struct rb_node_s ++{ ++ struct rb_node_s * rb_parent; ++ int rb_color; ++#define RB_RED 0 ++#define RB_BLACK 1 ++ struct rb_node_s * rb_right; ++ struct rb_node_s * rb_left; ++} ++rb_node_t; ++ ++typedef struct rb_root_s ++{ ++ struct rb_node_s * rb_node; ++} ++rb_root_t; ++ ++#define RB_ROOT (rb_root_t) { NULL, } ++#define rb_entry(ptr, type, member) \ ++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) ++ ++extern void rb_insert_color(rb_node_t *, rb_root_t *); ++extern void rb_erase(rb_node_t *, rb_root_t *); ++ ++static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link) ++{ ++ node->rb_parent = parent; ++ node->rb_color = RB_RED; ++ node->rb_left = node->rb_right = NULL; ++ ++ *rb_link = node; ++} ++ ++#endif /* _LINUX_RBTREE_H */ +diff -Nurb linux-mips-2.4.27/include/linux/rbtree.h linux/include/linux/rbtree.h +--- linux-mips-2.4.27/include/linux/rbtree.h 2001-10-19 03:25:03.000000000 +0200 ++++ linux/include/linux/rbtree.h 2004-11-19 10:25:12.148161944 +0100 +@@ -1,133 +1,25 @@ + /* +- Red Black Trees +- (C) 1999 Andrea Arcangeli ++ * 2.5 compatibility ++ * $Id: rbtree.h,v 1.3 2003/01/14 13:56:05 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_COMPAT_RBTREE_H__ ++#define __MTD_COMPAT_RBTREE_H__ ++ ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40) ++#include_next ++#else ++#define rb_node_s rb_node ++#define rb_root_s rb_root ++ ++#include ++ ++/* Find logical next and previous nodes in a tree */ ++extern struct rb_node *rb_next(struct rb_node *); ++extern struct rb_node *rb_prev(struct rb_node *); ++extern struct rb_node *rb_first(struct rb_root *); ++#endif + +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- +- linux/include/linux/rbtree.h +- +- To use rbtrees you'll have to implement your own insert and search cores. +- This will avoid us to use callbacks and to drop drammatically performances. +- I know it's not the cleaner way, but in C (not in C++) to get +- performances and genericity... +- +- Some example of insert and search follows here. The search is a plain +- normal search over an ordered tree. The insert instead must be implemented +- int two steps: as first thing the code must insert the element in +- order as a red leaf in the tree, then the support library function +- rb_insert_color() must be called. Such function will do the +- not trivial work to rebalance the rbtree if necessary. +- +------------------------------------------------------------------------ +-static inline struct page * rb_search_page_cache(struct inode * inode, +- unsigned long offset) +-{ +- rb_node_t * n = inode->i_rb_page_cache.rb_node; +- struct page * page; +- +- while (n) +- { +- page = rb_entry(n, struct page, rb_page_cache); +- +- if (offset < page->offset) +- n = n->rb_left; +- else if (offset > page->offset) +- n = n->rb_right; +- else +- return page; +- } +- return NULL; +-} +- +-static inline struct page * __rb_insert_page_cache(struct inode * inode, +- unsigned long offset, +- rb_node_t * node) +-{ +- rb_node_t ** p = &inode->i_rb_page_cache.rb_node; +- rb_node_t * parent = NULL; +- struct page * page; +- +- while (*p) +- { +- parent = *p; +- page = rb_entry(parent, struct page, rb_page_cache); +- +- if (offset < page->offset) +- p = &(*p)->rb_left; +- else if (offset > page->offset) +- p = &(*p)->rb_right; +- else +- return page; +- } +- +- rb_link_node(node, parent, p); +- +- return NULL; +-} +- +-static inline struct page * rb_insert_page_cache(struct inode * inode, +- unsigned long offset, +- rb_node_t * node) +-{ +- struct page * ret; +- if ((ret = __rb_insert_page_cache(inode, offset, node))) +- goto out; +- rb_insert_color(node, &inode->i_rb_page_cache); +- out: +- return ret; +-} +------------------------------------------------------------------------ +-*/ +- +-#ifndef _LINUX_RBTREE_H +-#define _LINUX_RBTREE_H +- +-#include +-#include +- +-typedef struct rb_node_s +-{ +- struct rb_node_s * rb_parent; +- int rb_color; +-#define RB_RED 0 +-#define RB_BLACK 1 +- struct rb_node_s * rb_right; +- struct rb_node_s * rb_left; +-} +-rb_node_t; +- +-typedef struct rb_root_s +-{ +- struct rb_node_s * rb_node; +-} +-rb_root_t; +- +-#define RB_ROOT (rb_root_t) { NULL, } +-#define rb_entry(ptr, type, member) \ +- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) +- +-extern void rb_insert_color(rb_node_t *, rb_root_t *); +-extern void rb_erase(rb_node_t *, rb_root_t *); +- +-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link) +-{ +- node->rb_parent = parent; +- node->rb_color = RB_RED; +- node->rb_left = node->rb_right = NULL; +- +- *rb_link = node; +-} +- +-#endif /* _LINUX_RBTREE_H */ ++#endif /* __MTD_COMPAT_RBTREE_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/suspend.h linux/include/linux/suspend.h +--- linux-mips-2.4.27/include/linux/suspend.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/suspend.h 2004-11-19 10:25:12.150161640 +0100 +@@ -0,0 +1,10 @@ ++/* $Id: suspend.h,v 1.1 2003/10/13 20:56:47 dwmw2 Exp $ */ ++ ++#ifndef __MTD_COMPAT_VERSION_H__ ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include_next ++#endif ++ ++#endif /* __MTD_COMPAT_VERSION_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/workqueue.h linux/include/linux/workqueue.h +--- linux-mips-2.4.27/include/linux/workqueue.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/workqueue.h 2004-11-19 10:25:12.152161336 +0100 +@@ -0,0 +1,21 @@ ++/* ++ * 2.5 compatibility ++ * $Id: workqueue.h,v 1.1 2002/11/11 16:39:10 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_COMPAT_WORKQUEUE_H__ ++#define __MTD_COMPAT_WORKQUEUE_H__ ++ ++#include ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) ++#include_next ++#else ++#include ++#define work_struct tq_struct ++#define schedule_work(x) schedule_task(x) ++#define flush_scheduled_work flush_scheduled_tasks ++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z) ++#endif ++ ++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */ diff --git a/packages/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff b/packages/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff index e69de29bb2..ba24bb1381 100644 --- a/packages/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff +++ b/packages/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff @@ -0,0 +1,248 @@ +diff -Nurb linux/drivers/mtd/maps/Config.in linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in +--- linux/drivers/mtd/maps/Config.in 2004-11-17 18:17:59.049312400 +0100 ++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in 2004-11-17 18:12:26.000000000 +0100 +@@ -80,6 +80,7 @@ + bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT + bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER + fi ++ dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1 + dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS + if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000 +diff -Nurb linux/drivers/mtd/maps/Makefile linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile +--- linux/drivers/mtd/maps/Makefile 2004-11-17 18:17:59.051312096 +0100 ++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile 2004-11-17 18:12:26.000000000 +0100 +@@ -49,6 +49,7 @@ + obj-$(CONFIG_MTD_PCI) += pci.o + obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o + obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o ++obj-$(CONFIG_MTD_MTX1) += mtx-1.o + obj-$(CONFIG_MTD_LASAT) += lasat.o + obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o + obj-$(CONFIG_MTD_EDB7312) += edb7312.o +diff -Nurb linux/drivers/mtd/maps/mtx-1.c linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c +--- linux/drivers/mtd/maps/mtx-1.c 2004-11-17 18:17:02.689880336 +0100 ++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c 2004-11-17 18:12:26.000000000 +0100 +@@ -1,166 +1,78 @@ + /* + * Flash memory access on 4G Systems MTX-1 board + * +- * (C) 2003 Pete Popov +- * Bruno Randolf ++ * (C) Bruno Randolf (4G Systeme GmbH) + */ + + #include + #include + #include + #include ++#include + + #include + #include + #include + + #include +-#include + +-#ifdef DEBUG_RW +-#define DBG(x...) printk(x) +-#else +-#define DBG(x...) +-#endif +- +-#ifdef CONFIG_MIPS_MTX1 + #define WINDOW_ADDR 0x1E000000 + #define WINDOW_SIZE 0x2000000 +-#endif +- +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- ret = __raw_readb(map->map_priv_1 + ofs); +- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- ret = __raw_readw(map->map_priv_1 + ofs); +- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- __u32 ret; +- ret = __raw_readl(map->map_priv_1 + ofs); +- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from); +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- +- ++#define BUSWIDTH 4 + + static struct map_info mtx1_map = { +- name: "MTX-1 flash", +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to, ++ .name = "MTX-1 flash", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR + }; + +- +-static unsigned long flash_size = 0x01000000; +-static unsigned char flash_buswidth = 4; + static struct mtd_partition mtx1_partitions[] = { + { +- name: "user fs", +- size: 0x1c00000, +- offset: 0, ++ .name = "user fs", ++ .size = 0x1c00000, ++ .offset = 0, + },{ +- name: "yamon", +- size: 0x0100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ +- name: "raw kernel", +- size: 0x02c0000, +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = 0x02c0000, ++ .offset = MTDPART_OFS_APPEND, + },{ +- name: "yamon env vars", +- size: 0x0040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon environment", ++ .size = 0x0040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + } + }; + +- +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- +-static struct mtd_partition *parsed_parts; + static struct mtd_info *mymtd; + + int __init mtx1_mtd_init(void) + { +- struct mtd_partition *parts; +- int nb_parts = 0; +- char *part_type; ++ printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus at %x\n", ++ mtx1_map.buswidth*8, WINDOW_ADDR); + +- /* Default flash buswidth */ +- mtx1_map.buswidth = flash_buswidth; ++ mtx1_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ if (!mtx1_map.virt) { ++ printk("mtx_mtd_init: failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&mtx1_map); + +- /* +- * Static partition definition selection +- */ +- part_type = "static"; +- parts = mtx1_partitions; +- nb_parts = NB_OF(mtx1_partitions); +- mtx1_map.size = flash_size; +- +- /* +- * Now let's probe for the actual flash. Do it here since +- * specific machine settings might have been set above. +- */ +- printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus\n", +- mtx1_map.buswidth*8); +- mtx1_map.map_priv_1 = +- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + mymtd = do_map_probe("cfi_probe", &mtx1_map); +- if (!mymtd) return -ENXIO; +- mymtd->module = THIS_MODULE; ++ if (!mymtd) { ++ iounmap(mtx1_map.virt); ++ return -ENXIO; ++ } ++ ++ mymtd->owner = THIS_MODULE; + +- add_mtd_partitions(mymtd, parts, nb_parts); +- return 0; ++ return add_mtd_partitions(mymtd, mtx1_partitions, ARRAY_SIZE(mtx1_partitions)); + } + + static void __exit mtx1_mtd_cleanup(void) +@@ -168,14 +80,14 @@ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); + } ++ if (mtx1_map.virt) ++ iounmap(mtx1_map.virt); + } + + module_init(mtx1_mtd_init); + module_exit(mtx1_mtd_cleanup); + +-MODULE_AUTHOR("Pete Popov"); ++MODULE_AUTHOR("Bruno Randolf "); + MODULE_DESCRIPTION("MTX-1 CFI map driver"); + MODULE_LICENSE("GPL"); + diff --git a/packages/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff b/packages/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff index e69de29bb2..9e310bf327 100644 --- a/packages/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff +++ b/packages/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff @@ -0,0 +1,21 @@ +--- linux-mips-2.4.24-pre2+mtd-2004-01-27/fs/jffs2/erase.c 2004-11-17 18:17:59.000000000 +0100 ++++ linux/fs/jffs2/erase.c 2004-11-17 18:44:52.260067088 +0100 +@@ -365,11 +365,13 @@ + jeb->dirty_size = 0; + jeb->wasted_size = 0; + } else { +- struct jffs2_unknown_node marker = { +- .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), +- .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), +- .totlen = cpu_to_je32(c->cleanmarker_size) +- }; ++ /* compiler workaround, structure was not initialized before ++ on mipsel cross compilers ++ fix by Eugene.Wisor@flukenetworks.com */ ++ struct jffs2_unknown_node marker; ++ marker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ marker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ++ marker.totlen = cpu_to_je32(c->cleanmarker_size); + + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); + diff --git a/packages/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff b/packages/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff index e69de29bb2..9a13c2a403 100644 --- a/packages/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff +++ b/packages/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff @@ -0,0 +1,15 @@ +--- linux/arch/mips/au1000/mtx-1/board_setup.c.orig 2004-10-13 19:05:15.340583632 +0200 ++++ linux/arch/mips/au1000/mtx-1/board_setup.c 2004-10-13 19:01:03.402883984 +0200 +@@ -48,6 +48,12 @@ + + extern struct rtc_ops no_rtc_ops; + ++void board_reset (void) ++{ ++ /* Hit BCSR.SYSTEM_CONTROL[SW_RST] */ ++ au_writel(0x00000000, 0xAE00001C); ++} ++ + void __init board_setup(void) + { + rtc_ops = &no_rtc_ops; diff --git a/packages/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff b/packages/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff index e69de29bb2..4fc6b9e667 100644 --- a/packages/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff +++ b/packages/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff @@ -0,0 +1,18 @@ +diff -Nurb linux-mips-2.4.27/arch/mips/au1000/mtx-1/irqmap.c linux/arch/mips/au1000/mtx-1/irqmap.c +--- linux-mips-2.4.27/arch/mips/au1000/mtx-1/irqmap.c 2004-04-02 11:04:00.000000000 +0200 ++++ linux/arch/mips/au1000/mtx-1/irqmap.c 2004-11-22 14:15:56.000000000 +0100 +@@ -72,10 +72,10 @@ + * A B C D + */ + { +- {INTA, INTB, INTC, INTD}, /* IDSEL 0 */ +- {INTA, INTB, INTC, INTD}, /* IDSEL 1 */ +- {INTA, INTB, INTC, INTD}, /* IDSEL 2 */ +- {INTA, INTB, INTC, INTD}, /* IDSEL 3 */ ++ {INTA, INTB, INTX, INTX}, /* IDSEL 0 */ ++ {INTB, INTA, INTX, INTX}, /* IDSEL 1 */ ++ {INTC, INTD, INTX, INTX}, /* IDSEL 2 */ ++ {INTD, INTC, INTX, INTX}, /* IDSEL 3 */ + }; + const long min_idsel = 0, max_idsel = 3, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; diff --git a/packages/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch b/packages/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch index e69de29bb2..4e8a7576bd 100644 --- a/packages/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch +++ b/packages/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch @@ -0,0 +1,5308 @@ +diff -Naru linux/arch/mips/Makefile linux.spi/arch/mips/Makefile +--- linux/arch/mips/Makefile 2004-05-06 15:23:41.000000000 -0400 ++++ linux.spi/arch/mips/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -29,6 +29,8 @@ + endif + + MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot ++MAKEZBOOT = $(MAKE) -C arch/$(ARCH)/zboot ++BOOT_TARGETS = zImage zImage.initrd zImage.flash + + check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi) + +@@ -757,12 +749,16 @@ + + vmlinux.ecoff: vmlinux + @$(MAKEBOOT) $@ ++ ++$(BOOT_TARGETS): vmlinux ++ @$(MAKEZBOOT) $@ + + vmlinux.srec: vmlinux + @$(MAKEBOOT) $@ + + archclean: + @$(MAKEBOOT) clean ++ @$(MAKEZBOOT) clean + rm -f arch/$(ARCH)/ld.script + $(MAKE) -C arch/$(ARCH)/tools clean + $(MAKE) -C arch/mips/baget clean +diff -Naru linux/arch/mips/zboot/common/au1k_uart.c linux.spi/arch/mips/zboot/common/au1k_uart.c +--- linux/arch/mips/zboot/common/au1k_uart.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/au1k_uart.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,103 @@ ++/* ++ * BRIEF MODULE DESCRIPTION ++ * Simple Au1000 uart routines. ++ * ++ * Copyright 2001 MontaVista Software Inc. ++ * Author: MontaVista Software, Inc. ++ * ppopov@mvista.com or source@mvista.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++#include ++#include "ns16550.h" ++ ++typedef unsigned char uint8; ++typedef unsigned int uint32; ++ ++#define UART16550_BAUD_2400 2400 ++#define UART16550_BAUD_4800 4800 ++#define UART16550_BAUD_9600 9600 ++#define UART16550_BAUD_19200 19200 ++#define UART16550_BAUD_38400 38400 ++#define UART16550_BAUD_57600 57600 ++#define UART16550_BAUD_115200 115200 ++ ++#define UART16550_PARITY_NONE 0 ++#define UART16550_PARITY_ODD 0x08 ++#define UART16550_PARITY_EVEN 0x18 ++#define UART16550_PARITY_MARK 0x28 ++#define UART16550_PARITY_SPACE 0x38 ++ ++#define UART16550_DATA_5BIT 0x0 ++#define UART16550_DATA_6BIT 0x1 ++#define UART16550_DATA_7BIT 0x2 ++#define UART16550_DATA_8BIT 0x3 ++ ++#define UART16550_STOP_1BIT 0x0 ++#define UART16550_STOP_2BIT 0x4 ++ ++/* It would be nice if we had a better way to do this. ++ * It could be a variable defined in one of the board specific files. ++ */ ++#undef UART_BASE ++#ifdef CONFIG_COGENT_CSB250 ++#define UART_BASE UART3_ADDR ++#else ++#define UART_BASE UART0_ADDR ++#endif ++ ++/* memory-mapped read/write of the port */ ++#define UART16550_READ(y) (readl(UART_BASE + y) & 0xff) ++#define UART16550_WRITE(y,z) (writel(z&0xff, UART_BASE + y)) ++ ++/* ++ * We use uart 0, which is already initialized by ++ * yamon. ++ */ ++volatile struct NS16550 * ++serial_init(int chan) ++{ ++ volatile struct NS16550 *com_port; ++ com_port = (struct NS16550 *) UART_BASE; ++ return (com_port); ++} ++ ++void ++serial_putc(volatile struct NS16550 *com_port, unsigned char c) ++{ ++ while ((UART16550_READ(UART_LSR)&0x40) == 0); ++ UART16550_WRITE(UART_TX, c); ++} ++ ++unsigned char ++serial_getc(volatile struct NS16550 *com_port) ++{ ++ while((UART16550_READ(UART_LSR) & 0x1) == 0); ++ return UART16550_READ(UART_RX); ++} ++ ++int ++serial_tstc(volatile struct NS16550 *com_port) ++{ ++ return((UART16550_READ(UART_LSR) & LSR_DR) != 0); ++} +diff -Naru linux/arch/mips/zboot/common/ctype.c linux.spi/arch/mips/zboot/common/ctype.c +--- linux/arch/mips/zboot/common/ctype.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/ctype.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,35 @@ ++/* ++ * linux/lib/ctype.c ++ * ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ */ ++ ++#include ++ ++unsigned char _ctype[] = { ++_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ ++_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ ++_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ ++_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ ++_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ ++_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ ++_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ ++_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ ++_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ ++_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ ++_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ ++_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ ++_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ ++_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ ++_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ ++_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ ++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ ++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ ++_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ ++_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ ++_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ ++_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ ++_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ ++_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ ++ ++ +diff -Naru linux/arch/mips/zboot/common/dummy.c linux.spi/arch/mips/zboot/common/dummy.c +--- linux/arch/mips/zboot/common/dummy.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/dummy.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,4 @@ ++int main(void) ++{ ++ return 0; ++} +diff -Naru linux/arch/mips/zboot/common/Makefile linux.spi/arch/mips/zboot/common/Makefile +--- linux/arch/mips/zboot/common/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,27 @@ ++# ++# arch/mips/zboot/common/Makefile ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++# ++# Tom Rini January 2001 ++# ++ ++.c.s: ++ $(CC) $(CFLAGS) -S -o $*.s $< ++.s.o: ++ $(AS) -o $*.o $< ++.c.o: ++ $(CC) $(CFLAGS) -c -o $*.o $< ++.S.s: ++ $(CPP) $(AFLAGS) -o $*.o $< ++.S.o: ++ $(CC) $(AFLAGS) -c -o $*.o $< ++ ++clean: ++ rm -rf *.o ++ ++OBJCOPY_ARGS = -O elf32-tradlittlemips ++ ++include $(TOPDIR)/Rules.make +diff -Naru linux/arch/mips/zboot/common/misc-common.c linux.spi/arch/mips/zboot/common/misc-common.c +--- linux/arch/mips/zboot/common/misc-common.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/misc-common.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,437 @@ ++/* ++ * arch/mips/zboot/common/misc-common.c ++ * ++ * Misc. bootloader code (almost) all platforms can use ++ * ++ * Author: Johnnie Peters ++ * Editor: Tom Rini ++ * ++ * Derived from arch/ppc/boot/prep/misc.c ++ * ++ * Ported by Pete Popov to ++ * support mips board(s). I also got rid of the vga console ++ * code. ++ * ++ * Copyright 2000-2001 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include "zlib.h" ++#include ++ ++extern char *avail_ram; ++extern char *end_avail; ++extern char _end[]; ++ ++void puts(const char *); ++void putc(const char c); ++void puthex(unsigned long val); ++void _bcopy(char *src, char *dst, int len); ++void gunzip(void *, int, unsigned char *, int *); ++static int _cvt(unsigned long val, char *buf, long radix, char *digits); ++ ++void _vprintk(void(*)(const char), const char *, va_list ap); ++ ++struct NS16550 *com_port; ++ ++int serial_tstc(volatile struct NS16550 *); ++unsigned char serial_getc(volatile struct NS16550 *); ++void serial_putc(volatile struct NS16550 *, unsigned char); ++ ++void pause(void) ++{ ++ puts("pause\n"); ++} ++ ++void exit(void) ++{ ++ puts("exit\n"); ++ while(1); ++} ++ ++int tstc(void) ++{ ++ return (serial_tstc(com_port)); ++} ++ ++int getc(void) ++{ ++ while (1) { ++ if (serial_tstc(com_port)) ++ return (serial_getc(com_port)); ++ } ++} ++ ++void ++putc(const char c) ++{ ++ int x,y; ++ ++ serial_putc(com_port, c); ++ if ( c == '\n' ) ++ serial_putc(com_port, '\r'); ++} ++ ++void puts(const char *s) ++{ ++ char c; ++ while ( ( c = *s++ ) != '\0' ) { ++ serial_putc(com_port, c); ++ if ( c == '\n' ) serial_putc(com_port, '\r'); ++ } ++} ++ ++void error(char *x) ++{ ++ puts("\n\n"); ++ puts(x); ++ puts("\n\n -- System halted"); ++ ++ while(1); /* Halt */ ++} ++ ++void *zalloc(void *x, unsigned items, unsigned size) ++{ ++ void *p = avail_ram; ++ ++ size *= items; ++ size = (size + 7) & -8; ++ avail_ram += size; ++ if (avail_ram > end_avail) { ++ puts("oops... out of memory\n"); ++ pause(); ++ } ++ return p; ++} ++ ++void zfree(void *x, void *addr, unsigned nb) ++{ ++} ++ ++#define HEAD_CRC 2 ++#define EXTRA_FIELD 4 ++#define ORIG_NAME 8 ++#define COMMENT 0x10 ++#define RESERVED 0xe0 ++ ++#define DEFLATED 8 ++ ++void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) ++{ ++ z_stream s; ++ int r, i, flags; ++ ++ /* skip header */ ++ i = 10; ++ flags = src[3]; ++ if (src[2] != DEFLATED || (flags & RESERVED) != 0) { ++ puts("bad gzipped data\n"); ++ exit(); ++ } ++ if ((flags & EXTRA_FIELD) != 0) ++ i = 12 + src[10] + (src[11] << 8); ++ if ((flags & ORIG_NAME) != 0) ++ while (src[i++] != 0) ++ ; ++ if ((flags & COMMENT) != 0) ++ while (src[i++] != 0) ++ ; ++ if ((flags & HEAD_CRC) != 0) ++ i += 2; ++ if (i >= *lenp) { ++ puts("gunzip: ran out of data in header\n"); ++ exit(); ++ } ++ ++ s.zalloc = zalloc; ++ s.zfree = zfree; ++ r = inflateInit2(&s, -MAX_WBITS); ++ if (r != Z_OK) { ++ puts("inflateInit2 returned %d\n"); ++ exit(); ++ } ++ s.next_in = src + i; ++ s.avail_in = *lenp - i; ++ s.next_out = dst; ++ s.avail_out = dstlen; ++ r = inflate(&s, Z_FINISH); ++ if (r != Z_OK && r != Z_STREAM_END) { ++ puts("inflate returned %d\n"); ++ exit(); ++ } ++ *lenp = s.next_out - (unsigned char *) dst; ++ inflateEnd(&s); ++} ++ ++void ++puthex(unsigned long val) ++{ ++ ++ unsigned char buf[10]; ++ int i; ++ for (i = 7; i >= 0; i--) ++ { ++ buf[i] = "0123456789ABCDEF"[val & 0x0F]; ++ val >>= 4; ++ } ++ buf[8] = '\0'; ++ puts(buf); ++} ++ ++#define FALSE 0 ++#define TRUE 1 ++ ++void ++_printk(char const *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ _vprintk(putc, fmt, ap); ++ va_end(ap); ++ return; ++} ++ ++#define is_digit(c) ((c >= '0') && (c <= '9')) ++ ++void ++_vprintk(void(*putc)(const char), const char *fmt0, va_list ap) ++{ ++ char c, sign, *cp = 0; ++ int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right; ++ char buf[32]; ++ long val; ++ while ((c = *fmt0++)) ++ { ++ if (c == '%') ++ { ++ c = *fmt0++; ++ left_prec = right_prec = pad_on_right = 0; ++ if (c == '-') ++ { ++ c = *fmt0++; ++ pad_on_right++; ++ } ++ if (c == '0') ++ { ++ zero_fill = TRUE; ++ c = *fmt0++; ++ } else ++ { ++ zero_fill = FALSE; ++ } ++ while (is_digit(c)) ++ { ++ left_prec = (left_prec * 10) + (c - '0'); ++ c = *fmt0++; ++ } ++ if (c == '.') ++ { ++ c = *fmt0++; ++ zero_fill++; ++ while (is_digit(c)) ++ { ++ right_prec = (right_prec * 10) + (c - '0'); ++ c = *fmt0++; ++ } ++ } else ++ { ++ right_prec = left_prec; ++ } ++ sign = '\0'; ++ switch (c) ++ { ++ case 'd': ++ case 'x': ++ case 'X': ++ val = va_arg(ap, long); ++ switch (c) ++ { ++ case 'd': ++ if (val < 0) ++ { ++ sign = '-'; ++ val = -val; ++ } ++ length = _cvt(val, buf, 10, "0123456789"); ++ break; ++ case 'x': ++ length = _cvt(val, buf, 16, "0123456789abcdef"); ++ break; ++ case 'X': ++ length = _cvt(val, buf, 16, "0123456789ABCDEF"); ++ break; ++ } ++ cp = buf; ++ break; ++ case 's': ++ cp = va_arg(ap, char *); ++ length = strlen(cp); ++ break; ++ case 'c': ++ c = va_arg(ap, long /*char*/); ++ (*putc)(c); ++ continue; ++ default: ++ (*putc)('?'); ++ } ++ pad = left_prec - length; ++ if (sign != '\0') ++ { ++ pad--; ++ } ++ if (zero_fill) ++ { ++ c = '0'; ++ if (sign != '\0') ++ { ++ (*putc)(sign); ++ sign = '\0'; ++ } ++ } else ++ { ++ c = ' '; ++ } ++ if (!pad_on_right) ++ { ++ while (pad-- > 0) ++ { ++ (*putc)(c); ++ } ++ } ++ if (sign != '\0') ++ { ++ (*putc)(sign); ++ } ++ while (length-- > 0) ++ { ++ (*putc)(c = *cp++); ++ if (c == '\n') ++ { ++ (*putc)('\r'); ++ } ++ } ++ if (pad_on_right) ++ { ++ while (pad-- > 0) ++ { ++ (*putc)(c); ++ } ++ } ++ } else ++ { ++ (*putc)(c); ++ if (c == '\n') ++ { ++ (*putc)('\r'); ++ } ++ } ++ } ++} ++ ++int ++_cvt(unsigned long val, char *buf, long radix, char *digits) ++{ ++ char temp[80]; ++ char *cp = temp; ++ int length = 0; ++ if (val == 0) ++ { /* Special case */ ++ *cp++ = '0'; ++ } else ++ while (val) ++ { ++ *cp++ = digits[val % radix]; ++ val /= radix; ++ } ++ while (cp != temp) ++ { ++ *buf++ = *--cp; ++ length++; ++ } ++ *buf = '\0'; ++ return (length); ++} ++ ++void ++_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base) ++{ ++ int i, c; ++ if ((unsigned int)s > (unsigned int)p) ++ { ++ s = (unsigned int)s - (unsigned int)p; ++ } ++ while (s > 0) ++ { ++ if (base) ++ { ++ _printk("%06X: ", (int)p - (int)base); ++ } else ++ { ++ _printk("%06X: ", p); ++ } ++ for (i = 0; i < 16; i++) ++ { ++ if (i < s) ++ { ++ _printk("%02X", p[i] & 0xFF); ++ } else ++ { ++ _printk(" "); ++ } ++ if ((i % 2) == 1) _printk(" "); ++ if ((i % 8) == 7) _printk(" "); ++ } ++ _printk(" |"); ++ for (i = 0; i < 16; i++) ++ { ++ if (i < s) ++ { ++ c = p[i] & 0xFF; ++ if ((c < 0x20) || (c >= 0x7F)) c = '.'; ++ } else ++ { ++ c = ' '; ++ } ++ _printk("%c", c); ++ } ++ _printk("|\n"); ++ s -= 16; ++ p += 16; ++ } ++} ++ ++void ++_dump_buf(unsigned char *p, int s) ++{ ++ _printk("\n"); ++ _dump_buf_with_offset(p, s, 0); ++} ++ ++/* ++ * Local variables: ++ * c-indent-level: 8 ++ * c-basic-offset: 8 ++ * tab-width: 8 ++ * End: ++ */ +diff -Naru linux/arch/mips/zboot/common/misc-simple.c linux.spi/arch/mips/zboot/common/misc-simple.c +--- linux/arch/mips/zboot/common/misc-simple.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/misc-simple.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,127 @@ ++/* ++ * arch/mips/zboot/common/misc-simple.c ++ * ++ * Misc. bootloader code for many machines. This assumes you have are using ++ * a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory ++ * below 8MB is free. Finally, it assumes you have a NS16550-style uart for ++ * your serial console. If a machine meets these requirements, it can quite ++ * likely use this code during boot. ++ * ++ * Author: Matt Porter ++ * Derived from arch/ppc/boot/prep/misc.c ++ * ++ * Copyright 2001 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "zlib.h" ++ ++extern struct NS16550 *com_port; ++ ++char *avail_ram; ++char *end_avail; ++extern char _end[]; ++char *zimage_start; ++ ++#ifdef CONFIG_CMDLINE ++#define CMDLINE CONFIG_CMDLINE ++#else ++#define CMDLINE "" ++#endif ++char cmd_preset[] = CMDLINE; ++char cmd_buf[256]; ++char *cmd_line = cmd_buf; ++ ++/* The linker tells us where the image is. ++*/ ++extern unsigned char __image_begin, __image_end; ++extern unsigned char __ramdisk_begin, __ramdisk_end; ++unsigned long initrd_size; ++ ++extern void puts(const char *); ++extern void putc(const char c); ++extern void puthex(unsigned long val); ++extern void *memcpy(void * __dest, __const void * __src, ++ __kernel_size_t __n); ++extern void gunzip(void *, int, unsigned char *, int *); ++extern void udelay(long delay); ++extern int tstc(void); ++extern int getc(void); ++extern volatile struct NS16550 *serial_init(int chan); ++ ++void ++decompress_kernel(unsigned long load_addr, int num_words, ++ unsigned long cksum, unsigned long *sp) ++{ ++ int timer = 0; ++ extern unsigned long start; ++ char *cp, ch; ++ int i; ++ int zimage_size; ++ ++ com_port = (struct NS16550 *)serial_init(0); ++ ++ initrd_size = (unsigned long)(&__ramdisk_end) - ++ (unsigned long)(&__ramdisk_begin); ++ ++ /* ++ * Reveal where we were loaded at and where we ++ * were relocated to. ++ */ ++ puts("loaded at: "); puthex(load_addr); ++ puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); ++ if ( (unsigned long)load_addr != (unsigned long)&start ) ++ { ++ puts("relocated to: "); puthex((unsigned long)&start); ++ puts(" "); ++ puthex((unsigned long)((unsigned long)&start + (4*num_words))); ++ puts("\n"); ++ } ++ ++ /* ++ * We link ourself to an arbitrary low address. When we run, we ++ * relocate outself to that address. __image_being points to ++ * the part of the image where the zImage is. -- Tom ++ */ ++ zimage_start = (char *)(unsigned long)(&__image_begin); ++ zimage_size = (unsigned long)(&__image_end) - ++ (unsigned long)(&__image_begin); ++ ++ /* ++ * The zImage and initrd will be between start and _end, so they've ++ * already been moved once. We're good to go now. -- Tom ++ */ ++ puts("zimage at: "); puthex((unsigned long)zimage_start); ++ puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); ++ puts("\n"); ++ ++ if ( initrd_size ) { ++ puts("initrd at: "); ++ puthex((unsigned long)(&__ramdisk_begin)); ++ puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); ++ } ++ ++ /* assume the chunk below 8M is free */ ++ avail_ram = (char *)AVAIL_RAM_START; ++ end_avail = (char *)AVAIL_RAM_END; ++ ++ /* Display standard Linux/MIPS boot prompt for kernel args */ ++ puts("Uncompressing Linux at load address "); ++ puthex(LOADADDR); ++ puts("\n"); ++ /* I don't like this hard coded gunzip size (fixme) */ ++ gunzip((void *)LOADADDR, 0x400000, zimage_start, &zimage_size); ++ puts("Now booting the kernel\n"); ++} +diff -Naru linux/arch/mips/zboot/common/no_initrd.c linux.spi/arch/mips/zboot/common/no_initrd.c +--- linux/arch/mips/zboot/common/no_initrd.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/no_initrd.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,2 @@ ++char initrd_data[1]; ++int initrd_len = 0; +diff -Naru linux/arch/mips/zboot/common/ns16550.c linux.spi/arch/mips/zboot/common/ns16550.c +--- linux/arch/mips/zboot/common/ns16550.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/ns16550.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,57 @@ ++/* ++ * NS16550 support ++ */ ++ ++#include ++#include ++#include "ns16550.h" ++ ++typedef struct NS16550 *NS16550_t; ++ ++const NS16550_t COM_PORTS[] = { (NS16550_t) COM1, ++ (NS16550_t) COM2, ++ (NS16550_t) COM3, ++ (NS16550_t) COM4 }; ++ ++volatile struct NS16550 * ++serial_init(int chan) ++{ ++ volatile struct NS16550 *com_port; ++ com_port = (struct NS16550 *) COM_PORTS[chan]; ++ /* See if port is present */ ++ com_port->lcr = 0x00; ++ com_port->ier = 0xFF; ++#if 0 ++ if (com_port->ier != 0x0F) return ((struct NS16550 *)0); ++#endif ++ com_port->ier = 0x00; ++ com_port->lcr = 0x80; /* Access baud rate */ ++#ifdef CONFIG_SERIAL_CONSOLE_NONSTD ++ com_port->dll = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD); ++ com_port->dlm = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8; ++#endif ++ com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */ ++ com_port->mcr = 0x03; /* RTS/DTR */ ++ com_port->fcr = 0x07; /* Clear & enable FIFOs */ ++ return (com_port); ++} ++ ++void ++serial_putc(volatile struct NS16550 *com_port, unsigned char c) ++{ ++ while ((com_port->lsr & LSR_THRE) == 0) ; ++ com_port->thr = c; ++} ++ ++unsigned char ++serial_getc(volatile struct NS16550 *com_port) ++{ ++ while ((com_port->lsr & LSR_DR) == 0) ; ++ return (com_port->rbr); ++} ++ ++int ++serial_tstc(volatile struct NS16550 *com_port) ++{ ++ return ((com_port->lsr & LSR_DR) != 0); ++} +diff -Naru linux/arch/mips/zboot/common/string.c linux.spi/arch/mips/zboot/common/string.c +--- linux/arch/mips/zboot/common/string.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/common/string.c 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,497 @@ ++/* ++ * linux/lib/string.c ++ * ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ */ ++ ++/* ++ * stupid library routines.. The optimized versions should generally be found ++ * as inline code in ++ * ++ * These are buggy as well.. ++ * ++ * * Fri Jun 25 1999, Ingo Oeser ++ * - Added strsep() which will replace strtok() soon (because strsep() is ++ * reentrant and should be faster). Use only strsep() in new code, please. ++ */ ++ ++#include ++#include ++#include ++ ++/** ++ * strnicmp - Case insensitive, length-limited string comparison ++ * @s1: One string ++ * @s2: The other string ++ * @len: the maximum number of characters to compare ++ */ ++int strnicmp(const char *s1, const char *s2, size_t len) ++{ ++ /* Yes, Virginia, it had better be unsigned */ ++ unsigned char c1, c2; ++ ++ c1 = 0; c2 = 0; ++ if (len) { ++ do { ++ c1 = *s1; c2 = *s2; ++ s1++; s2++; ++ if (!c1) ++ break; ++ if (!c2) ++ break; ++ if (c1 == c2) ++ continue; ++ c1 = tolower(c1); ++ c2 = tolower(c2); ++ if (c1 != c2) ++ break; ++ } while (--len); ++ } ++ return (int)c1 - (int)c2; ++} ++ ++char * ___strtok; ++ ++#ifndef __HAVE_ARCH_STRCPY ++/** ++ * strcpy - Copy a %NUL terminated string ++ * @dest: Where to copy the string to ++ * @src: Where to copy the string from ++ */ ++char * strcpy(char * dest,const char *src) ++{ ++ char *tmp = dest; ++ ++ while ((*dest++ = *src++) != '\0') ++ /* nothing */; ++ return tmp; ++} ++#endif ++ ++#ifndef __HAVE_ARCH_STRNCPY ++/** ++ * strncpy - Copy a length-limited, %NUL-terminated string ++ * @dest: Where to copy the string to ++ * @src: Where to copy the string from ++ * @count: The maximum number of bytes to copy ++ * ++ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer. ++ * However, the result is not %NUL-terminated if the source exceeds ++ * @count bytes. ++ */ ++char * strncpy(char * dest,const char *src,size_t count) ++{ ++ char *tmp = dest; ++ ++ while (count-- && (*dest++ = *src++) != '\0') ++ /* nothing */; ++ ++ return tmp; ++} ++#endif ++ ++/** ++ * strcat - Append one %NUL-terminated string to another ++ * @dest: The string to be appended to ++ * @src: The string to append to it ++ */ ++char * strcat(char * dest, const char * src) ++{ ++ char *tmp = dest; ++ ++ while (*dest) ++ dest++; ++ while ((*dest++ = *src++) != '\0') ++ ; ++ ++ return tmp; ++} ++ ++/** ++ * strncat - Append a length-limited, %NUL-terminated string to another ++ * @dest: The string to be appended to ++ * @src: The string to append to it ++ * @count: The maximum numbers of bytes to copy ++ * ++ * Note that in contrast to strncpy, strncat ensures the result is ++ * terminated. ++ */ ++char * strncat(char *dest, const char *src, size_t count) ++{ ++ char *tmp = dest; ++ ++ if (count) { ++ while (*dest) ++ dest++; ++ while ((*dest++ = *src++)) { ++ if (--count == 0) { ++ *dest = '\0'; ++ break; ++ } ++ } ++ } ++ ++ return tmp; ++} ++ ++#ifndef __HAVE_ARCH_STRCMP ++/** ++ * strcmp - Compare two strings ++ * @cs: One string ++ * @ct: Another string ++ */ ++int strcmp(const char * cs,const char * ct) ++{ ++ register signed char __res; ++ ++ while (1) { ++ if ((__res = *cs - *ct++) != 0 || !*cs++) ++ break; ++ } ++ ++ return __res; ++} ++#endif ++ ++#ifndef __HAVE_ARCH_STRNCMP ++/** ++ * strncmp - Compare two length-limited strings ++ * @cs: One string ++ * @ct: Another string ++ * @count: The maximum number of bytes to compare ++ */ ++int strncmp(const char * cs,const char * ct,size_t count) ++{ ++ register signed char __res = 0; ++ ++ while (count) { ++ if ((__res = *cs - *ct++) != 0 || !*cs++) ++ break; ++ count--; ++ } ++ ++ return __res; ++} ++#endif ++ ++/** ++ * strchr - Find the first occurrence of a character in a string ++ * @s: The string to be searched ++ * @c: The character to search for ++ */ ++char * strchr(const char * s, int c) ++{ ++ for(; *s != (char) c; ++s) ++ if (*s == '\0') ++ return NULL; ++ return (char *) s; ++} ++ ++/** ++ * strrchr - Find the last occurrence of a character in a string ++ * @s: The string to be searched ++ * @c: The character to search for ++ */ ++char * strrchr(const char * s, int c) ++{ ++ const char *p = s + strlen(s); ++ do { ++ if (*p == (char)c) ++ return (char *)p; ++ } while (--p >= s); ++ return NULL; ++} ++ ++/** ++ * strlen - Find the length of a string ++ * @s: The string to be sized ++ */ ++size_t strlen(const char * s) ++{ ++ const char *sc; ++ ++ for (sc = s; *sc != '\0'; ++sc) ++ /* nothing */; ++ return sc - s; ++} ++ ++/** ++ * strnlen - Find the length of a length-limited string ++ * @s: The string to be sized ++ * @count: The maximum number of bytes to search ++ */ ++size_t strnlen(const char * s, size_t count) ++{ ++ const char *sc; ++ ++ for (sc = s; count-- && *sc != '\0'; ++sc) ++ /* nothing */; ++ return sc - s; ++} ++ ++/** ++ * strspn - Calculate the length of the initial substring of @s which only ++ * contain letters in @accept ++ * @s: The string to be searched ++ * @accept: The string to search for ++ */ ++size_t strspn(const char *s, const char *accept) ++{ ++ const char *p; ++ const char *a; ++ size_t count = 0; ++ ++ for (p = s; *p != '\0'; ++p) { ++ for (a = accept; *a != '\0'; ++a) { ++ if (*p == *a) ++ break; ++ } ++ if (*a == '\0') ++ return count; ++ ++count; ++ } ++ ++ return count; ++} ++ ++/** ++ * strpbrk - Find the first occurrence of a set of characters ++ * @cs: The string to be searched ++ * @ct: The characters to search for ++ */ ++char * strpbrk(const char * cs,const char * ct) ++{ ++ const char *sc1,*sc2; ++ ++ for( sc1 = cs; *sc1 != '\0'; ++sc1) { ++ for( sc2 = ct; *sc2 != '\0'; ++sc2) { ++ if (*sc1 == *sc2) ++ return (char *) sc1; ++ } ++ } ++ return NULL; ++} ++ ++/** ++ * strtok - Split a string into tokens ++ * @s: The string to be searched ++ * @ct: The characters to search for ++ * ++ * WARNING: strtok is deprecated, use strsep instead. ++ */ ++char * strtok(char * s,const char * ct) ++{ ++ char *sbegin, *send; ++ ++ sbegin = s ? s : ___strtok; ++ if (!sbegin) { ++ return NULL; ++ } ++ sbegin += strspn(sbegin,ct); ++ if (*sbegin == '\0') { ++ ___strtok = NULL; ++ return( NULL ); ++ } ++ send = strpbrk( sbegin, ct); ++ if (send && *send != '\0') ++ *send++ = '\0'; ++ ___strtok = send; ++ return (sbegin); ++} ++ ++/** ++ * strsep - Split a string into tokens ++ * @s: The string to be searched ++ * @ct: The characters to search for ++ * ++ * strsep() updates @s to point after the token, ready for the next call. ++ * ++ * It returns empty tokens, too, behaving exactly like the libc function ++ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied. ++ * Same semantics, slimmer shape. ;) ++ */ ++char * strsep(char **s, const char *ct) ++{ ++ char *sbegin = *s, *end; ++ ++ if (sbegin == NULL) ++ return NULL; ++ ++ end = strpbrk(sbegin, ct); ++ if (end) ++ *end++ = '\0'; ++ *s = end; ++ ++ return sbegin; ++} ++ ++/** ++ * memset - Fill a region of memory with the given value ++ * @s: Pointer to the start of the area. ++ * @c: The byte to fill the area with ++ * @count: The size of the area. ++ * ++ * Do not use memset() to access IO space, use memset_io() instead. ++ */ ++void * memset(void * s,int c, size_t count) ++{ ++ char *xs = (char *) s; ++ ++ while (count--) ++ *xs++ = c; ++ ++ return s; ++} ++ ++/** ++ * bcopy - Copy one area of memory to another ++ * @src: Where to copy from ++ * @dest: Where to copy to ++ * @count: The size of the area. ++ * ++ * Note that this is the same as memcpy(), with the arguments reversed. ++ * memcpy() is the standard, bcopy() is a legacy BSD function. ++ * ++ * You should not use this function to access IO space, use memcpy_toio() ++ * or memcpy_fromio() instead. ++ */ ++char * bcopy(const char * src, char * dest, int count) ++{ ++ char *tmp = dest; ++ ++ while (count--) ++ *tmp++ = *src++; ++ ++ return dest; ++} ++ ++/** ++ * memcpy - Copy one area of memory to another ++ * @dest: Where to copy to ++ * @src: Where to copy from ++ * @count: The size of the area. ++ * ++ * You should not use this function to access IO space, use memcpy_toio() ++ * or memcpy_fromio() instead. ++ */ ++void * memcpy(void * dest,const void *src,size_t count) ++{ ++ char *tmp = (char *) dest, *s = (char *) src; ++ ++ while (count--) ++ *tmp++ = *s++; ++ ++ return dest; ++} ++ ++/** ++ * memmove - Copy one area of memory to another ++ * @dest: Where to copy to ++ * @src: Where to copy from ++ * @count: The size of the area. ++ * ++ * Unlike memcpy(), memmove() copes with overlapping areas. ++ */ ++void * memmove(void * dest,const void *src,size_t count) ++{ ++ char *tmp, *s; ++ ++ if (dest <= src) { ++ tmp = (char *) dest; ++ s = (char *) src; ++ while (count--) ++ *tmp++ = *s++; ++ } ++ else { ++ tmp = (char *) dest + count; ++ s = (char *) src + count; ++ while (count--) ++ *--tmp = *--s; ++ } ++ ++ return dest; ++} ++ ++/** ++ * memcmp - Compare two areas of memory ++ * @cs: One area of memory ++ * @ct: Another area of memory ++ * @count: The size of the area. ++ */ ++int memcmp(const void * cs,const void * ct,size_t count) ++{ ++ const unsigned char *su1, *su2; ++ signed char res = 0; ++ ++ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) ++ if ((res = *su1 - *su2) != 0) ++ break; ++ return res; ++} ++ ++#ifndef __HAVE_ARCH_MEMSCAN ++/** ++ * memscan - Find a character in an area of memory. ++ * @addr: The memory area ++ * @c: The byte to search for ++ * @size: The size of the area. ++ * ++ * returns the address of the first occurrence of @c, or 1 byte past ++ * the area if @c is not found ++ */ ++void * memscan(void * addr, int c, size_t size) ++{ ++ unsigned char * p = (unsigned char *) addr; ++ unsigned char * e = p + size; ++ ++ while (p != e) { ++ if (*p == c) ++ return (void *) p; ++ p++; ++ } ++ ++ return (void *) p; ++} ++#endif ++ ++/** ++ * strstr - Find the first substring in a %NUL terminated string ++ * @s1: The string to be searched ++ * @s2: The string to search for ++ */ ++char * strstr(const char * s1,const char * s2) ++{ ++ int l1, l2; ++ ++ l2 = strlen(s2); ++ if (!l2) ++ return (char *) s1; ++ l1 = strlen(s1); ++ while (l1 >= l2) { ++ l1--; ++ if (!memcmp(s1,s2,l2)) ++ return (char *) s1; ++ s1++; ++ } ++ return NULL; ++} ++ ++/** ++ * memchr - Find a character in an area of memory. ++ * @s: The memory area ++ * @c: The byte to search for ++ * @n: The size of the area. ++ * ++ * returns the address of the first occurrence of @c, or %NULL ++ * if @c is not found ++ */ ++void *memchr(const void *s, int c, size_t n) ++{ ++ const unsigned char *p = s; ++ while (n-- != 0) { ++ if ((unsigned char)c == *p++) { ++ return (void *)(p-1); ++ } ++ } ++ return NULL; ++} +diff -Naru linux/arch/mips/zboot/csb250/head.S linux.spi/arch/mips/zboot/csb250/head.S +--- linux/arch/mips/zboot/csb250/head.S 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/csb250/head.S 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,157 @@ ++/* ++ * arch/mips/kernel/head.S ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 1994, 1995 Waldorf Electronics ++ * Written by Ralf Baechle and Andreas Busse ++ * Copyright (C) 1995 - 1999 Ralf Baechle ++ * Copyright (C) 1996 Paul M. Antoine ++ * Modified for DECStation and hence R3000 support by Paul M. Antoine ++ * Further modifications by David S. Miller and Harald Koerfgen ++ * Copyright (C) 1999 Silicon Graphics, Inc. ++ * ++ * Head.S contains the MIPS exception handler and startup code. ++ * ++ ************************************************************************** ++ * 9 Nov, 2000. ++ * Added Cache Error exception handler and SBDDP EJTAG debug exception. ++ * ++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. ++ ************************************************************************** ++ */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IndexInvalidate_I 0x00 ++#define IndexWriteBack_D 0x01 ++ ++ .set noreorder ++ .cprestore ++ LEAF(start) ++start: ++ bal locate ++ nop ++ ++ .globl asize /* Someday we'll put the initrd info here. */ ++asize: .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ ++locate: ++ subu s8, ra, 8 /* Where we were loaded */ ++ la sp, (.stack + 8192) ++ ++ move s0, a0 /* Save boot rom start args */ ++ move s1, a1 ++ move s2, a2 ++ move s3, a3 ++ ++ la a0, start /* Where we were linked to run */ ++ ++ move a1, s8 ++ la a2, _edata ++ subu t1, a2, a0 ++ srl t1, t1, 2 ++ ++ /* copy text section */ ++ li t0, 0 ++1: lw v0, 0(a1) ++ nop ++ sw v0, 0(a0) ++ xor t0, t0, v0 ++ addu a0, 4 ++ bne a2, a0, 1b ++ addu a1, 4 ++ ++ /* Clear BSS */ ++ la a0, _edata ++ la a2, _end ++2: sw zero, 0(a0) ++ bne a2, a0, 2b ++ addu a0, 4 ++ ++ /* push the D-Cache and invalidate I-Cache */ ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexWriteBack_D, 0(k0) ++ cache IndexWriteBack_D, 32(k0) ++ cache IndexWriteBack_D, 64(k0) ++ cache IndexWriteBack_D, 96(k0) ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ /* done */ ++ ++/* move a0, s8 /* load address */ ++ subu a0, s8, 0x1000 /* load address */ ++ move a1, t1 /* length in words */ ++ move a2, t0 /* checksum */ ++ move a3, sp ++ ++ la ra, 1f ++ la k0, decompress_kernel ++ jr k0 ++ nop ++1: ++ ++ la a2, __ramdisk_begin ++ la a3, initrd_size ++ lw a0, 0(a2) ++ lw a1, 0(a3) ++ li k0, KERNEL_ENTRY ++ jr k0 ++ nop ++3: ++ b 3b ++ END(start) ++ ++ LEAF(udelay) ++udelay: ++ END(udelay) ++ ++ ++ LEAF(FlushCache) ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexWriteBack_D, 0(k0) ++ cache IndexWriteBack_D, 32(k0) ++ cache IndexWriteBack_D, 64(k0) ++ cache IndexWriteBack_D, 96(k0) ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ jr ra ++ nop ++ END(FlushCache) ++ ++ .comm .stack,4096*2,4 +diff -Naru linux/arch/mips/zboot/csb250/Makefile linux.spi/arch/mips/zboot/csb250/Makefile +--- linux/arch/mips/zboot/csb250/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/csb250/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,90 @@ ++# arch/mips/zboot/pb1xxx/Makefile ++# ++# Makefile for Cogent CSB250 Au1500 board. ++# All of the boot loader code was derived from the ppc ++# boot code. ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. ++ ++.c.s: ++ $(CC) $(CFLAGS) -S -o $*.s $< ++.s.o: ++ $(AS) -o $*.o $< ++.c.o: ++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $< ++.S.s: ++ $(CPP) $(AFLAGS) -o $*.o $< ++.S.o: ++ $(CC) $(AFLAGS) -c -o $*.o $< ++ ++######################################################################### ++# START BOARD SPECIFIC VARIABLES ++BNAME=csb250 ++ ++# These two variables control where the zImage is stored ++# in flash and loaded in memory. It only controls how the srec ++# file is generated, the code is the same. ++RAM_RUN_ADDR = 0x80a00000 ++FLASH_LOAD_ADDR = 0xBFD00000 ++ ++# These two variables specify the free ram region ++# that can be used for temporary malloc area ++AVAIL_RAM_START=0x80400000 ++AVAIL_RAM_END=0x80800000 ++ ++# This one must match the LOADADDR in arch/mips/Makefile! ++LOADADDR=0x80100000 ++# END BOARD SPECIFIC VARIABLES ++######################################################################### ++ ++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \ ++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o ++LIBS := ../lib/zlib.a ++ ++ENTRY := ../utils/entry ++OFFSET := ../utils/offset ++SIZE := ../utils/size ++ ++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic ++OBJCOPY_ARGS = -O elf32-tradbigmips ++ ++all: zImage ++ ++clean: ++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec ++ ++head.o: head.S $(TOPDIR)/vmlinux ++ $(CC) $(AFLAGS) \ ++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \ ++ -c -o $*.o $< ++ ++../common/misc-simple.o: ++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \ ++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \ ++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \ ++ -DLOADADDR=$(LOADADDR) \ ++ -DZIMAGE_SIZE=0 -c -o $@ $*.c ++ ++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o ++ $(OBJCOPY) \ ++ --add-section=.image=../images/vmlinux.gz \ ++ --set-section-flags=.image=contents,alloc,load,readonly,data \ ++ ../common/dummy.o image.o ++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS) ++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \ ++ -R .initrd -R .sysmap ++ ++# Here we manipulate the image in order to get it the necessary ++# srecord file we need. ++zImage: zvmlinux ++ mv zvmlinux ../images/zImage.$(BNAME) ++ $(OBJCOPY) -O binary ../images/zImage.$(BNAME) ../images/$(BNAME).bin ++ ++zImage.flash: zImage ++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \ ++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec ++ ++include $(TOPDIR)/Rules.make +diff -Naru linux/arch/mips/zboot/images/Makefile linux.spi/arch/mips/zboot/images/Makefile +--- linux/arch/mips/zboot/images/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/images/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,10 @@ ++ ++include $(TOPDIR)/Rules.make ++ ++vmlinux.gz: $(TOPDIR)/vmlinux ++ $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux ++ gzip -vf vmlinux ++ ++clean: ++ rm -f vmlinux.* zImage.* ++ +diff -Naru linux/arch/mips/zboot/include/nonstdio.h linux.spi/arch/mips/zboot/include/nonstdio.h +--- linux/arch/mips/zboot/include/nonstdio.h 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/include/nonstdio.h 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,18 @@ ++/* ++ * Copyright (C) Paul Mackerras 1997. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++typedef int FILE; ++extern FILE *stdin, *stdout; ++#define NULL ((void *)0) ++#define EOF (-1) ++#define fopen(n, m) NULL ++#define fflush(f) 0 ++#define fclose(f) 0 ++extern char *fgets(); ++ ++#define perror(s) printf("%s: no files!\n", (s)) +diff -Naru linux/arch/mips/zboot/include/ns16550.h linux.spi/arch/mips/zboot/include/ns16550.h +--- linux/arch/mips/zboot/include/ns16550.h 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/include/ns16550.h 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,49 @@ ++/* ++ * NS16550 Serial Port ++ */ ++ ++/* ++ * Figure out which file will have the definitons of COMx ++ */ ++#if !defined(CONFIG_AU1X00_UART) ++#error no serial.h ++#endif ++ ++/* Some machines have their uart registers 16 bytes apart. Most don't. ++ * TODO: Make this work like drivers/char/serial does - Tom */ ++#if !defined(UART_REG_PAD) ++#define UART_REG_PAD(x) ++#endif ++ ++struct NS16550 ++ { ++ unsigned char rbr; /* 0 */ ++ UART_REG_PAD(rbr) ++ unsigned char ier; /* 1 */ ++ UART_REG_PAD(ier) ++ unsigned char fcr; /* 2 */ ++ UART_REG_PAD(fcr) ++ unsigned char lcr; /* 3 */ ++ UART_REG_PAD(lcr) ++ unsigned char mcr; /* 4 */ ++ UART_REG_PAD(mcr) ++ unsigned char lsr; /* 5 */ ++ UART_REG_PAD(lsr) ++ unsigned char msr; /* 6 */ ++ UART_REG_PAD(msr) ++ unsigned char scr; /* 7 */ ++ }; ++ ++#define thr rbr ++#define iir fcr ++#define dll rbr ++#define dlm ier ++ ++#define LSR_DR 0x01 /* Data ready */ ++#define LSR_OE 0x02 /* Overrun */ ++#define LSR_PE 0x04 /* Parity error */ ++#define LSR_FE 0x08 /* Framing error */ ++#define LSR_BI 0x10 /* Break */ ++#define LSR_THRE 0x20 /* Xmit holding register empty */ ++#define LSR_TEMT 0x40 /* Xmitter empty */ ++#define LSR_ERR 0x80 /* Error */ +diff -Naru linux/arch/mips/zboot/include/pb1000_serial.h linux.spi/arch/mips/zboot/include/pb1000_serial.h +--- linux/arch/mips/zboot/include/pb1000_serial.h 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/include/pb1000_serial.h 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,20 @@ ++/* ++ * arch/ppc/boot/include/sandpoint_serial.h ++ * ++ * Location of the COM ports on Motorola SPS Sandpoint machines ++ * ++ * Author: Mark A. Greer ++ * mgreer@mvista.com ++ * ++ * Copyright 2001 MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#define COM1 0xfe0003f8 ++#define COM2 0xfe0002f8 ++#define COM3 0x00000000 /* No COM3 */ ++#define COM4 0x00000000 /* No COM4 */ +diff -Naru linux/arch/mips/zboot/include/zlib.h linux.spi/arch/mips/zboot/include/zlib.h +--- linux/arch/mips/zboot/include/zlib.h 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/include/zlib.h 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,432 @@ ++/* $Id: zlib.h,v 1.2 2002/02/16 16:55:45 ppopov Exp $ */ ++ ++/* ++ * This file is derived from zlib.h and zconf.h from the zlib-0.95 ++ * distribution by Jean-loup Gailly and Mark Adler, with some additions ++ * by Paul Mackerras to aid in implementing Deflate compression and ++ * decompression for PPP packets. ++ */ ++ ++/* ++ * ==FILEVERSION 960122== ++ * ++ * This marker is used by the Linux installation script to determine ++ * whether an up-to-date version of this file is already installed. ++ */ ++ ++/* zlib.h -- interface of the 'zlib' general purpose compression library ++ version 0.95, Aug 16th, 1995. ++ ++ Copyright (C) 1995 Jean-loup Gailly and Mark Adler ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++ ++ Jean-loup Gailly Mark Adler ++ gzip@prep.ai.mit.edu madler@alumni.caltech.edu ++ */ ++ ++#ifndef _ZLIB_H ++#define _ZLIB_H ++ ++/* #include "zconf.h" */ /* included directly here */ ++ ++/* zconf.h -- configuration of the zlib compression library ++ * Copyright (C) 1995 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ ++ ++/* ++ The library does not install any signal handler. It is recommended to ++ add at least a handler for SIGSEGV when decompressing; the library checks ++ the consistency of the input data whenever possible but may go nuts ++ for some forms of corrupted input. ++ */ ++ ++/* ++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more ++ * than 64k bytes at a time (needed on systems with 16-bit int). ++ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints ++ * at addresses which are not a multiple of their size. ++ * Under DOS, -DFAR=far or -DFAR=__far may be needed. ++ */ ++ ++#ifndef STDC ++# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) ++# define STDC ++# endif ++#endif ++ ++#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ ++# include ++#endif ++ ++/* Maximum value for memLevel in deflateInit2 */ ++#ifndef MAX_MEM_LEVEL ++# ifdef MAXSEG_64K ++# define MAX_MEM_LEVEL 8 ++# else ++# define MAX_MEM_LEVEL 9 ++# endif ++#endif ++ ++#ifndef FAR ++# define FAR ++#endif ++ ++/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ ++#ifndef MAX_WBITS ++# define MAX_WBITS 15 /* 32K LZ77 window */ ++#endif ++ ++/* The memory requirements for deflate are (in bytes): ++ 1 << (windowBits+2) + 1 << (memLevel+9) ++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) ++ plus a few kilobytes for small objects. For example, if you want to reduce ++ the default memory requirements from 256K to 128K, compile with ++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" ++ Of course this will generally degrade compression (there's no free lunch). ++ ++ The memory requirements for inflate are (in bytes) 1 << windowBits ++ that is, 32K for windowBits=15 (default value) plus a few kilobytes ++ for small objects. ++*/ ++ ++ /* Type declarations */ ++ ++#ifndef OF /* function prototypes */ ++# ifdef STDC ++# define OF(args) args ++# else ++# define OF(args) () ++# endif ++#endif ++ ++typedef unsigned char Byte; /* 8 bits */ ++typedef unsigned int uInt; /* 16 bits or more */ ++typedef unsigned long uLong; /* 32 bits or more */ ++ ++typedef Byte FAR Bytef; ++typedef char FAR charf; ++typedef int FAR intf; ++typedef uInt FAR uIntf; ++typedef uLong FAR uLongf; ++ ++#ifdef STDC ++ typedef void FAR *voidpf; ++ typedef void *voidp; ++#else ++ typedef Byte FAR *voidpf; ++ typedef Byte *voidp; ++#endif ++ ++/* end of original zconf.h */ ++ ++#define ZLIB_VERSION "0.95P" ++ ++/* ++ The 'zlib' compression library provides in-memory compression and ++ decompression functions, including integrity checks of the uncompressed ++ data. This version of the library supports only one compression method ++ (deflation) but other algorithms may be added later and will have the same ++ stream interface. ++ ++ For compression the application must provide the output buffer and ++ may optionally provide the input buffer for optimization. For decompression, ++ the application must provide the input buffer and may optionally provide ++ the output buffer for optimization. ++ ++ Compression can be done in a single step if the buffers are large ++ enough (for example if an input file is mmap'ed), or can be done by ++ repeated calls of the compression function. In the latter case, the ++ application must provide more input and/or consume the output ++ (providing more output space) before each call. ++*/ ++ ++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); ++typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); ++ ++struct internal_state; ++ ++typedef struct z_stream_s { ++ Bytef *next_in; /* next input byte */ ++ uInt avail_in; /* number of bytes available at next_in */ ++ uLong total_in; /* total nb of input bytes read so far */ ++ ++ Bytef *next_out; /* next output byte should be put there */ ++ uInt avail_out; /* remaining free space at next_out */ ++ uLong total_out; /* total nb of bytes output so far */ ++ ++ char *msg; /* last error message, NULL if no error */ ++ struct internal_state FAR *state; /* not visible by applications */ ++ ++ alloc_func zalloc; /* used to allocate the internal state */ ++ free_func zfree; /* used to free the internal state */ ++ voidp opaque; /* private data object passed to zalloc and zfree */ ++ ++ Byte data_type; /* best guess about the data type: ascii or binary */ ++ ++} z_stream; ++ ++/* ++ The application must update next_in and avail_in when avail_in has ++ dropped to zero. It must update next_out and avail_out when avail_out ++ has dropped to zero. The application must initialize zalloc, zfree and ++ opaque before calling the init function. All other fields are set by the ++ compression library and must not be updated by the application. ++ ++ The opaque value provided by the application will be passed as the first ++ parameter for calls of zalloc and zfree. This can be useful for custom ++ memory management. The compression library attaches no meaning to the ++ opaque value. ++ ++ zalloc must return Z_NULL if there is not enough memory for the object. ++ On 16-bit systems, the functions zalloc and zfree must be able to allocate ++ exactly 65536 bytes, but will not be required to allocate more than this ++ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, ++ pointers returned by zalloc for objects of exactly 65536 bytes *must* ++ have their offset normalized to zero. The default allocation function ++ provided by this library ensures this (see zutil.c). To reduce memory ++ requirements and avoid any allocation of 64K objects, at the expense of ++ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). ++ ++ The fields total_in and total_out can be used for statistics or ++ progress reports. After compression, total_in holds the total size of ++ the uncompressed data and may be saved for use in the decompressor ++ (particularly if the decompressor wants to decompress everything in ++ a single step). ++*/ ++ ++ /* constants */ ++ ++#define Z_NO_FLUSH 0 ++#define Z_PARTIAL_FLUSH 1 ++#define Z_FULL_FLUSH 2 ++#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ ++#define Z_FINISH 4 ++#define Z_PACKET_FLUSH 5 ++/* See deflate() below for the usage of these constants */ ++ ++#define Z_OK 0 ++#define Z_STREAM_END 1 ++#define Z_ERRNO (-1) ++#define Z_STREAM_ERROR (-2) ++#define Z_DATA_ERROR (-3) ++#define Z_MEM_ERROR (-4) ++#define Z_BUF_ERROR (-5) ++/* error codes for the compression/decompression functions */ ++ ++#define Z_BEST_SPEED 1 ++#define Z_BEST_COMPRESSION 9 ++#define Z_DEFAULT_COMPRESSION (-1) ++/* compression levels */ ++ ++#define Z_FILTERED 1 ++#define Z_HUFFMAN_ONLY 2 ++#define Z_DEFAULT_STRATEGY 0 ++ ++#define Z_BINARY 0 ++#define Z_ASCII 1 ++#define Z_UNKNOWN 2 ++/* Used to set the data_type field */ ++ ++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ ++ ++extern char *zlib_version; ++/* The application can compare zlib_version and ZLIB_VERSION for consistency. ++ If the first character differs, the library code actually used is ++ not compatible with the zlib.h header file used by the application. ++ */ ++ ++ /* basic functions */ ++ ++extern int inflateInit OF((z_stream *strm)); ++/* ++ Initializes the internal stream state for decompression. The fields ++ zalloc and zfree must be initialized before by the caller. If zalloc and ++ zfree are set to Z_NULL, inflateInit updates them to use default allocation ++ functions. ++ ++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory. msg is set to null if there is no error message. ++ inflateInit does not perform any decompression: this will be done by ++ inflate(). ++*/ ++ ++ ++extern int inflate OF((z_stream *strm, int flush)); ++/* ++ Performs one or both of the following actions: ++ ++ - Decompress more input starting at next_in and update next_in and avail_in ++ accordingly. If not all input can be processed (because there is not ++ enough room in the output buffer), next_in is updated and processing ++ will resume at this point for the next call of inflate(). ++ ++ - Provide more output starting at next_out and update next_out and avail_out ++ accordingly. inflate() always provides as much output as possible ++ (until there is no more input data or no more space in the output buffer). ++ ++ Before the call of inflate(), the application should ensure that at least ++ one of the actions is possible, by providing more input and/or consuming ++ more output, and updating the next_* and avail_* values accordingly. ++ The application can consume the uncompressed output when it wants, for ++ example when the output buffer is full (avail_out == 0), or after each ++ call of inflate(). ++ ++ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, ++ inflate flushes as much output as possible to the output buffer. The ++ flushing behavior of inflate is not specified for values of the flush ++ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the ++ current implementation actually flushes as much output as possible ++ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data ++ has been consumed, it is expecting to see the length field of a stored ++ block; if not, it returns Z_DATA_ERROR. ++ ++ inflate() should normally be called until it returns Z_STREAM_END or an ++ error. However if all decompression is to be performed in a single step ++ (a single call of inflate), the parameter flush should be set to ++ Z_FINISH. In this case all pending input is processed and all pending ++ output is flushed; avail_out must be large enough to hold all the ++ uncompressed data. (The size of the uncompressed data may have been saved ++ by the compressor for this purpose.) The next operation on this stream must ++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH ++ is never required, but can be used to inform inflate that a faster routine ++ may be used for the single inflate() call. ++ ++ inflate() returns Z_OK if some progress has been made (more input ++ processed or more output produced), Z_STREAM_END if the end of the ++ compressed data has been reached and all uncompressed output has been ++ produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if ++ the stream structure was inconsistent (for example if next_in or next_out ++ was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no ++ progress is possible or if there was not enough room in the output buffer ++ when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then ++ call inflateSync to look for a good compression block. */ ++ ++ ++extern int inflateEnd OF((z_stream *strm)); ++/* ++ All dynamically allocated data structures for this stream are freed. ++ This function discards any unprocessed input and does not flush any ++ pending output. ++ ++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state ++ was inconsistent. In the error case, msg may be set but then points to a ++ static string (which must not be deallocated). ++*/ ++ ++ /* advanced functions */ ++ ++extern int inflateInit2 OF((z_stream *strm, ++ int windowBits)); ++/* ++ This is another version of inflateInit with more compression options. The ++ fields next_out, zalloc and zfree must be initialized before by the caller. ++ ++ The windowBits parameter is the base two logarithm of the maximum window ++ size (the size of the history buffer). It should be in the range 8..15 for ++ this version of the library (the value 16 will be allowed soon). The ++ default value is 15 if inflateInit is used instead. If a compressed stream ++ with a larger window size is given as input, inflate() will return with ++ the error code Z_DATA_ERROR instead of trying to allocate a larger window. ++ ++ If next_out is not null, the library will use this buffer for the history ++ buffer; the buffer must either be large enough to hold the entire output ++ data, or have at least 1<msg=z_errmsg[1-err], err) ++/* To be used only when the state is known to be valid */ ++ ++#ifndef NULL ++#define NULL ((void *) 0) ++#endif ++ ++ /* common constants */ ++ ++#define DEFLATED 8 ++ ++#ifndef DEF_WBITS ++# define DEF_WBITS MAX_WBITS ++#endif ++/* default windowBits for decompression. MAX_WBITS is for compression only */ ++ ++#if MAX_MEM_LEVEL >= 8 ++# define DEF_MEM_LEVEL 8 ++#else ++# define DEF_MEM_LEVEL MAX_MEM_LEVEL ++#endif ++/* default memLevel */ ++ ++#define STORED_BLOCK 0 ++#define STATIC_TREES 1 ++#define DYN_TREES 2 ++/* The three kinds of block type */ ++ ++#define MIN_MATCH 3 ++#define MAX_MATCH 258 ++/* The minimum and maximum match lengths */ ++ ++ /* functions */ ++ ++#include ++#define zmemcpy memcpy ++#define zmemzero(dest, len) memset(dest, 0, len) ++ ++/* Diagnostic functions */ ++#ifdef DEBUG_ZLIB ++# include ++# ifndef verbose ++# define verbose 0 ++# endif ++# define Assert(cond,msg) {if(!(cond)) z_error(msg);} ++# define Trace(x) fprintf x ++# define Tracev(x) {if (verbose) fprintf x ;} ++# define Tracevv(x) {if (verbose>1) fprintf x ;} ++# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} ++# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} ++#else ++# define Assert(cond,msg) ++# define Trace(x) ++# define Tracev(x) ++# define Tracevv(x) ++# define Tracec(c,x) ++# define Tracecv(c,x) ++#endif ++ ++ ++typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); ++ ++/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ ++/* void zcfree OF((voidpf opaque, voidpf ptr)); */ ++ ++#define ZALLOC(strm, items, size) \ ++ (*((strm)->zalloc))((strm)->opaque, (items), (size)) ++#define ZFREE(strm, addr, size) \ ++ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) ++#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} ++ ++/* deflate.h -- internal compression state ++ * Copyright (C) 1995 Jean-loup Gailly ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/*+++++*/ ++/* infblock.h -- header to use infblock.c ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++struct inflate_blocks_state; ++typedef struct inflate_blocks_state FAR inflate_blocks_statef; ++ ++local inflate_blocks_statef * inflate_blocks_new OF(( ++ z_stream *z, ++ check_func c, /* check function */ ++ uInt w)); /* window size */ ++ ++local int inflate_blocks OF(( ++ inflate_blocks_statef *, ++ z_stream *, ++ int)); /* initial return code */ ++ ++local void inflate_blocks_reset OF(( ++ inflate_blocks_statef *, ++ z_stream *, ++ uLongf *)); /* check value on output */ ++ ++local int inflate_blocks_free OF(( ++ inflate_blocks_statef *, ++ z_stream *, ++ uLongf *)); /* check value on output */ ++ ++local int inflate_addhistory OF(( ++ inflate_blocks_statef *, ++ z_stream *)); ++ ++local int inflate_packet_flush OF(( ++ inflate_blocks_statef *)); ++ ++/*+++++*/ ++/* inftrees.h -- header to use inftrees.c ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/* Huffman code lookup table entry--this entry is four bytes for machines ++ that have 16-bit pointers (e.g. PC's in the small or medium model). */ ++ ++typedef struct inflate_huft_s FAR inflate_huft; ++ ++struct inflate_huft_s { ++ union { ++ struct { ++ Byte Exop; /* number of extra bits or operation */ ++ Byte Bits; /* number of bits in this code or subcode */ ++ } what; ++ uInt Nalloc; /* number of these allocated here */ ++ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ ++ } word; /* 16-bit, 8 bytes for 32-bit machines) */ ++ union { ++ uInt Base; /* literal, length base, or distance base */ ++ inflate_huft *Next; /* pointer to next level of table */ ++ } more; ++}; ++ ++#ifdef DEBUG_ZLIB ++ local uInt inflate_hufts; ++#endif ++ ++local int inflate_trees_bits OF(( ++ uIntf *, /* 19 code lengths */ ++ uIntf *, /* bits tree desired/actual depth */ ++ inflate_huft * FAR *, /* bits tree result */ ++ z_stream *)); /* for zalloc, zfree functions */ ++ ++local int inflate_trees_dynamic OF(( ++ uInt, /* number of literal/length codes */ ++ uInt, /* number of distance codes */ ++ uIntf *, /* that many (total) code lengths */ ++ uIntf *, /* literal desired/actual bit depth */ ++ uIntf *, /* distance desired/actual bit depth */ ++ inflate_huft * FAR *, /* literal/length tree result */ ++ inflate_huft * FAR *, /* distance tree result */ ++ z_stream *)); /* for zalloc, zfree functions */ ++ ++local int inflate_trees_fixed OF(( ++ uIntf *, /* literal desired/actual bit depth */ ++ uIntf *, /* distance desired/actual bit depth */ ++ inflate_huft * FAR *, /* literal/length tree result */ ++ inflate_huft * FAR *)); /* distance tree result */ ++ ++local int inflate_trees_free OF(( ++ inflate_huft *, /* tables to free */ ++ z_stream *)); /* for zfree function */ ++ ++ ++/*+++++*/ ++/* infcodes.h -- header to use infcodes.c ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++struct inflate_codes_state; ++typedef struct inflate_codes_state FAR inflate_codes_statef; ++ ++local inflate_codes_statef *inflate_codes_new OF(( ++ uInt, uInt, ++ inflate_huft *, inflate_huft *, ++ z_stream *)); ++ ++local int inflate_codes OF(( ++ inflate_blocks_statef *, ++ z_stream *, ++ int)); ++ ++local void inflate_codes_free OF(( ++ inflate_codes_statef *, ++ z_stream *)); ++ ++ ++/*+++++*/ ++/* inflate.c -- zlib interface to inflate modules ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* inflate private state */ ++struct internal_state { ++ ++ /* mode */ ++ enum { ++ METHOD, /* waiting for method byte */ ++ FLAG, /* waiting for flag byte */ ++ BLOCKS, /* decompressing blocks */ ++ CHECK4, /* four check bytes to go */ ++ CHECK3, /* three check bytes to go */ ++ CHECK2, /* two check bytes to go */ ++ CHECK1, /* one check byte to go */ ++ DONE, /* finished check, done */ ++ BAD} /* got an error--stay here */ ++ mode; /* current inflate mode */ ++ ++ /* mode dependent information */ ++ union { ++ uInt method; /* if FLAGS, method byte */ ++ struct { ++ uLong was; /* computed check value */ ++ uLong need; /* stream check value */ ++ } check; /* if CHECK, check values to compare */ ++ uInt marker; /* if BAD, inflateSync's marker bytes count */ ++ } sub; /* submode */ ++ ++ /* mode independent information */ ++ int nowrap; /* flag for no wrapper */ ++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */ ++ inflate_blocks_statef ++ *blocks; /* current inflate_blocks state */ ++ ++}; ++ ++ ++int inflateReset(z) ++z_stream *z; ++{ ++ uLong c; ++ ++ if (z == Z_NULL || z->state == Z_NULL) ++ return Z_STREAM_ERROR; ++ z->total_in = z->total_out = 0; ++ z->msg = Z_NULL; ++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD; ++ inflate_blocks_reset(z->state->blocks, z, &c); ++ Trace((stderr, "inflate: reset\n")); ++ return Z_OK; ++} ++ ++ ++int inflateEnd(z) ++z_stream *z; ++{ ++ uLong c; ++ ++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) ++ return Z_STREAM_ERROR; ++ if (z->state->blocks != Z_NULL) ++ inflate_blocks_free(z->state->blocks, z, &c); ++ ZFREE(z, z->state, sizeof(struct internal_state)); ++ z->state = Z_NULL; ++ Trace((stderr, "inflate: end\n")); ++ return Z_OK; ++} ++ ++ ++int inflateInit2(z, w) ++z_stream *z; ++int w; ++{ ++ /* initialize state */ ++ if (z == Z_NULL) ++ return Z_STREAM_ERROR; ++/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ ++/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ ++ if ((z->state = (struct internal_state FAR *) ++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) ++ return Z_MEM_ERROR; ++ z->state->blocks = Z_NULL; ++ ++ /* handle undocumented nowrap option (no zlib header or check) */ ++ z->state->nowrap = 0; ++ if (w < 0) ++ { ++ w = - w; ++ z->state->nowrap = 1; ++ } ++ ++ /* set window size */ ++ if (w < 8 || w > 15) ++ { ++ inflateEnd(z); ++ return Z_STREAM_ERROR; ++ } ++ z->state->wbits = (uInt)w; ++ ++ /* create inflate_blocks state */ ++ if ((z->state->blocks = ++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) ++ == Z_NULL) ++ { ++ inflateEnd(z); ++ return Z_MEM_ERROR; ++ } ++ Trace((stderr, "inflate: allocated\n")); ++ ++ /* reset state */ ++ inflateReset(z); ++ return Z_OK; ++} ++ ++ ++int inflateInit(z) ++z_stream *z; ++{ ++ return inflateInit2(z, DEF_WBITS); ++} ++ ++ ++#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} ++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) ++ ++int inflate(z, f) ++z_stream *z; ++int f; ++{ ++ int r; ++ uInt b; ++ ++ if (z == Z_NULL || z->next_in == Z_NULL) ++ return Z_STREAM_ERROR; ++ r = Z_BUF_ERROR; ++ while (1) switch (z->state->mode) ++ { ++ case METHOD: ++ NEEDBYTE ++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) ++ { ++ z->state->mode = BAD; ++ z->msg = "unknown compression method"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits) ++ { ++ z->state->mode = BAD; ++ z->msg = "invalid window size"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ z->state->mode = FLAG; ++ case FLAG: ++ NEEDBYTE ++ if ((b = NEXTBYTE) & 0x20) ++ { ++ z->state->mode = BAD; ++ z->msg = "invalid reserved bit"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ if (((z->state->sub.method << 8) + b) % 31) ++ { ++ z->state->mode = BAD; ++ z->msg = "incorrect header check"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ Trace((stderr, "inflate: zlib header ok\n")); ++ z->state->mode = BLOCKS; ++ case BLOCKS: ++ r = inflate_blocks(z->state->blocks, z, r); ++ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) ++ r = inflate_packet_flush(z->state->blocks); ++ if (r == Z_DATA_ERROR) ++ { ++ z->state->mode = BAD; ++ z->state->sub.marker = 0; /* can try inflateSync */ ++ break; ++ } ++ if (r != Z_STREAM_END) ++ return r; ++ r = Z_OK; ++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); ++ if (z->state->nowrap) ++ { ++ z->state->mode = DONE; ++ break; ++ } ++ z->state->mode = CHECK4; ++ case CHECK4: ++ NEEDBYTE ++ z->state->sub.check.need = (uLong)NEXTBYTE << 24; ++ z->state->mode = CHECK3; ++ case CHECK3: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 16; ++ z->state->mode = CHECK2; ++ case CHECK2: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 8; ++ z->state->mode = CHECK1; ++ case CHECK1: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE; ++ ++ if (z->state->sub.check.was != z->state->sub.check.need) ++ { ++ z->state->mode = BAD; ++ z->msg = "incorrect data check"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ Trace((stderr, "inflate: zlib check ok\n")); ++ z->state->mode = DONE; ++ case DONE: ++ return Z_STREAM_END; ++ case BAD: ++ return Z_DATA_ERROR; ++ default: ++ return Z_STREAM_ERROR; ++ } ++ ++ empty: ++ if (f != Z_PACKET_FLUSH) ++ return r; ++ z->state->mode = BAD; ++ z->state->sub.marker = 0; /* can try inflateSync */ ++ return Z_DATA_ERROR; ++} ++ ++/* ++ * This subroutine adds the data at next_in/avail_in to the output history ++ * without performing any output. The output buffer must be "caught up"; ++ * i.e. no pending output (hence s->read equals s->write), and the state must ++ * be BLOCKS (i.e. we should be willing to see the start of a series of ++ * BLOCKS). On exit, the output will also be caught up, and the checksum ++ * will have been updated if need be. ++ */ ++ ++int inflateIncomp(z) ++z_stream *z; ++{ ++ if (z->state->mode != BLOCKS) ++ return Z_DATA_ERROR; ++ return inflate_addhistory(z->state->blocks, z); ++} ++ ++ ++int inflateSync(z) ++z_stream *z; ++{ ++ uInt n; /* number of bytes to look at */ ++ Bytef *p; /* pointer to bytes */ ++ uInt m; /* number of marker bytes found in a row */ ++ uLong r, w; /* temporaries to save total_in and total_out */ ++ ++ /* set up */ ++ if (z == Z_NULL || z->state == Z_NULL) ++ return Z_STREAM_ERROR; ++ if (z->state->mode != BAD) ++ { ++ z->state->mode = BAD; ++ z->state->sub.marker = 0; ++ } ++ if ((n = z->avail_in) == 0) ++ return Z_BUF_ERROR; ++ p = z->next_in; ++ m = z->state->sub.marker; ++ ++ /* search */ ++ while (n && m < 4) ++ { ++ if (*p == (Byte)(m < 2 ? 0 : 0xff)) ++ m++; ++ else if (*p) ++ m = 0; ++ else ++ m = 4 - m; ++ p++, n--; ++ } ++ ++ /* restore */ ++ z->total_in += p - z->next_in; ++ z->next_in = p; ++ z->avail_in = n; ++ z->state->sub.marker = m; ++ ++ /* return no joy or set up to restart on a new block */ ++ if (m != 4) ++ return Z_DATA_ERROR; ++ r = z->total_in; w = z->total_out; ++ inflateReset(z); ++ z->total_in = r; z->total_out = w; ++ z->state->mode = BLOCKS; ++ return Z_OK; ++} ++ ++#undef NEEDBYTE ++#undef NEXTBYTE ++ ++/*+++++*/ ++/* infutil.h -- types and macros common to blocks and codes ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/* inflate blocks semi-private state */ ++struct inflate_blocks_state { ++ ++ /* mode */ ++ enum { ++ TYPE, /* get type bits (3, including end bit) */ ++ LENS, /* get lengths for stored */ ++ STORED, /* processing stored block */ ++ TABLE, /* get table lengths */ ++ BTREE, /* get bit lengths tree for a dynamic block */ ++ DTREE, /* get length, distance trees for a dynamic block */ ++ CODES, /* processing fixed or dynamic block */ ++ DRY, /* output remaining window bytes */ ++ DONEB, /* finished last block, done */ ++ BADB} /* got a data error--stuck here */ ++ mode; /* current inflate_block mode */ ++ ++ /* mode dependent information */ ++ union { ++ uInt left; /* if STORED, bytes left to copy */ ++ struct { ++ uInt table; /* table lengths (14 bits) */ ++ uInt index; /* index into blens (or border) */ ++ uIntf *blens; /* bit lengths of codes */ ++ uInt bb; /* bit length tree depth */ ++ inflate_huft *tb; /* bit length decoding tree */ ++ int nblens; /* # elements allocated at blens */ ++ } trees; /* if DTREE, decoding info for trees */ ++ struct { ++ inflate_huft *tl, *td; /* trees to free */ ++ inflate_codes_statef ++ *codes; ++ } decode; /* if CODES, current state */ ++ } sub; /* submode */ ++ uInt last; /* true if this block is the last block */ ++ ++ /* mode independent information */ ++ uInt bitk; /* bits in bit buffer */ ++ uLong bitb; /* bit buffer */ ++ Bytef *window; /* sliding window */ ++ Bytef *end; /* one byte after sliding window */ ++ Bytef *read; /* window read pointer */ ++ Bytef *write; /* window write pointer */ ++ check_func checkfn; /* check function */ ++ uLong check; /* check on output */ ++ ++}; ++ ++ ++/* defines for inflate input/output */ ++/* update pointers and return */ ++#define UPDBITS {s->bitb=b;s->bitk=k;} ++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} ++#define UPDOUT {s->write=q;} ++#define UPDATE {UPDBITS UPDIN UPDOUT} ++#define LEAVE {UPDATE return inflate_flush(s,z,r);} ++/* get bytes and bits */ ++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} ++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} ++#define NEXTBYTE (n--,*p++) ++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} ++/* output bytes */ ++#define WAVAIL (qread?s->read-q-1:s->end-q) ++#define LOADOUT {q=s->write;m=WAVAIL;} ++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} ++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} ++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} ++#define OUTBYTE(a) {*q++=(Byte)(a);m--;} ++/* load local pointers */ ++#define LOAD {LOADIN LOADOUT} ++ ++/* ++ * The IBM 150 firmware munges the data right after _etext[]. This ++ * protects it. -- Cort ++ */ ++local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0}; ++/* And'ing with mask[n] masks the lower n bits */ ++local uInt inflate_mask[] = { ++ 0x0000, ++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, ++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff ++}; ++ ++/* copy as much as possible from the sliding window to the output area */ ++local int inflate_flush OF(( ++ inflate_blocks_statef *, ++ z_stream *, ++ int)); ++ ++/*+++++*/ ++/* inffast.h -- header to use inffast.c ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++local int inflate_fast OF(( ++ uInt, ++ uInt, ++ inflate_huft *, ++ inflate_huft *, ++ inflate_blocks_statef *, ++ z_stream *)); ++ ++ ++/*+++++*/ ++/* infblock.c -- interpret and process block types to last block ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* Table for deflate from PKZIP's appnote.txt. */ ++local uInt border[] = { /* Order of the bit length code lengths */ ++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; ++ ++/* ++ Notes beyond the 1.93a appnote.txt: ++ ++ 1. Distance pointers never point before the beginning of the output ++ stream. ++ 2. Distance pointers can point back across blocks, up to 32k away. ++ 3. There is an implied maximum of 7 bits for the bit length table and ++ 15 bits for the actual data. ++ 4. If only one code exists, then it is encoded using one bit. (Zero ++ would be more efficient, but perhaps a little confusing.) If two ++ codes exist, they are coded using one bit each (0 and 1). ++ 5. There is no way of sending zero distance codes--a dummy must be ++ sent if there are none. (History: a pre 2.0 version of PKZIP would ++ store blocks with no distance codes, but this was discovered to be ++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow ++ zero distance codes, which is sent as one code of zero bits in ++ length. ++ 6. There are up to 286 literal/length codes. Code 256 represents the ++ end-of-block. Note however that the static length tree defines ++ 288 codes just to fill out the Huffman codes. Codes 286 and 287 ++ cannot be used though, since there is no length base or extra bits ++ defined for them. Similarily, there are up to 30 distance codes. ++ However, static trees define 32 codes (all 5 bits) to fill out the ++ Huffman codes, but the last two had better not show up in the data. ++ 7. Unzip can check dynamic Huffman blocks for complete code sets. ++ The exception is that a single code would not be complete (see #4). ++ 8. The five bits following the block type is really the number of ++ literal codes sent minus 257. ++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits ++ (1+6+6). Therefore, to output three times the length, you output ++ three codes (1+1+1), whereas to output four times the same length, ++ you only need two codes (1+3). Hmm. ++ 10. In the tree reconstruction algorithm, Code = Code + Increment ++ only if BitLength(i) is not zero. (Pretty obvious.) ++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) ++ 12. Note: length code 284 can represent 227-258, but length code 285 ++ really is 258. The last length deserves its own, short code ++ since it gets used a lot in very redundant files. The length ++ 258 is special since 258 - 3 (the min match length) is 255. ++ 13. The literal/length and distance code bit lengths are read as a ++ single stream of lengths. It is possible (and advantageous) for ++ a repeat code (16, 17, or 18) to go across the boundary between ++ the two sets of lengths. ++ */ ++ ++ ++local void inflate_blocks_reset(s, z, c) ++inflate_blocks_statef *s; ++z_stream *z; ++uLongf *c; ++{ ++ if (s->checkfn != Z_NULL) ++ *c = s->check; ++ if (s->mode == BTREE || s->mode == DTREE) ++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); ++ if (s->mode == CODES) ++ { ++ inflate_codes_free(s->sub.decode.codes, z); ++ inflate_trees_free(s->sub.decode.td, z); ++ inflate_trees_free(s->sub.decode.tl, z); ++ } ++ s->mode = TYPE; ++ s->bitk = 0; ++ s->bitb = 0; ++ s->read = s->write = s->window; ++ if (s->checkfn != Z_NULL) ++ s->check = (*s->checkfn)(0L, Z_NULL, 0); ++ Trace((stderr, "inflate: blocks reset\n")); ++} ++ ++ ++local inflate_blocks_statef *inflate_blocks_new(z, c, w) ++z_stream *z; ++check_func c; ++uInt w; ++{ ++ inflate_blocks_statef *s; ++ ++ if ((s = (inflate_blocks_statef *)ZALLOC ++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) ++ return s; ++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) ++ { ++ ZFREE(z, s, sizeof(struct inflate_blocks_state)); ++ return Z_NULL; ++ } ++ s->end = s->window + w; ++ s->checkfn = c; ++ s->mode = TYPE; ++ Trace((stderr, "inflate: blocks allocated\n")); ++ inflate_blocks_reset(s, z, &s->check); ++ return s; ++} ++ ++ ++local int inflate_blocks(s, z, r) ++inflate_blocks_statef *s; ++z_stream *z; ++int r; ++{ ++ uInt t; /* temporary storage */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ ++ /* copy input/output information to locals (UPDATE macro restores) */ ++ LOAD ++ ++ /* process input based on current state */ ++ while (1) switch (s->mode) ++ { ++ case TYPE: ++ NEEDBITS(3) ++ t = (uInt)b & 7; ++ s->last = t & 1; ++ switch (t >> 1) ++ { ++ case 0: /* stored */ ++ Trace((stderr, "inflate: stored block%s\n", ++ s->last ? " (last)" : "")); ++ DUMPBITS(3) ++ t = k & 7; /* go to byte boundary */ ++ DUMPBITS(t) ++ s->mode = LENS; /* get length of stored block */ ++ break; ++ case 1: /* fixed */ ++ Trace((stderr, "inflate: fixed codes block%s\n", ++ s->last ? " (last)" : "")); ++ { ++ uInt bl, bd; ++ inflate_huft *tl, *td; ++ ++ inflate_trees_fixed(&bl, &bd, &tl, &td); ++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); ++ if (s->sub.decode.codes == Z_NULL) ++ { ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ s->sub.decode.tl = Z_NULL; /* don't try to free these */ ++ s->sub.decode.td = Z_NULL; ++ } ++ DUMPBITS(3) ++ s->mode = CODES; ++ break; ++ case 2: /* dynamic */ ++ Trace((stderr, "inflate: dynamic codes block%s\n", ++ s->last ? " (last)" : "")); ++ DUMPBITS(3) ++ s->mode = TABLE; ++ break; ++ case 3: /* illegal */ ++ DUMPBITS(3) ++ s->mode = BADB; ++ z->msg = "invalid block type"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ break; ++ case LENS: ++ NEEDBITS(32) ++ if (((~b) >> 16) != (b & 0xffff)) ++ { ++ s->mode = BADB; ++ z->msg = "invalid stored block lengths"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ s->sub.left = (uInt)b & 0xffff; ++ b = k = 0; /* dump bits */ ++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); ++ s->mode = s->sub.left ? STORED : TYPE; ++ break; ++ case STORED: ++ if (n == 0) ++ LEAVE ++ NEEDOUT ++ t = s->sub.left; ++ if (t > n) t = n; ++ if (t > m) t = m; ++ zmemcpy(q, p, t); ++ p += t; n -= t; ++ q += t; m -= t; ++ if ((s->sub.left -= t) != 0) ++ break; ++ Tracev((stderr, "inflate: stored end, %lu total out\n", ++ z->total_out + (q >= s->read ? q - s->read : ++ (s->end - s->read) + (q - s->window)))); ++ s->mode = s->last ? DRY : TYPE; ++ break; ++ case TABLE: ++ NEEDBITS(14) ++ s->sub.trees.table = t = (uInt)b & 0x3fff; ++#ifndef PKZIP_BUG_WORKAROUND ++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) ++ { ++ s->mode = BADB; ++ z->msg = "too many length or distance symbols"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++#endif ++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); ++ if (t < 19) ++ t = 19; ++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) ++ { ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ s->sub.trees.nblens = t; ++ DUMPBITS(14) ++ s->sub.trees.index = 0; ++ Tracev((stderr, "inflate: table sizes ok\n")); ++ s->mode = BTREE; ++ case BTREE: ++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) ++ { ++ NEEDBITS(3) ++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; ++ DUMPBITS(3) ++ } ++ while (s->sub.trees.index < 19) ++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0; ++ s->sub.trees.bb = 7; ++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, ++ &s->sub.trees.tb, z); ++ if (t != Z_OK) ++ { ++ r = t; ++ if (r == Z_DATA_ERROR) ++ s->mode = BADB; ++ LEAVE ++ } ++ s->sub.trees.index = 0; ++ Tracev((stderr, "inflate: bits tree ok\n")); ++ s->mode = DTREE; ++ case DTREE: ++ while (t = s->sub.trees.table, ++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) ++ { ++ inflate_huft *h; ++ uInt i, j, c; ++ ++ t = s->sub.trees.bb; ++ NEEDBITS(t) ++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); ++ t = h->word.what.Bits; ++ c = h->more.Base; ++ if (c < 16) ++ { ++ DUMPBITS(t) ++ s->sub.trees.blens[s->sub.trees.index++] = c; ++ } ++ else /* c == 16..18 */ ++ { ++ i = c == 18 ? 7 : c - 14; ++ j = c == 18 ? 11 : 3; ++ NEEDBITS(t + i) ++ DUMPBITS(t) ++ j += (uInt)b & inflate_mask[i]; ++ DUMPBITS(i) ++ i = s->sub.trees.index; ++ t = s->sub.trees.table; ++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || ++ (c == 16 && i < 1)) ++ { ++ s->mode = BADB; ++ z->msg = "invalid bit length repeat"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0; ++ do { ++ s->sub.trees.blens[i++] = c; ++ } while (--j); ++ s->sub.trees.index = i; ++ } ++ } ++ inflate_trees_free(s->sub.trees.tb, z); ++ s->sub.trees.tb = Z_NULL; ++ { ++ uInt bl, bd; ++ inflate_huft *tl, *td; ++ inflate_codes_statef *c; ++ ++ bl = 9; /* must be <= 9 for lookahead assumptions */ ++ bd = 6; /* must be <= 9 for lookahead assumptions */ ++ t = s->sub.trees.table; ++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), ++ s->sub.trees.blens, &bl, &bd, &tl, &td, z); ++ if (t != Z_OK) ++ { ++ if (t == (uInt)Z_DATA_ERROR) ++ s->mode = BADB; ++ r = t; ++ LEAVE ++ } ++ Tracev((stderr, "inflate: trees ok\n")); ++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) ++ { ++ inflate_trees_free(td, z); ++ inflate_trees_free(tl, z); ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); ++ s->sub.decode.codes = c; ++ s->sub.decode.tl = tl; ++ s->sub.decode.td = td; ++ } ++ s->mode = CODES; ++ case CODES: ++ UPDATE ++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) ++ return inflate_flush(s, z, r); ++ r = Z_OK; ++ inflate_codes_free(s->sub.decode.codes, z); ++ inflate_trees_free(s->sub.decode.td, z); ++ inflate_trees_free(s->sub.decode.tl, z); ++ LOAD ++ Tracev((stderr, "inflate: codes end, %lu total out\n", ++ z->total_out + (q >= s->read ? q - s->read : ++ (s->end - s->read) + (q - s->window)))); ++ if (!s->last) ++ { ++ s->mode = TYPE; ++ break; ++ } ++ if (k > 7) /* return unused byte, if any */ ++ { ++ Assert(k < 16, "inflate_codes grabbed too many bytes") ++ k -= 8; ++ n++; ++ p--; /* can always return one */ ++ } ++ s->mode = DRY; ++ case DRY: ++ FLUSH ++ if (s->read != s->write) ++ LEAVE ++ s->mode = DONEB; ++ case DONEB: ++ r = Z_STREAM_END; ++ LEAVE ++ case BADB: ++ r = Z_DATA_ERROR; ++ LEAVE ++ default: ++ r = Z_STREAM_ERROR; ++ LEAVE ++ } ++} ++ ++ ++local int inflate_blocks_free(s, z, c) ++inflate_blocks_statef *s; ++z_stream *z; ++uLongf *c; ++{ ++ inflate_blocks_reset(s, z, c); ++ ZFREE(z, s->window, s->end - s->window); ++ ZFREE(z, s, sizeof(struct inflate_blocks_state)); ++ Trace((stderr, "inflate: blocks freed\n")); ++ return Z_OK; ++} ++ ++/* ++ * This subroutine adds the data at next_in/avail_in to the output history ++ * without performing any output. The output buffer must be "caught up"; ++ * i.e. no pending output (hence s->read equals s->write), and the state must ++ * be BLOCKS (i.e. we should be willing to see the start of a series of ++ * BLOCKS). On exit, the output will also be caught up, and the checksum ++ * will have been updated if need be. ++ */ ++local int inflate_addhistory(s, z) ++inflate_blocks_statef *s; ++z_stream *z; ++{ ++ uLong b; /* bit buffer */ /* NOT USED HERE */ ++ uInt k; /* bits in bit buffer */ /* NOT USED HERE */ ++ uInt t; /* temporary storage */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ ++ if (s->read != s->write) ++ return Z_STREAM_ERROR; ++ if (s->mode != TYPE) ++ return Z_DATA_ERROR; ++ ++ /* we're ready to rock */ ++ LOAD ++ /* while there is input ready, copy to output buffer, moving ++ * pointers as needed. ++ */ ++ while (n) { ++ t = n; /* how many to do */ ++ /* is there room until end of buffer? */ ++ if (t > m) t = m; ++ /* update check information */ ++ if (s->checkfn != Z_NULL) ++ s->check = (*s->checkfn)(s->check, q, t); ++ zmemcpy(q, p, t); ++ q += t; ++ p += t; ++ n -= t; ++ z->total_out += t; ++ s->read = q; /* drag read pointer forward */ ++/* WRAP */ /* expand WRAP macro by hand to handle s->read */ ++ if (q == s->end) { ++ s->read = q = s->window; ++ m = WAVAIL; ++ } ++ } ++ UPDATE ++ return Z_OK; ++} ++ ++ ++/* ++ * At the end of a Deflate-compressed PPP packet, we expect to have seen ++ * a `stored' block type value but not the (zero) length bytes. ++ */ ++local int inflate_packet_flush(s) ++ inflate_blocks_statef *s; ++{ ++ if (s->mode != LENS) ++ return Z_DATA_ERROR; ++ s->mode = TYPE; ++ return Z_OK; ++} ++ ++ ++/*+++++*/ ++/* inftrees.c -- generate Huffman trees for efficient decoding ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define base more.Base ++#define next more.Next ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++ ++local int huft_build OF(( ++ uIntf *, /* code lengths in bits */ ++ uInt, /* number of codes */ ++ uInt, /* number of "simple" codes */ ++ uIntf *, /* list of base values for non-simple codes */ ++ uIntf *, /* list of extra bits for non-simple codes */ ++ inflate_huft * FAR*,/* result: starting table */ ++ uIntf *, /* maximum lookup bits (returns actual) */ ++ z_stream *)); /* for zalloc function */ ++ ++local voidpf falloc OF(( ++ voidpf, /* opaque pointer (not used) */ ++ uInt, /* number of items */ ++ uInt)); /* size of item */ ++ ++local void ffree OF(( ++ voidpf q, /* opaque pointer (not used) */ ++ voidpf p, /* what to free (not used) */ ++ uInt n)); /* number of bytes (not used) */ ++ ++/* Tables for deflate from PKZIP's appnote.txt. */ ++local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ ++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, ++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; ++ /* actually lengths - 2; also see note #13 above about 258 */ ++local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, ++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ ++local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ ++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, ++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, ++ 8193, 12289, 16385, 24577}; ++local uInt cpdext[] = { /* Extra bits for distance codes */ ++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, ++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, ++ 12, 12, 13, 13}; ++ ++/* ++ Huffman code decoding is performed using a multi-level table lookup. ++ The fastest way to decode is to simply build a lookup table whose ++ size is determined by the longest code. However, the time it takes ++ to build this table can also be a factor if the data being decoded ++ is not very long. The most common codes are necessarily the ++ shortest codes, so those codes dominate the decoding time, and hence ++ the speed. The idea is you can have a shorter table that decodes the ++ shorter, more probable codes, and then point to subsidiary tables for ++ the longer codes. The time it costs to decode the longer codes is ++ then traded against the time it takes to make longer tables. ++ ++ This results of this trade are in the variables lbits and dbits ++ below. lbits is the number of bits the first level table for literal/ ++ length codes can decode in one step, and dbits is the same thing for ++ the distance codes. Subsequent tables are also less than or equal to ++ those sizes. These values may be adjusted either when all of the ++ codes are shorter than that, in which case the longest code length in ++ bits is used, or when the shortest code is *longer* than the requested ++ table size, in which case the length of the shortest code in bits is ++ used. ++ ++ There are two different values for the two tables, since they code a ++ different number of possibilities each. The literal/length table ++ codes 286 possible values, or in a flat code, a little over eight ++ bits. The distance table codes 30 possible values, or a little less ++ than five bits, flat. The optimum values for speed end up being ++ about one bit more than those, so lbits is 8+1 and dbits is 5+1. ++ The optimum values may differ though from machine to machine, and ++ possibly even between compilers. Your mileage may vary. ++ */ ++ ++ ++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ ++#define BMAX 15 /* maximum bit length of any code */ ++#define N_MAX 288 /* maximum number of codes in any set */ ++ ++#ifdef DEBUG_ZLIB ++ uInt inflate_hufts; ++#endif ++ ++local int huft_build(b, n, s, d, e, t, m, zs) ++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ ++uInt n; /* number of codes (assumed <= N_MAX) */ ++uInt s; /* number of simple-valued codes (0..s-1) */ ++uIntf *d; /* list of base values for non-simple codes */ ++uIntf *e; /* list of extra bits for non-simple codes */ ++inflate_huft * FAR *t; /* result: starting table */ ++uIntf *m; /* maximum lookup bits, returns actual */ ++z_stream *zs; /* for zalloc function */ ++/* Given a list of code lengths and a maximum table size, make a set of ++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR ++ if the given code set is incomplete (the tables are still built in this ++ case), Z_DATA_ERROR if the input is invalid (all zero length codes or an ++ over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ ++{ ++ ++ uInt a; /* counter for codes of length k */ ++ uInt c[BMAX+1]; /* bit length count table */ ++ uInt f; /* i repeats in table every f entries */ ++ int g; /* maximum code length */ ++ int h; /* table level */ ++ register uInt i; /* counter, current code */ ++ register uInt j; /* counter */ ++ register int k; /* number of bits in current code */ ++ int l; /* bits per table (returned in m) */ ++ register uIntf *p; /* pointer into c[], b[], or v[] */ ++ inflate_huft *q; /* points to current table */ ++ struct inflate_huft_s r; /* table entry for structure assignment */ ++ inflate_huft *u[BMAX]; /* table stack */ ++ uInt v[N_MAX]; /* values in order of bit length */ ++ register int w; /* bits before this table == (l * h) */ ++ uInt x[BMAX+1]; /* bit offsets, then code stack */ ++ uIntf *xp; /* pointer into x */ ++ int y; /* number of dummy codes added */ ++ uInt z; /* number of entries in current table */ ++ ++ ++ /* Generate counts for each bit length */ ++ p = c; ++#define C0 *p++ = 0; ++#define C2 C0 C0 C0 C0 ++#define C4 C2 C2 C2 C2 ++ C4 /* clear c[]--assume BMAX+1 is 16 */ ++ p = b; i = n; ++ do { ++ c[*p++]++; /* assume all entries <= BMAX */ ++ } while (--i); ++ if (c[0] == n) /* null input--all zero length codes */ ++ { ++ *t = (inflate_huft *)Z_NULL; ++ *m = 0; ++ return Z_OK; ++ } ++ ++ ++ /* Find minimum and maximum length, bound *m by those */ ++ l = *m; ++ for (j = 1; j <= BMAX; j++) ++ if (c[j]) ++ break; ++ k = j; /* minimum code length */ ++ if ((uInt)l < j) ++ l = j; ++ for (i = BMAX; i; i--) ++ if (c[i]) ++ break; ++ g = i; /* maximum code length */ ++ if ((uInt)l > i) ++ l = i; ++ *m = l; ++ ++ ++ /* Adjust last length count to fill out codes, if needed */ ++ for (y = 1 << j; j < i; j++, y <<= 1) ++ if ((y -= c[j]) < 0) ++ return Z_DATA_ERROR; ++ if ((y -= c[i]) < 0) ++ return Z_DATA_ERROR; ++ c[i] += y; ++ ++ ++ /* Generate starting offsets into the value table for each length */ ++ x[1] = j = 0; ++ p = c + 1; xp = x + 2; ++ while (--i) { /* note that i == g from above */ ++ *xp++ = (j += *p++); ++ } ++ ++ ++ /* Make a table of values in order of bit lengths */ ++ p = b; i = 0; ++ do { ++ if ((j = *p++) != 0) ++ v[x[j]++] = i; ++ } while (++i < n); ++ ++ ++ /* Generate the Huffman codes and for each, make the table entries */ ++ x[0] = i = 0; /* first Huffman code is zero */ ++ p = v; /* grab values in bit order */ ++ h = -1; /* no tables yet--level -1 */ ++ w = -l; /* bits decoded == (l * h) */ ++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ ++ q = (inflate_huft *)Z_NULL; /* ditto */ ++ z = 0; /* ditto */ ++ ++ /* go through the bit lengths (k already is bits in shortest code) */ ++ for (; k <= g; k++) ++ { ++ a = c[k]; ++ while (a--) ++ { ++ /* here i is the Huffman code of length k bits for value *p */ ++ /* make tables up to required level */ ++ while (k > w + l) ++ { ++ h++; ++ w += l; /* previous table always l bits */ ++ ++ /* compute minimum size table less than or equal to l bits */ ++ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ ++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ ++ { /* too few codes for k-w bit table */ ++ f -= a + 1; /* deduct codes from patterns left */ ++ xp = c + k; ++ if (j < z) ++ while (++j < z) /* try smaller tables up to z bits */ ++ { ++ if ((f <<= 1) <= *++xp) ++ break; /* enough codes to use up j bits */ ++ f -= *xp; /* else deduct codes from patterns */ ++ } ++ } ++ z = 1 << j; /* table entries for j-bit table */ ++ ++ /* allocate and link in new table */ ++ if ((q = (inflate_huft *)ZALLOC ++ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) ++ { ++ if (h) ++ inflate_trees_free(u[0], zs); ++ return Z_MEM_ERROR; /* not enough memory */ ++ } ++ q->word.Nalloc = z + 1; ++#ifdef DEBUG_ZLIB ++ inflate_hufts += z + 1; ++#endif ++ *t = q + 1; /* link to list for huft_free() */ ++ *(t = &(q->next)) = Z_NULL; ++ u[h] = ++q; /* table starts after link */ ++ ++ /* connect to last table, if there is one */ ++ if (h) ++ { ++ x[h] = i; /* save pattern for backing up */ ++ r.bits = (Byte)l; /* bits to dump before this table */ ++ r.exop = (Byte)j; /* bits in this table */ ++ r.next = q; /* pointer to this table */ ++ j = i >> (w - l); /* (get around Turbo C bug) */ ++ u[h-1][j] = r; /* connect to last table */ ++ } ++ } ++ ++ /* set up table entry in r */ ++ r.bits = (Byte)(k - w); ++ if (p >= v + n) ++ r.exop = 128 + 64; /* out of values--invalid code */ ++ else if (*p < s) ++ { ++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ ++ r.base = *p++; /* simple code is just the value */ ++ } ++ else ++ { ++ r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ ++ r.base = d[*p++ - s]; ++ } ++ ++ /* fill code-like entries with r */ ++ f = 1 << (k - w); ++ for (j = i >> w; j < z; j += f) ++ q[j] = r; ++ ++ /* backwards increment the k-bit code i */ ++ for (j = 1 << (k - 1); i & j; j >>= 1) ++ i ^= j; ++ i ^= j; ++ ++ /* backup over finished tables */ ++ while ((i & ((1 << w) - 1)) != x[h]) ++ { ++ h--; /* don't need to update q */ ++ w -= l; ++ } ++ } ++ } ++ ++ ++ /* Return Z_BUF_ERROR if we were given an incomplete table */ ++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; ++} ++ ++ ++local int inflate_trees_bits(c, bb, tb, z) ++uIntf *c; /* 19 code lengths */ ++uIntf *bb; /* bits tree desired/actual depth */ ++inflate_huft * FAR *tb; /* bits tree result */ ++z_stream *z; /* for zfree function */ ++{ ++ int r; ++ ++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); ++ if (r == Z_DATA_ERROR) ++ z->msg = "oversubscribed dynamic bit lengths tree"; ++ else if (r == Z_BUF_ERROR) ++ { ++ inflate_trees_free(*tb, z); ++ z->msg = "incomplete dynamic bit lengths tree"; ++ r = Z_DATA_ERROR; ++ } ++ return r; ++} ++ ++ ++local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) ++uInt nl; /* number of literal/length codes */ ++uInt nd; /* number of distance codes */ ++uIntf *c; /* that many (total) code lengths */ ++uIntf *bl; /* literal desired/actual bit depth */ ++uIntf *bd; /* distance desired/actual bit depth */ ++inflate_huft * FAR *tl; /* literal/length tree result */ ++inflate_huft * FAR *td; /* distance tree result */ ++z_stream *z; /* for zfree function */ ++{ ++ int r; ++ ++ /* build literal/length tree */ ++ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) ++ { ++ if (r == Z_DATA_ERROR) ++ z->msg = "oversubscribed literal/length tree"; ++ else if (r == Z_BUF_ERROR) ++ { ++ inflate_trees_free(*tl, z); ++ z->msg = "incomplete literal/length tree"; ++ r = Z_DATA_ERROR; ++ } ++ return r; ++ } ++ ++ /* build distance tree */ ++ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) ++ { ++ if (r == Z_DATA_ERROR) ++ z->msg = "oversubscribed literal/length tree"; ++ else if (r == Z_BUF_ERROR) { ++#ifdef PKZIP_BUG_WORKAROUND ++ r = Z_OK; ++ } ++#else ++ inflate_trees_free(*td, z); ++ z->msg = "incomplete literal/length tree"; ++ r = Z_DATA_ERROR; ++ } ++ inflate_trees_free(*tl, z); ++ return r; ++#endif ++ } ++ ++ /* done */ ++ return Z_OK; ++} ++ ++ ++/* build fixed tables only once--keep them here */ ++local int fixed_lock = 0; ++local int fixed_built = 0; ++#define FIXEDH 530 /* number of hufts used by fixed tables */ ++local uInt fixed_left = FIXEDH; ++local inflate_huft fixed_mem[FIXEDH]; ++local uInt fixed_bl; ++local uInt fixed_bd; ++local inflate_huft *fixed_tl; ++local inflate_huft *fixed_td; ++ ++ ++local voidpf falloc(q, n, s) ++voidpf q; /* opaque pointer (not used) */ ++uInt n; /* number of items */ ++uInt s; /* size of item */ ++{ ++ Assert(s == sizeof(inflate_huft) && n <= fixed_left, ++ "inflate_trees falloc overflow"); ++ if (q) s++; /* to make some compilers happy */ ++ fixed_left -= n; ++ return (voidpf)(fixed_mem + fixed_left); ++} ++ ++ ++local void ffree(q, p, n) ++voidpf q; ++voidpf p; ++uInt n; ++{ ++ Assert(0, "inflate_trees ffree called!"); ++ if (q) q = p; /* to make some compilers happy */ ++} ++ ++ ++local int inflate_trees_fixed(bl, bd, tl, td) ++uIntf *bl; /* literal desired/actual bit depth */ ++uIntf *bd; /* distance desired/actual bit depth */ ++inflate_huft * FAR *tl; /* literal/length tree result */ ++inflate_huft * FAR *td; /* distance tree result */ ++{ ++ /* build fixed tables if not built already--lock out other instances */ ++ while (++fixed_lock > 1) ++ fixed_lock--; ++ if (!fixed_built) ++ { ++ int k; /* temporary variable */ ++ unsigned c[288]; /* length list for huft_build */ ++ z_stream z; /* for falloc function */ ++ ++ /* set up fake z_stream for memory routines */ ++ z.zalloc = falloc; ++ z.zfree = ffree; ++ z.opaque = Z_NULL; ++ ++ /* literal table */ ++ for (k = 0; k < 144; k++) ++ c[k] = 8; ++ for (; k < 256; k++) ++ c[k] = 9; ++ for (; k < 280; k++) ++ c[k] = 7; ++ for (; k < 288; k++) ++ c[k] = 8; ++ fixed_bl = 7; ++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); ++ ++ /* distance table */ ++ for (k = 0; k < 30; k++) ++ c[k] = 5; ++ fixed_bd = 5; ++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); ++ ++ /* done */ ++ fixed_built = 1; ++ } ++ fixed_lock--; ++ *bl = fixed_bl; ++ *bd = fixed_bd; ++ *tl = fixed_tl; ++ *td = fixed_td; ++ return Z_OK; ++} ++ ++ ++local int inflate_trees_free(t, z) ++inflate_huft *t; /* table to free */ ++z_stream *z; /* for zfree function */ ++/* Free the malloc'ed tables built by huft_build(), which makes a linked ++ list of the tables it made, with the links in a dummy first entry of ++ each table. */ ++{ ++ register inflate_huft *p, *q; ++ ++ /* Go through linked list, freeing from the malloced (t[-1]) address. */ ++ p = t; ++ while (p != Z_NULL) ++ { ++ q = (--p)->next; ++ ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); ++ p = q; ++ } ++ return Z_OK; ++} ++ ++/*+++++*/ ++/* infcodes.c -- process literals and length/distance pairs ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define base more.Base ++#define next more.Next ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++/* inflate codes private state */ ++struct inflate_codes_state { ++ ++ /* mode */ ++ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ ++ START, /* x: set up for LEN */ ++ LEN, /* i: get length/literal/eob next */ ++ LENEXT, /* i: getting length extra (have base) */ ++ DIST, /* i: get distance next */ ++ DISTEXT, /* i: getting distance extra */ ++ COPY, /* o: copying bytes in window, waiting for space */ ++ LIT, /* o: got literal, waiting for output space */ ++ WASH, /* o: got eob, possibly still output waiting */ ++ END, /* x: got eob and all data flushed */ ++ BADCODE} /* x: got error */ ++ mode; /* current inflate_codes mode */ ++ ++ /* mode dependent information */ ++ uInt len; ++ union { ++ struct { ++ inflate_huft *tree; /* pointer into tree */ ++ uInt need; /* bits needed */ ++ } code; /* if LEN or DIST, where in tree */ ++ uInt lit; /* if LIT, literal */ ++ struct { ++ uInt get; /* bits to get for extra */ ++ uInt dist; /* distance back to copy from */ ++ } copy; /* if EXT or COPY, where and how much */ ++ } sub; /* submode */ ++ ++ /* mode independent information */ ++ Byte lbits; /* ltree bits decoded per branch */ ++ Byte dbits; /* dtree bits decoder per branch */ ++ inflate_huft *ltree; /* literal/length/eob tree */ ++ inflate_huft *dtree; /* distance tree */ ++ ++}; ++ ++ ++local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) ++uInt bl, bd; ++inflate_huft *tl, *td; ++z_stream *z; ++{ ++ inflate_codes_statef *c; ++ ++ if ((c = (inflate_codes_statef *) ++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) ++ { ++ c->mode = START; ++ c->lbits = (Byte)bl; ++ c->dbits = (Byte)bd; ++ c->ltree = tl; ++ c->dtree = td; ++ Tracev((stderr, "inflate: codes new\n")); ++ } ++ return c; ++} ++ ++ ++local int inflate_codes(s, z, r) ++inflate_blocks_statef *s; ++z_stream *z; ++int r; ++{ ++ uInt j; /* temporary storage */ ++ inflate_huft *t; /* temporary pointer */ ++ uInt e; /* extra bits or operation */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ Bytef *f; /* pointer to copy strings from */ ++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ ++ ++ /* copy input/output information to locals (UPDATE macro restores) */ ++ LOAD ++ ++ /* process input and output based on current state */ ++ while (1) switch (c->mode) ++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ ++ case START: /* x: set up for LEN */ ++#ifndef SLOW ++ if (m >= 258 && n >= 10) ++ { ++ UPDATE ++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); ++ LOAD ++ if (r != Z_OK) ++ { ++ c->mode = r == Z_STREAM_END ? WASH : BADCODE; ++ break; ++ } ++ } ++#endif /* !SLOW */ ++ c->sub.code.need = c->lbits; ++ c->sub.code.tree = c->ltree; ++ c->mode = LEN; ++ case LEN: /* i: get length/literal/eob next */ ++ j = c->sub.code.need; ++ NEEDBITS(j) ++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); ++ DUMPBITS(t->bits) ++ e = (uInt)(t->exop); ++ if (e == 0) /* literal */ ++ { ++ c->sub.lit = t->base; ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: literal '%c'\n" : ++ "inflate: literal 0x%02x\n", t->base)); ++ c->mode = LIT; ++ break; ++ } ++ if (e & 16) /* length */ ++ { ++ c->sub.copy.get = e & 15; ++ c->len = t->base; ++ c->mode = LENEXT; ++ break; ++ } ++ if ((e & 64) == 0) /* next table */ ++ { ++ c->sub.code.need = e; ++ c->sub.code.tree = t->next; ++ break; ++ } ++ if (e & 32) /* end of block */ ++ { ++ Tracevv((stderr, "inflate: end of block\n")); ++ c->mode = WASH; ++ break; ++ } ++ c->mode = BADCODE; /* invalid code */ ++ z->msg = "invalid literal/length code"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ case LENEXT: /* i: getting length extra (have base) */ ++ j = c->sub.copy.get; ++ NEEDBITS(j) ++ c->len += (uInt)b & inflate_mask[j]; ++ DUMPBITS(j) ++ c->sub.code.need = c->dbits; ++ c->sub.code.tree = c->dtree; ++ Tracevv((stderr, "inflate: length %u\n", c->len)); ++ c->mode = DIST; ++ case DIST: /* i: get distance next */ ++ j = c->sub.code.need; ++ NEEDBITS(j) ++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); ++ DUMPBITS(t->bits) ++ e = (uInt)(t->exop); ++ if (e & 16) /* distance */ ++ { ++ c->sub.copy.get = e & 15; ++ c->sub.copy.dist = t->base; ++ c->mode = DISTEXT; ++ break; ++ } ++ if ((e & 64) == 0) /* next table */ ++ { ++ c->sub.code.need = e; ++ c->sub.code.tree = t->next; ++ break; ++ } ++ c->mode = BADCODE; /* invalid code */ ++ z->msg = "invalid distance code"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ case DISTEXT: /* i: getting distance extra */ ++ j = c->sub.copy.get; ++ NEEDBITS(j) ++ c->sub.copy.dist += (uInt)b & inflate_mask[j]; ++ DUMPBITS(j) ++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); ++ c->mode = COPY; ++ case COPY: /* o: copying bytes in window, waiting for space */ ++#ifndef __TURBOC__ /* Turbo C bug for following expression */ ++ f = (uInt)(q - s->window) < c->sub.copy.dist ? ++ s->end - (c->sub.copy.dist - (q - s->window)) : ++ q - c->sub.copy.dist; ++#else ++ f = q - c->sub.copy.dist; ++ if ((uInt)(q - s->window) < c->sub.copy.dist) ++ f = s->end - (c->sub.copy.dist - (q - s->window)); ++#endif ++ while (c->len) ++ { ++ NEEDOUT ++ OUTBYTE(*f++) ++ if (f == s->end) ++ f = s->window; ++ c->len--; ++ } ++ c->mode = START; ++ break; ++ case LIT: /* o: got literal, waiting for output space */ ++ NEEDOUT ++ OUTBYTE(c->sub.lit) ++ c->mode = START; ++ break; ++ case WASH: /* o: got eob, possibly more output */ ++ FLUSH ++ if (s->read != s->write) ++ LEAVE ++ c->mode = END; ++ case END: ++ r = Z_STREAM_END; ++ LEAVE ++ case BADCODE: /* x: got error */ ++ r = Z_DATA_ERROR; ++ LEAVE ++ default: ++ r = Z_STREAM_ERROR; ++ LEAVE ++ } ++} ++ ++ ++local void inflate_codes_free(c, z) ++inflate_codes_statef *c; ++z_stream *z; ++{ ++ ZFREE(z, c, sizeof(struct inflate_codes_state)); ++ Tracev((stderr, "inflate: codes free\n")); ++} ++ ++/*+++++*/ ++/* inflate_util.c -- data and routines common to blocks and codes ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* copy as much as possible from the sliding window to the output area */ ++local int inflate_flush(s, z, r) ++inflate_blocks_statef *s; ++z_stream *z; ++int r; ++{ ++ uInt n; ++ Bytef *p, *q; ++ ++ /* local copies of source and destination pointers */ ++ p = z->next_out; ++ q = s->read; ++ ++ /* compute number of bytes to copy as far as end of window */ ++ n = (uInt)((q <= s->write ? s->write : s->end) - q); ++ if (n > z->avail_out) n = z->avail_out; ++ if (n && r == Z_BUF_ERROR) r = Z_OK; ++ ++ /* update counters */ ++ z->avail_out -= n; ++ z->total_out += n; ++ ++ /* update check information */ ++ if (s->checkfn != Z_NULL) ++ s->check = (*s->checkfn)(s->check, q, n); ++ ++ /* copy as far as end of window */ ++ zmemcpy(p, q, n); ++ p += n; ++ q += n; ++ ++ /* see if more to copy at beginning of window */ ++ if (q == s->end) ++ { ++ /* wrap pointers */ ++ q = s->window; ++ if (s->write == s->end) ++ s->write = s->window; ++ ++ /* compute bytes to copy */ ++ n = (uInt)(s->write - q); ++ if (n > z->avail_out) n = z->avail_out; ++ if (n && r == Z_BUF_ERROR) r = Z_OK; ++ ++ /* update counters */ ++ z->avail_out -= n; ++ z->total_out += n; ++ ++ /* update check information */ ++ if (s->checkfn != Z_NULL) ++ s->check = (*s->checkfn)(s->check, q, n); ++ ++ /* copy */ ++ zmemcpy(p, q, n); ++ p += n; ++ q += n; ++ } ++ ++ /* update pointers */ ++ z->next_out = p; ++ s->read = q; ++ ++ /* done */ ++ return r; ++} ++ ++ ++/*+++++*/ ++/* inffast.c -- process literals and length/distance pairs fast ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define base more.Base ++#define next more.Next ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++/* macros for bit input with no checking and for returning unused bytes */ ++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} ++ ++/* Called with number of bytes left to write in window at least 258 ++ (the maximum string length) and number of input bytes available ++ at least ten. The ten bytes are six bytes for the longest length/ ++ distance pair plus four bytes for overloading the bit buffer. */ ++ ++local int inflate_fast(bl, bd, tl, td, s, z) ++uInt bl, bd; ++inflate_huft *tl, *td; ++inflate_blocks_statef *s; ++z_stream *z; ++{ ++ inflate_huft *t; /* temporary pointer */ ++ uInt e; /* extra bits or operation */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ uInt ml; /* mask for literal/length tree */ ++ uInt md; /* mask for distance tree */ ++ uInt c; /* bytes to copy */ ++ uInt d; /* distance back to copy from */ ++ Bytef *r; /* copy source pointer */ ++ ++ /* load input, output, bit values */ ++ LOAD ++ ++ /* initialize masks */ ++ ml = inflate_mask[bl]; ++ md = inflate_mask[bd]; ++ ++ /* do until not enough input or output space for fast loop */ ++ do { /* assume called with m >= 258 && n >= 10 */ ++ /* get literal/length code */ ++ GRABBITS(20) /* max bits for literal/length code */ ++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) ++ { ++ DUMPBITS(t->bits) ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: * literal '%c'\n" : ++ "inflate: * literal 0x%02x\n", t->base)); ++ *q++ = (Byte)t->base; ++ m--; ++ continue; ++ } ++ do { ++ DUMPBITS(t->bits) ++ if (e & 16) ++ { ++ /* get extra bits for length */ ++ e &= 15; ++ c = t->base + ((uInt)b & inflate_mask[e]); ++ DUMPBITS(e) ++ Tracevv((stderr, "inflate: * length %u\n", c)); ++ ++ /* decode distance base of block to copy */ ++ GRABBITS(15); /* max bits for distance code */ ++ e = (t = td + ((uInt)b & md))->exop; ++ do { ++ DUMPBITS(t->bits) ++ if (e & 16) ++ { ++ /* get extra bits to add to distance base */ ++ e &= 15; ++ GRABBITS(e) /* get extra bits (up to 13) */ ++ d = t->base + ((uInt)b & inflate_mask[e]); ++ DUMPBITS(e) ++ Tracevv((stderr, "inflate: * distance %u\n", d)); ++ ++ /* do the copy */ ++ m -= c; ++ if ((uInt)(q - s->window) >= d) /* offset before dest */ ++ { /* just copy */ ++ r = q - d; ++ *q++ = *r++; c--; /* minimum count is three, */ ++ *q++ = *r++; c--; /* so unroll loop a little */ ++ } ++ else /* else offset after destination */ ++ { ++ e = d - (q - s->window); /* bytes from offset to end */ ++ r = s->end - e; /* pointer to offset */ ++ if (c > e) /* if source crosses, */ ++ { ++ c -= e; /* copy to end of window */ ++ do { ++ *q++ = *r++; ++ } while (--e); ++ r = s->window; /* copy rest from start of window */ ++ } ++ } ++ do { /* copy all or what's left */ ++ *q++ = *r++; ++ } while (--c); ++ break; ++ } ++ else if ((e & 64) == 0) ++ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; ++ else ++ { ++ z->msg = "invalid distance code"; ++ UNGRAB ++ UPDATE ++ return Z_DATA_ERROR; ++ } ++ } while (1); ++ break; ++ } ++ if ((e & 64) == 0) ++ { ++ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) ++ { ++ DUMPBITS(t->bits) ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: * literal '%c'\n" : ++ "inflate: * literal 0x%02x\n", t->base)); ++ *q++ = (Byte)t->base; ++ m--; ++ break; ++ } ++ } ++ else if (e & 32) ++ { ++ Tracevv((stderr, "inflate: * end of block\n")); ++ UNGRAB ++ UPDATE ++ return Z_STREAM_END; ++ } ++ else ++ { ++ z->msg = "invalid literal/length code"; ++ UNGRAB ++ UPDATE ++ return Z_DATA_ERROR; ++ } ++ } while (1); ++ } while (m >= 258 && n >= 10); ++ ++ /* not enough input or output--restore pointers and return */ ++ UNGRAB ++ UPDATE ++ return Z_OK; ++} ++ ++ ++/*+++++*/ ++/* zutil.c -- target dependent utility functions for the compression library ++ * Copyright (C) 1995 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ ++ ++char *zlib_version = ZLIB_VERSION; ++ ++char *z_errmsg[] = { ++"stream end", /* Z_STREAM_END 1 */ ++"", /* Z_OK 0 */ ++"file error", /* Z_ERRNO (-1) */ ++"stream error", /* Z_STREAM_ERROR (-2) */ ++"data error", /* Z_DATA_ERROR (-3) */ ++"insufficient memory", /* Z_MEM_ERROR (-4) */ ++"buffer error", /* Z_BUF_ERROR (-5) */ ++""}; ++ ++ ++/*+++++*/ ++/* adler32.c -- compute the Adler-32 checksum of a data stream ++ * Copyright (C) 1995 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ ++ ++#define BASE 65521L /* largest prime smaller than 65536 */ ++#define NMAX 5552 ++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ ++ ++#define DO1(buf) {s1 += *buf++; s2 += s1;} ++#define DO2(buf) DO1(buf); DO1(buf); ++#define DO4(buf) DO2(buf); DO2(buf); ++#define DO8(buf) DO4(buf); DO4(buf); ++#define DO16(buf) DO8(buf); DO8(buf); ++ ++/* ========================================================================= */ ++uLong adler32(adler, buf, len) ++ uLong adler; ++ Bytef *buf; ++ uInt len; ++{ ++ unsigned long s1 = adler & 0xffff; ++ unsigned long s2 = (adler >> 16) & 0xffff; ++ int k; ++ ++ if (buf == Z_NULL) return 1L; ++ ++ while (len > 0) { ++ k = len < NMAX ? len : NMAX; ++ len -= k; ++ while (k >= 16) { ++ DO16(buf); ++ k -= 16; ++ } ++ if (k != 0) do { ++ DO1(buf); ++ } while (--k); ++ s1 %= BASE; ++ s2 %= BASE; ++ } ++ return (s2 << 16) | s1; ++} +diff -Naru linux/arch/mips/zboot/Makefile linux.spi/arch/mips/zboot/Makefile +--- linux/arch/mips/zboot/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,84 @@ ++# ++# arch/mips/zboot/Makefile ++# ++# This file is subject to the terms and conditions of the GNU General Public ++# License. See the file "COPYING" in the main directory of this archive ++# for more details. ++ ++# Adapted for MIPS Pete Popov, Dan Malek ++# ++# Copyright (C) 1994 by Linus Torvalds ++# Adapted for PowerPC by Gary Thomas ++# modified by Cort (cort@cs.nmt.edu) ++# ++ ++.c.s: ++ $(CC) $(CFLAGS) -S -o $*.s $< ++.s.o: ++ $(AS) -o $*.o $< ++.c.o: ++ $(CC) $(CFLAGS) -c -o $*.o $< ++.S.s: ++ $(CPP) $(AFLAGS) -o $*.o $< ++.S.o: ++ $(CC) $(AFLAGS) -c -o $*.o $< ++ ++GZIP_FLAGS = -v9f ++ ++CFLAGS += -D__BOOTER__ -I$(TOPDIR)/arch/$(ARCH)/zboot/include ++AFLAGS += -D__BOOTER__ ++ ++BOOT_TARGETS = zImage zImage.initrd zImage.flash zImage.initrd.flash ++ ++lib/zlib.a: ++ $(MAKE) -C lib ++ ++images/vmlinux.gz: $(TOPDIR)/vmlinux ++ $(MAKE) -C images vmlinux.gz ++ ++$(BOOT_TARGETS): lib/zlib.a images/vmlinux.gz ++ifdef CONFIG_MIPS_PB1000 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_PB1500 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_PB1100 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_PB1550 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_DB1000 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_DB1100 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_DB1500 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_BOSPORUS ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_MIRAGE ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_MIPS_MTX1 ++ $(MAKE) -C pb1xxx $@ ++endif ++ifdef CONFIG_COGENT_CSB250 ++ $(MAKE) -C csb250 $@ ++endif ++ifdef CONFIG_MIPS_XXS1500 ++BOOT_DIR = xxs1500 ++endif ++ ++# Do the dirs ++clean: ++ $(MAKE) -C common clean ++ $(MAKE) -C images clean ++ $(MAKE) -C pb1xxx clean ++ $(MAKE) -C xxs1500 clean ++ ++include $(TOPDIR)/Rules.make +diff -Naru linux/arch/mips/zboot/pb1xxx/head.S linux.spi/arch/mips/zboot/pb1xxx/head.S +--- linux/arch/mips/zboot/pb1xxx/head.S 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/pb1xxx/head.S 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,149 @@ ++/* ++ * arch/mips/kernel/head.S ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 1994, 1995 Waldorf Electronics ++ * Written by Ralf Baechle and Andreas Busse ++ * Copyright (C) 1995 - 1999 Ralf Baechle ++ * Copyright (C) 1996 Paul M. Antoine ++ * Modified for DECStation and hence R3000 support by Paul M. Antoine ++ * Further modifications by David S. Miller and Harald Koerfgen ++ * Copyright (C) 1999 Silicon Graphics, Inc. ++ * ++ * Head.S contains the MIPS exception handler and startup code. ++ * ++ ************************************************************************** ++ * 9 Nov, 2000. ++ * Added Cache Error exception handler and SBDDP EJTAG debug exception. ++ * ++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. ++ ************************************************************************** ++ */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IndexInvalidate_I 0x00 ++#define IndexWriteBack_D 0x01 ++ ++ .set noreorder ++ .cprestore ++ LEAF(start) ++start: ++ bal locate ++ nop ++locate: ++ subu s8, ra, 8 /* Where we were loaded */ ++ la sp, (.stack + 8192) ++ ++ move s0, a0 /* Save boot rom start args */ ++ move s1, a1 ++ move s2, a2 ++ move s3, a3 ++ ++ la a0, start /* Where we were linked to run */ ++ ++ move a1, s8 ++ la a2, _edata ++ subu t1, a2, a0 ++ srl t1, t1, 2 ++ ++ /* copy text section */ ++ li t0, 0 ++1: lw v0, 0(a1) ++ nop ++ sw v0, 0(a0) ++ xor t0, t0, v0 ++ addu a0, 4 ++ bne a2, a0, 1b ++ addu a1, 4 ++ ++ /* Clear BSS */ ++ la a0, _edata ++ la a2, _end ++2: sw zero, 0(a0) ++ bne a2, a0, 2b ++ addu a0, 4 ++ ++ /* push the D-Cache and invalidate I-Cache */ ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexWriteBack_D, 0(k0) ++ cache IndexWriteBack_D, 32(k0) ++ cache IndexWriteBack_D, 64(k0) ++ cache IndexWriteBack_D, 96(k0) ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ /* done */ ++ ++ move a0, s8 /* load address */ ++ move a1, t1 /* length in words */ ++ move a2, t0 /* checksum */ ++ move a3, sp ++ ++ la ra, 1f ++ la k0, decompress_kernel ++ jr k0 ++ nop ++1: ++ ++ move a0, s0 ++ move a1, s1 ++ move a2, s2 ++ move a3, s3 ++ li k0, KERNEL_ENTRY ++ jr k0 ++ nop ++3: ++ b 3b ++ END(start) ++ ++ LEAF(udelay) ++udelay: ++ END(udelay) ++ ++ ++ LEAF(FlushCache) ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexWriteBack_D, 0(k0) ++ cache IndexWriteBack_D, 32(k0) ++ cache IndexWriteBack_D, 64(k0) ++ cache IndexWriteBack_D, 96(k0) ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ jr ra ++ nop ++ END(FlushCache) ++ ++ .comm .stack,4096*2,4 +diff -Naru linux/arch/mips/zboot/pb1xxx/Makefile linux.spi/arch/mips/zboot/pb1xxx/Makefile +--- linux/arch/mips/zboot/pb1xxx/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/pb1xxx/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,135 @@ ++# arch/mips/zboot/pb1xxx/Makefile ++# ++# Makefile for Alchemy Semiconductor Pb1[015]00 boards. ++# All of the boot loader code was derived from the ppc ++# boot code. ++# ++# Copyright 2001,2002 MontaVista Software Inc. ++# ++# Author: Mark A. Greer ++# mgreer@mvista.com ++# Ported and modified for mips support by ++# Pete Popov ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. ++ ++.c.s: ++ $(CC) $(CFLAGS) -S -o $*.s $< ++.s.o: ++ $(AS) -o $*.o $< ++.c.o: ++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $< ++.S.s: ++ $(CPP) $(AFLAGS) -o $*.o $< ++.S.o: ++ $(CC) $(AFLAGS) -c -o $*.o $< ++ ++######################################################################### ++# START BOARD SPECIFIC VARIABLES ++ifdef CONFIG_MIPS_PB1000 ++BNAME=pb1000 ++endif ++ ++ifdef CONFIG_MIPS_PB1100 ++BNAME=pb1100 ++endif ++ ++ifdef CONFIG_MIPS_PB1500 ++BNAME=pb1500 ++endif ++ ++ifdef CONFIG_MIPS_PB1550 ++BNAME=pb1550 ++endif ++ ++ifdef CONFIG_MIPS_DB1000 ++BNAME=db1000 ++endif ++ ++ifdef CONFIG_MIPS_DB1100 ++BNAME=db1100 ++endif ++ ++ifdef CONFIG_MIPS_DB1500 ++BNAME=db1500 ++endif ++ ++ifdef CONFIG_MIPS_BOSPORUS ++BNAME=bosporus ++endif ++ ++ifdef CONFIG_MIPS_MIRAGE ++BNAME=mirage ++endif ++ ++ifdef CONFIG_MIPS_MTX1 ++BNAME=mtx-1 ++endif ++ ++# These two variables control where the zImage is stored ++# in flash and loaded in memory. It only controls how the srec ++# file is generated, the code is the same. ++RAM_RUN_ADDR = 0x81000000 ++FLASH_LOAD_ADDR = 0xBFD00000 ++ ++# These two variables specify the free ram region ++# that can be used for temporary malloc area ++AVAIL_RAM_START=0x80400000 ++AVAIL_RAM_END=0x80800000 ++ ++# This one must match the LOADADDR in arch/mips/Makefile! ++LOADADDR=0x80100000 ++# END BOARD SPECIFIC VARIABLES ++######################################################################### ++ ++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \ ++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o ++LIBS := ../lib/zlib.a ++ ++ENTRY := ../utils/entry ++OFFSET := ../utils/offset ++SIZE := ../utils/size ++ ++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic ++OBJCOPY_ARGS = -O elf32-tradlittlemips ++ ++all: zImage ++ ++clean: ++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec ++ ++head.o: head.S $(TOPDIR)/vmlinux ++ $(CC) $(AFLAGS) \ ++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \ ++ -c -o $*.o $< ++ ++../common/misc-simple.o: ++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \ ++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \ ++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \ ++ -DLOADADDR=$(LOADADDR) \ ++ -DZIMAGE_SIZE=0 -c -o $@ $*.c ++ ++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o ++ $(OBJCOPY) \ ++ --add-section=.image=../images/vmlinux.gz \ ++ --set-section-flags=.image=contents,alloc,load,readonly,data \ ++ ../common/dummy.o image.o ++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS) ++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \ ++ -R .initrd -R .sysmap ++ ++# Here we manipulate the image in order to get it the necessary ++# srecord file we need. ++zImage: zvmlinux ++ mv zvmlinux ../images/zImage.$(BNAME) ++ $(OBJCOPY) -O srec ../images/zImage.$(BNAME) ../images/$(BNAME).srec ++ ++zImage.flash: zImage ++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \ ++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec ++ ++include $(TOPDIR)/Rules.make +diff -Naru linux/arch/mips/zboot/utils/entry linux.spi/arch/mips/zboot/utils/entry +--- linux/arch/mips/zboot/utils/entry 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/utils/entry 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,12 @@ ++#!/bin/sh ++ ++# grab the kernel_entry address from the vmlinux elf image ++entry=`$1 $2 | grep kernel_entry` ++ ++fs=`echo $entry | grep ffffffff` # check toolchain output ++ ++if [ -n "$fs" ]; then ++ echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'` ++else ++ echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'` ++fi +diff -Naru linux/arch/mips/zboot/utils/offset linux.spi/arch/mips/zboot/utils/offset +--- linux/arch/mips/zboot/utils/offset 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/utils/offset 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,3 @@ ++#!/bin/sh ++ ++echo "0x"`$1 -h $2 | grep $3 | grep -v zvmlinux| awk '{print $6}'` +diff -Naru linux/arch/mips/zboot/utils/size linux.spi/arch/mips/zboot/utils/size +--- linux/arch/mips/zboot/utils/size 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/utils/size 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'` ++echo "0x"$OFFSET +diff -Naru linux/arch/mips/zboot/xxs1500/head.S linux.spi/arch/mips/zboot/xxs1500/head.S +--- linux/arch/mips/zboot/xxs1500/head.S 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/xxs1500/head.S 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,137 @@ ++/* ++ * arch/mips/kernel/head.S ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 1994, 1995 Waldorf Electronics ++ * Written by Ralf Baechle and Andreas Busse ++ * Copyright (C) 1995 - 1999 Ralf Baechle ++ * Copyright (C) 1996 Paul M. Antoine ++ * Modified for DECStation and hence R3000 support by Paul M. Antoine ++ * Further modifications by David S. Miller and Harald Koerfgen ++ * Copyright (C) 1999 Silicon Graphics, Inc. ++ * ++ * Head.S contains the MIPS exception handler and startup code. ++ * ++ ************************************************************************** ++ * 9 Nov, 2000. ++ * Added Cache Error exception handler and SBDDP EJTAG debug exception. ++ * ++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. ++ ************************************************************************** ++ */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IndexInvalidate_I 0x00 ++ ++ .set noreorder ++ .cprestore ++ LEAF(start) ++start: ++ ++locate: ++ la sp, .stack ++ move s0, a0 ++ move s1, a1 ++ move s2, a2 ++ move s3, a3 ++ ++ la a0, start ++ ++ li a1, FLASH_LOAD_ADDR ++ la a2, _edata ++ subu t1, a2, a0 ++ srl t1, t1, 2 ++ ++ /* copy text section */ ++ li t0, 0 ++1: lw v0, 0(a1) ++ nop ++ sw v0, 0(a0) ++ xor t0, t0, v0 ++ addu a0, 4 ++ bne a2, a0, 1b ++ addu a1, 4 ++ ++ /* Clear BSS */ ++ la a0, _edata ++ la a2, _end ++2: sw zero, 0(a0) ++ bne a2, a0, 2b ++ addu a0, 4 ++ ++ /* flush the I-Cache */ ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ /* done */ ++ ++ li a0, FLASH_LOAD_ADDR /* load address */ ++ move a1, t1 /* length in words */ ++ move a2, t0 /* checksum */ ++ move a3, sp ++ ++ la ra, 1f ++ la k0, decompress_kernel ++ jr k0 ++ nop ++1: ++ ++ move a0, s0 ++ move a1, s1 ++ move a2, s2 ++ move a3, s3 ++ li k0, KERNEL_ENTRY ++ jr k0 ++ nop ++3: ++ b 3b ++ END(start) ++ ++ LEAF(udelay) ++udelay: ++ END(udelay) ++ ++ ++ LEAF(FlushCache) ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++1: ++ .set mips3 ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 1b ++ addu k0, k0, 128 ++ jr ra ++ nop ++ END(FlushCache) ++ ++ .comm .stack,4096*2,4 +diff -Naru linux/arch/mips/zboot/xxs1500/ld.script linux.spi/arch/mips/zboot/xxs1500/ld.script +--- linux/arch/mips/zboot/xxs1500/ld.script 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/xxs1500/ld.script 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,147 @@ ++OUTPUT_ARCH(mips) ++ENTRY(start) ++SECTIONS ++{ ++ /* Read-only sections, merged into text segment: */ ++ /* . = 0x81000000; */ ++ .init : { *(.init) } =0 ++ .text : ++ { ++ _ftext = . ; ++ *(.text) ++ *(.rodata) ++ *(.rodata1) ++ /* .gnu.warning sections are handled specially by elf32.em. */ ++ *(.gnu.warning) ++ } =0 ++ .kstrtab : { *(.kstrtab) } ++ ++ . = ALIGN(16); /* Exception table */ ++ __start___ex_table = .; ++ __ex_table : { *(__ex_table) } ++ __stop___ex_table = .; ++ ++ __start___dbe_table = .; /* Exception table for data bus errors */ ++ __dbe_table : { *(__dbe_table) } ++ __stop___dbe_table = .; ++ ++ __start___ksymtab = .; /* Kernel symbol table */ ++ __ksymtab : { *(__ksymtab) } ++ __stop___ksymtab = .; ++ ++ _etext = .; ++ ++ . = ALIGN(8192); ++ .data.init_task : { *(.data.init_task) } ++ ++ /* Startup code */ ++ . = ALIGN(4096); ++ __init_begin = .; ++ .text.init : { *(.text.init) } ++ .data.init : { *(.data.init) } ++ . = ALIGN(16); ++ __setup_start = .; ++ .setup.init : { *(.setup.init) } ++ __setup_end = .; ++ __initcall_start = .; ++ .initcall.init : { *(.initcall.init) } ++ __initcall_end = .; ++ . = ALIGN(4096); /* Align double page for init_task_union */ ++ __init_end = .; ++ ++ . = ALIGN(4096); ++ .data.page_aligned : { *(.data.idt) } ++ ++ . = ALIGN(32); ++ .data.cacheline_aligned : { *(.data.cacheline_aligned) } ++ ++ .fini : { *(.fini) } =0 ++ .reginfo : { *(.reginfo) } ++ /* Adjust the address for the data segment. We want to adjust up to ++ the same address within the page on the next page up. It would ++ be more correct to do this: ++ . = .; ++ The current expression does not correctly handle the case of a ++ text segment ending precisely at the end of a page; it causes the ++ data segment to skip a page. The above expression does not have ++ this problem, but it will currently (2/95) cause BFD to allocate ++ a single segment, combining both text and data, for this case. ++ This will prevent the text segment from being shared among ++ multiple executions of the program; I think that is more ++ important than losing a page of the virtual address space (note ++ that no actual memory is lost; the page which is skipped can not ++ be referenced). */ ++ . = .; ++ .data : ++ { ++ _fdata = . ; ++ *(.data) ++ ++ /* Align the initial ramdisk image (INITRD) on page boundaries. */ ++ . = ALIGN(4096); ++ __rd_start = .; ++ *(.initrd) ++ __rd_end = .; ++ . = ALIGN(4096); ++ ++ CONSTRUCTORS ++ } ++ .data1 : { *(.data1) } ++ _gp = . + 0x8000; ++ .lit8 : { *(.lit8) } ++ .lit4 : { *(.lit4) } ++ .ctors : { *(.ctors) } ++ .dtors : { *(.dtors) } ++ .got : { *(.got.plt) *(.got) } ++ .dynamic : { *(.dynamic) } ++ /* We want the small data sections together, so single-instruction offsets ++ can access them all, and initialized data all before uninitialized, so ++ we can shorten the on-disk segment size. */ ++ .sdata : { *(.sdata) } ++ . = ALIGN(4); ++ _edata = .; ++ PROVIDE (edata = .); ++ ++ __bss_start = .; ++ _fbss = .; ++ .sbss : { *(.sbss) *(.scommon) } ++ .bss : ++ { ++ *(.dynbss) ++ *(.bss) ++ *(COMMON) ++ . = ALIGN(4); ++ _end = . ; ++ PROVIDE (end = .); ++ } ++ ++ /* Sections to be discarded */ ++ /DISCARD/ : ++ { ++ *(.text.exit) ++ *(.data.exit) ++ *(.exitcall.exit) ++ } ++ ++ /* This is the MIPS specific mdebug section. */ ++ .mdebug : { *(.mdebug) } ++ /* These are needed for ELF backends which have not yet been ++ converted to the new style linker. */ ++ .stab 0 : { *(.stab) } ++ .stabstr 0 : { *(.stabstr) } ++ /* DWARF debug sections. ++ Symbols in the .debug DWARF section are relative to the beginning of the ++ section so we begin .debug at 0. It's not clear yet what needs to happen ++ for the others. */ ++ .debug 0 : { *(.debug) } ++ .debug_srcinfo 0 : { *(.debug_srcinfo) } ++ .debug_aranges 0 : { *(.debug_aranges) } ++ .debug_pubnames 0 : { *(.debug_pubnames) } ++ .debug_sfnames 0 : { *(.debug_sfnames) } ++ .line 0 : { *(.line) } ++ /* These must appear regardless of . */ ++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } ++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } ++ .comment : { *(.comment) } ++ .note : { *(.note) } ++} +diff -Naru linux/arch/mips/zboot/xxs1500/Makefile linux.spi/arch/mips/zboot/xxs1500/Makefile +--- linux/arch/mips/zboot/xxs1500/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux.spi/arch/mips/zboot/xxs1500/Makefile 2004-05-11 23:19:24.000000000 -0400 +@@ -0,0 +1,123 @@ ++# arch/mips/compressed/alchemy/Makefile ++# ++# Makefile for Alchemy Semiconductor Pb1[015]00 boards. ++# All of the boot loader code was derived from the ppc ++# boot code. ++# ++# Copyright 2001,2002 MontaVista Software Inc. ++# ++# Author: Mark A. Greer ++# mgreer@mvista.com ++# Ported and modified for mips support by ++# Pete Popov ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. ++ ++.c.s: ++ $(CC) $(CFLAGS) -S -o $*.s $< ++.s.o: ++ $(AS) -o $*.o $< ++.c.o: ++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $< ++.S.s: ++ $(CPP) $(AFLAGS) -o $*.o $< ++.S.o: ++ $(CC) $(AFLAGS) -c -o $*.o $< ++ ++######################################################################### ++# START BOARD SPECIFIC VARIABLES ++BNAME=xxs1500 ++ ++ ++# These two variables control where the zImage is stored ++# in flash and loaded in memory. If you change either one, ++# be sure to make the appropriate change to the zImage ++# rule. ++RAM_LOAD_ADDR = 0x81000000 ++FLASH_LOAD_ADDR = 0xBF000000 ++ ++# These two variables specify the free ram region ++# that can be used for temporary malloc area ++AVAIL_RAM_START=0x80400000 ++AVAIL_RAM_END=0x80800000 ++ ++# This one must match the LOADADDR in arch/mips/Makefile! ++LOADADDR=0x80100000 ++# END BOARD SPECIFIC VARIABLES ++######################################################################### ++ ++ZLINKFLAGS = -T ld.script -Ttext $(RAM_LOAD_ADDR) ++ ++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \ ++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o ++LIBS := ../lib/zlib.a ++ ++ENTRY := ../utils/entry ++OFFSET := ../utils/offset ++SIZE := ../utils/size ++ ++all: zImage ++ ++clean: ++ rm -rf *.o vmlinux* zvmlinux.* ++ ++head.o: head.S $(TOPDIR)/vmlinux ++ $(CC) -DFLASH_LOAD_ADDR=$(FLASH_LOAD_ADDR) $(AFLAGS) \ ++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \ ++ -c -o $*.o $< ++ ++../common/misc-simple.o: ++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \ ++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \ ++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \ ++ -DLOADADDR=$(LOADADDR) \ ++ -DZIMAGE_SIZE=0 -c -o $@ $*.c ++ ++# This is the first pass at building the boot loader image, ++# without knowing the file offset where the vmlinuz.gz ++# kernel will end up. We build this image, check the offset, ++# and then rebuild it with the correct offset and size ++# passed to mips-simple.c ++zvmlinux.no: $(OBJECTS) $(LIBS) ../images/vmlinux.gz ++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) ++ $(OBJCOPY) -R .comment \ ++ --add-section=image=../images/vmlinux.gz \ ++ $@.tmp $@ ++ # rm -f $@.tmp ++ ++ ++# This is the final image we build, now that we know what ++# the vmlinuz.gz offset is. ++zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz zvmlinux.no ++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ ++ -DZIMAGE_OFFSET=$(shell sh $(OFFSET) $(OBJDUMP) $@.no image) \ ++ -DZIMAGE_SIZE=$(shell sh $(SIZE) $(OBJDUMP) $@.no image) \ ++ -D__BOOTER__ \ ++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \ ++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \ ++ -DLOADADDR=$(LOADADDR) \ ++ -c -o ../common/misc-simple.o ../common/misc-simple.c ++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS) ++ $(OBJCOPY) -R .comment \ ++ --add-section=image=../images/vmlinux.gz \ ++ $@.tmp $@ ++ $(OBJCOPY) --adjust-section-vma=image+$(RAM_LOAD_ADDR) $@ ++ $(OBJCOPY) --adjust-section-vma=image+$(shell sh $(OFFSET) \ ++ $(OBJDUMP) $@.no image ) $@ ++ # rm -f $@.tmp ++ # rm -f $@.no ++ ++ ++# Here we manipulate the image in order to get it the necessary ++# srecord file we need. ++zImage: zvmlinux ++ mv zvmlinux ../images/$@.$(BNAME) ++ $(OBJCOPY) --set-section-flags=image=alloc,load,code ../images/$@.$(BNAME) ++ $(OBJCOPY) -O srec --adjust-vma 0x3e000000 \ ++ ../images/$@.$(BNAME) ../images/$@.$(BNAME).srec ++ # rm ../images/vmlinux.gz ++ ++include $(TOPDIR)/Rules.make diff --git a/packages/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff b/packages/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff index e69de29bb2..dca79a3000 100644 --- a/packages/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff +++ b/packages/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff @@ -0,0 +1,11 @@ +diff -Nurb oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile linux.m/arch/mips/zboot/pb1xxx/Makefile +--- oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:49.840408328 +0200 ++++ linux.m/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:29.736464592 +0200 +@@ -131,5 +131,7 @@ + zImage.flash: zImage + $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \ + ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec ++ $(OBJCOPY) -O binary --adjust-vma 0x3ed00000 \ ++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.bin + + include $(TOPDIR)/Rules.make diff --git a/packages/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch b/packages/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch index e69de29bb2..ca093e1a67 100644 --- a/packages/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch +++ b/packages/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch @@ -0,0 +1,3185 @@ +diff -Nurb linux-mips-2.4.27/drivers/usb/host/Config.in linux/drivers/usb/host/Config.in +--- linux-mips-2.4.27/drivers/usb/host/Config.in 2003-11-17 02:07:42.000000000 +0100 ++++ linux/drivers/usb/host/Config.in 2004-11-23 11:24:10.599627448 +0100 +@@ -17,3 +17,4 @@ + dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL + fi ++dep_tristate ' Non-PCI OHCI support' CONFIG_USB_NON_PCI_OHCI $CONFIG_USB_OHCI +diff -Nurb linux-mips-2.4.27/drivers/usb/host/usb-ohci.c linux/drivers/usb/host/usb-ohci.c +--- linux-mips-2.4.27/drivers/usb/host/usb-ohci.c 2004-04-16 05:14:18.000000000 +0200 ++++ linux/drivers/usb/host/usb-ohci.c 2004-11-23 11:24:10.602626992 +0100 +@@ -2564,6 +2564,7 @@ + hc_release_ohci (ohci); + return ret; + } ++#ifndef CONFIG_USB_NON_PCI_OHCI + ohci->flags = id->driver_data; + + /* Check for NSC87560. We have to look at the bridge (fn1) to identify +@@ -2582,6 +2583,7 @@ + printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); + if (ohci->flags & OHCI_QUIRK_AMD756) + printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); ++#endif + + if (hc_reset (ohci) < 0) { + hc_release_ohci (ohci); +@@ -2627,8 +2629,10 @@ + int temp; + int i; + ++#ifndef CONFIG_USB_NON_PCI_OHCI + if (ohci->pci_latency) + pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); ++#endif + + ohci->disabled = 1; + ohci->sleeping = 0; +@@ -2658,6 +2662,7 @@ + + /*-------------------------------------------------------------------------*/ + ++#ifndef CONFIG_USB_NON_PCI_OHCI + /* configured so that an OHCI device is always provided */ + /* always called with process context; sleeping is OK */ + +@@ -2705,6 +2710,88 @@ + } + return status; + } ++#else /* CONFIG_USB_NON_PCI_OHCI */ ++ ++// Boot options ++static int ohci_base=0, ohci_len=0; ++static int ohci_irq=-1; ++ ++MODULE_PARM(ohci_base, "i"); ++MODULE_PARM(ohci_len, "i"); ++MODULE_PARM(ohci_irq, "i"); ++MODULE_PARM_DESC(ohci_base, "IO Base address of OHCI Oper. registers"); ++MODULE_PARM_DESC(ohci_len, "IO length of OHCI Oper. registers"); ++MODULE_PARM_DESC(ohci_irq, "IRQ for OHCI interrupts"); ++ ++// bogus pci_dev ++static struct pci_dev bogus_pcidev; ++ ++static struct pci_driver ohci_pci_driver = { ++ name: "usb-ohci", ++}; ++ ++static int __devinit ++ohci_non_pci_init (void) ++{ ++ void *mem_base; ++ ++ if (!ohci_base || !ohci_len || (ohci_irq < 0)) ++ return -ENODEV; ++ ++ if (!request_mem_region (ohci_base, ohci_len, ohci_pci_driver.name)) { ++ dbg ("controller already in use"); ++ return -EBUSY; ++ } ++ ++ mem_base = ioremap_nocache (ohci_base, ohci_len); ++ if (!mem_base) { ++ err("Error mapping OHCI memory"); ++ return -EFAULT; ++ } ++ ++ /* ++ * Fill in the bogus pci_dev. Only those members actually ++ * dereferenced in this driver are initialized. ++ */ ++ memset(&bogus_pcidev, 0, sizeof(struct pci_dev)); ++ strcpy(bogus_pcidev.name, "non-PCI OHCI"); ++ strcpy(bogus_pcidev.slot_name, "builtin"); ++ bogus_pcidev.resource[0].name = "OHCI Operational Registers"; ++ bogus_pcidev.resource[0].start = ohci_base; ++ bogus_pcidev.resource[0].end = ohci_base + ohci_len; ++ bogus_pcidev.resource[0].flags = 0; ++ bogus_pcidev.irq = ohci_irq; ++ ++ return hc_found_ohci (&bogus_pcidev, bogus_pcidev.irq, mem_base, NULL); ++} ++ ++#ifndef MODULE ++ ++static int __init ++ohci_setup (char* options) ++{ ++ char* this_opt; ++ ++ if (!options || !*options) ++ return 0; ++ ++ for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) { ++ if (!strncmp(this_opt, "base:", 5)) { ++ ohci_base = simple_strtoul(this_opt+5, NULL, 0); ++ } else if (!strncmp(this_opt, "len:", 4)) { ++ ohci_len = simple_strtoul(this_opt+4, NULL, 0); ++ } else if (!strncmp(this_opt, "irq:", 4)) { ++ ohci_irq = simple_strtoul(this_opt+4, NULL, 0); ++ } ++ } ++ return 0; ++} ++ ++__setup("usb_ohci=", ohci_setup); ++ ++#endif /* !MODULE */ ++ ++#endif /* CONFIG_USB_NON_PCI_OHCI */ + + /*-------------------------------------------------------------------------*/ + +@@ -2745,6 +2832,7 @@ + } + + ++#ifndef CONFIG_USB_NON_PCI_OHCI + #ifdef CONFIG_PM + + /*-------------------------------------------------------------------------*/ +@@ -2983,20 +3071,29 @@ + resume: ohci_pci_resume, + #endif /* PM */ + }; ++#endif /* CONFIG_USB_NON_PCI_OHCI */ + + + /*-------------------------------------------------------------------------*/ + + static int __init ohci_hcd_init (void) + { ++#ifndef CONFIG_USB_NON_PCI_OHCI + return pci_module_init (&ohci_pci_driver); ++#else ++ return ohci_non_pci_init(); ++#endif + } + + /*-------------------------------------------------------------------------*/ + + static void __exit ohci_hcd_cleanup (void) + { ++#ifndef CONFIG_USB_NON_PCI_OHCI + pci_unregister_driver (&ohci_pci_driver); ++#else ++ ohci_pci_remove(&bogus_pcidev); ++#endif + } + + module_init (ohci_hcd_init); +diff -Nurb linux-mips-2.4.27/drivers/usb/host/usb-ohci.c.orig linux/drivers/usb/host/usb-ohci.c.orig +--- linux-mips-2.4.27/drivers/usb/host/usb-ohci.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/usb/host/usb-ohci.c.orig 2004-11-23 11:21:54.984244120 +0100 +@@ -0,0 +1,3008 @@ ++/* ++ * URB OHCI HCD (Host Controller Driver) for USB. ++ * ++ * (C) Copyright 1999 Roman Weissgaerber ++ * (C) Copyright 2000-2001 David Brownell ++ * ++ * [ Initialisation is based on Linus' ] ++ * [ uhci code and gregs ohci fragments ] ++ * [ (C) Copyright 1999 Linus Torvalds ] ++ * [ (C) Copyright 1999 Gregory P. Smith] ++ * ++ * ++ * History: ++ * ++ * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX ) ++ * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on ++ * load failure (Matthew Frederickson) ++ * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and ++ * make interrupt unlink-in-completion work (db) ++ * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes) ++ * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt) ++ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); ++ pci_map_single (db) ++ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db) ++ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam) ++ * ++ * 2000/09/26 fixed races in removing the private portion of the urb ++ * 2000/09/07 disable bulk and control lists when unlinking the last ++ * endpoint descriptor in order to avoid unrecoverable errors on ++ * the Lucent chips. (rwc@sgi) ++ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some ++ * urb unlink probs, indentation fixes ++ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from ++ * device unplugs. ++ * 2000/06/28 use PCI hotplug framework, for better power management ++ * and for Cardbus support (David Brownell) ++ * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling ++ * when the controller loses power; handle UE; cleanup; ... ++ * ++ * v5.2 1999/12/07 URB 3rd preview, ++ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) ++ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume ++ * i386: HUB, Keyboard, Mouse, Printer ++ * ++ * v4.3 1999/10/27 multiple HCs, bulk_request ++ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes ++ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. ++ * v4.0 1999/08/18 ++ * v3.0 1999/06/25 ++ * v2.1 1999/05/09 code clean up ++ * v2.0 1999/05/04 ++ * v1.0 1999/04/27 initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for in_interrupt() */ ++#undef DEBUG ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define OHCI_USE_NPS // force NoPowerSwitching mode ++// #define OHCI_VERBOSE_DEBUG /* not always helpful */ ++ ++#include "usb-ohci.h" ++ ++#include "../hcd.h" ++ ++#ifdef CONFIG_PMAC_PBOOK ++#include ++#include ++#include ++#ifndef CONFIG_PM ++#define CONFIG_PM ++#endif ++#endif ++ ++ ++/* ++ * Version Information ++ */ ++#define DRIVER_VERSION "v5.3" ++#define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" ++#define DRIVER_DESC "USB OHCI Host Controller Driver" ++ ++/* For initializing controller (mask in an HCFS mode too) */ ++#define OHCI_CONTROL_INIT \ ++ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE ++ ++#define OHCI_UNLINK_TIMEOUT (HZ / 10) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* AMD-756 (D2 rev) reports corrupt register contents in some cases. ++ * The erratum (#4) description is incorrect. AMD's workaround waits ++ * till some bits (mostly reserved) are clear; ok for all revs. ++ */ ++#define read_roothub(hc, register, mask) ({ \ ++ u32 temp = readl (&hc->regs->roothub.register); \ ++ if (hc->flags & OHCI_QUIRK_AMD756) \ ++ while (temp & mask) \ ++ temp = readl (&hc->regs->roothub.register); \ ++ temp; }) ++ ++static u32 roothub_a (struct ohci *hc) ++ { return read_roothub (hc, a, 0xfc0fe000); } ++static inline u32 roothub_b (struct ohci *hc) ++ { return readl (&hc->regs->roothub.b); } ++static inline u32 roothub_status (struct ohci *hc) ++ { return readl (&hc->regs->roothub.status); } ++static u32 roothub_portstatus (struct ohci *hc, int i) ++ { return read_roothub (hc, portstatus [i], 0xffe0fce0); } ++ ++ ++/*-------------------------------------------------------------------------* ++ * URB support functions ++ *-------------------------------------------------------------------------*/ ++ ++static void ohci_complete_add(struct ohci *ohci, struct urb *urb) ++{ ++ ++ if (urb->hcpriv != NULL) { ++ printk("completing with non-null priv!\n"); ++ return; ++ } ++ ++ if (ohci->complete_tail == NULL) { ++ ohci->complete_head = urb; ++ ohci->complete_tail = urb; ++ } else { ++ ohci->complete_head->hcpriv = urb; ++ ohci->complete_tail = urb; ++ } ++} ++ ++static inline struct urb *ohci_complete_get(struct ohci *ohci) ++{ ++ struct urb *urb; ++ ++ if ((urb = ohci->complete_head) == NULL) ++ return NULL; ++ if (urb == ohci->complete_tail) { ++ ohci->complete_tail = NULL; ++ ohci->complete_head = NULL; ++ } else { ++ ohci->complete_head = urb->hcpriv; ++ } ++ urb->hcpriv = NULL; ++ return urb; ++} ++ ++static inline void ohci_complete(struct ohci *ohci) ++{ ++ struct urb *urb; ++ ++ spin_lock(&ohci->ohci_lock); ++ while ((urb = ohci_complete_get(ohci)) != NULL) { ++ spin_unlock(&ohci->ohci_lock); ++ if (urb->dev) { ++ usb_dec_dev_use (urb->dev); ++ urb->dev = NULL; ++ } ++ if (urb->complete) ++ (*urb->complete)(urb); ++ spin_lock(&ohci->ohci_lock); ++ } ++ spin_unlock(&ohci->ohci_lock); ++} ++ ++/* free HCD-private data associated with this URB */ ++ ++static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) ++{ ++ int i; ++ int last = urb_priv->length - 1; ++ int len; ++ int dir; ++ struct td *td; ++ ++ if (last >= 0) { ++ ++ /* ISOC, BULK, INTR data buffer starts at td 0 ++ * CTRL setup starts at td 0 */ ++ td = urb_priv->td [0]; ++ ++ len = td->urb->transfer_buffer_length, ++ dir = usb_pipeout (td->urb->pipe) ++ ? PCI_DMA_TODEVICE ++ : PCI_DMA_FROMDEVICE; ++ ++ /* unmap CTRL URB setup */ ++ if (usb_pipecontrol (td->urb->pipe)) { ++ pci_unmap_single (hc->ohci_dev, ++ td->data_dma, 8, PCI_DMA_TODEVICE); ++ ++ /* CTRL data buffer starts at td 1 if len > 0 */ ++ if (len && last > 0) ++ td = urb_priv->td [1]; ++ } ++ ++ /* unmap data buffer */ ++ if (len && td->data_dma) ++ pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir); ++ ++ for (i = 0; i <= last; i++) { ++ td = urb_priv->td [i]; ++ if (td) ++ td_free (hc, td); ++ } ++ } ++ ++ kfree (urb_priv); ++} ++ ++static void urb_rm_priv_locked (struct urb * urb) ++{ ++ urb_priv_t * urb_priv = urb->hcpriv; ++ ++ if (urb_priv) { ++ urb->hcpriv = NULL; ++ ++#ifdef DO_TIMEOUTS ++ if (urb->timeout) { ++ list_del (&urb->urb_list); ++ urb->timeout -= jiffies; ++ } ++#endif ++ ++ /* Release int/iso bandwidth */ ++ if (urb->bandwidth) { ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_INTERRUPT: ++ usb_release_bandwidth (urb->dev, urb, 0); ++ break; ++ case PIPE_ISOCHRONOUS: ++ usb_release_bandwidth (urb->dev, urb, 1); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); ++ } else { ++ if (urb->dev != NULL) { ++ err ("Non-null dev at rm_priv time"); ++ // urb->dev = NULL; ++ } ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef DEBUG ++static int sohci_get_current_frame_number (struct usb_device * dev); ++ ++/* debug| print the main components of an URB ++ * small: 0) header + data packets 1) just header */ ++ ++static void urb_print (struct urb * urb, char * str, int small) ++{ ++ unsigned int pipe= urb->pipe; ++ ++ if (!urb->dev || !urb->dev->bus) { ++ dbg("%s URB: no dev", str); ++ return; ++ } ++ ++#ifndef OHCI_VERBOSE_DEBUG ++ if (urb->status != 0) ++#endif ++ dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", ++ str, ++ sohci_get_current_frame_number (urb->dev), ++ usb_pipedevice (pipe), ++ usb_pipeendpoint (pipe), ++ usb_pipeout (pipe)? 'O': 'I', ++ usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): ++ (usb_pipecontrol (pipe)? "CTRL": "BULK"), ++ urb->transfer_flags, ++ urb->actual_length, ++ urb->transfer_buffer_length, ++ urb->status, urb->status); ++#ifdef OHCI_VERBOSE_DEBUG ++ if (!small) { ++ int i, len; ++ ++ if (usb_pipecontrol (pipe)) { ++ printk (KERN_DEBUG __FILE__ ": cmd(8):"); ++ for (i = 0; i < 8 ; i++) ++ printk (" %02x", ((__u8 *) urb->setup_packet) [i]); ++ printk ("\n"); ++ } ++ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { ++ printk (KERN_DEBUG __FILE__ ": data(%d/%d):", ++ urb->actual_length, ++ urb->transfer_buffer_length); ++ len = usb_pipeout (pipe)? ++ urb->transfer_buffer_length: urb->actual_length; ++ for (i = 0; i < 16 && i < len; i++) ++ printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); ++ printk ("%s stat:%d\n", i < len? "...": "", urb->status); ++ } ++ } ++#endif ++} ++ ++/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ ++void ep_print_int_eds (ohci_t * ohci, char * str) { ++ int i, j; ++ __u32 * ed_p; ++ for (i= 0; i < 32; i++) { ++ j = 5; ++ ed_p = &(ohci->hcca->int_table [i]); ++ if (*ed_p == 0) ++ continue; ++ printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i); ++ while (*ed_p != 0 && j--) { ++ ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); ++ printk (" ed: %4x;", ed->hwINFO); ++ ed_p = &ed->hwNextED; ++ } ++ printk ("\n"); ++ } ++} ++ ++ ++static void ohci_dump_intr_mask (char *label, __u32 mask) ++{ ++ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", ++ label, ++ mask, ++ (mask & OHCI_INTR_MIE) ? " MIE" : "", ++ (mask & OHCI_INTR_OC) ? " OC" : "", ++ (mask & OHCI_INTR_RHSC) ? " RHSC" : "", ++ (mask & OHCI_INTR_FNO) ? " FNO" : "", ++ (mask & OHCI_INTR_UE) ? " UE" : "", ++ (mask & OHCI_INTR_RD) ? " RD" : "", ++ (mask & OHCI_INTR_SF) ? " SF" : "", ++ (mask & OHCI_INTR_WDH) ? " WDH" : "", ++ (mask & OHCI_INTR_SO) ? " SO" : "" ++ ); ++} ++ ++static void maybe_print_eds (char *label, __u32 value) ++{ ++ if (value) ++ dbg ("%s %08x", label, value); ++} ++ ++static char *hcfs2string (int state) ++{ ++ switch (state) { ++ case OHCI_USB_RESET: return "reset"; ++ case OHCI_USB_RESUME: return "resume"; ++ case OHCI_USB_OPER: return "operational"; ++ case OHCI_USB_SUSPEND: return "suspend"; ++ } ++ return "?"; ++} ++ ++// dump control and status registers ++static void ohci_dump_status (ohci_t *controller) ++{ ++ struct ohci_regs *regs = controller->regs; ++ __u32 temp; ++ ++ temp = readl (®s->revision) & 0xff; ++ if (temp != 0x10) ++ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); ++ ++ temp = readl (®s->control); ++ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, ++ (temp & OHCI_CTRL_RWE) ? " RWE" : "", ++ (temp & OHCI_CTRL_RWC) ? " RWC" : "", ++ (temp & OHCI_CTRL_IR) ? " IR" : "", ++ hcfs2string (temp & OHCI_CTRL_HCFS), ++ (temp & OHCI_CTRL_BLE) ? " BLE" : "", ++ (temp & OHCI_CTRL_CLE) ? " CLE" : "", ++ (temp & OHCI_CTRL_IE) ? " IE" : "", ++ (temp & OHCI_CTRL_PLE) ? " PLE" : "", ++ temp & OHCI_CTRL_CBSR ++ ); ++ ++ temp = readl (®s->cmdstatus); ++ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, ++ (temp & OHCI_SOC) >> 16, ++ (temp & OHCI_OCR) ? " OCR" : "", ++ (temp & OHCI_BLF) ? " BLF" : "", ++ (temp & OHCI_CLF) ? " CLF" : "", ++ (temp & OHCI_HCR) ? " HCR" : "" ++ ); ++ ++ ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); ++ ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); ++ // intrdisable always same as intrenable ++ // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); ++ ++ maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); ++ ++ maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); ++ maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); ++ ++ maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); ++ maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); ++ ++ maybe_print_eds ("donehead", readl (®s->donehead)); ++} ++ ++static void ohci_dump_roothub (ohci_t *controller, int verbose) ++{ ++ __u32 temp, ndp, i; ++ ++ temp = roothub_a (controller); ++ if (temp == ~(u32)0) ++ return; ++ ndp = (temp & RH_A_NDP); ++ ++ if (verbose) { ++ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, ++ ((temp & RH_A_POTPGT) >> 24) & 0xff, ++ (temp & RH_A_NOCP) ? " NOCP" : "", ++ (temp & RH_A_OCPM) ? " OCPM" : "", ++ (temp & RH_A_DT) ? " DT" : "", ++ (temp & RH_A_NPS) ? " NPS" : "", ++ (temp & RH_A_PSM) ? " PSM" : "", ++ ndp ++ ); ++ temp = roothub_b (controller); ++ dbg ("roothub.b: %08x PPCM=%04x DR=%04x", ++ temp, ++ (temp & RH_B_PPCM) >> 16, ++ (temp & RH_B_DR) ++ ); ++ temp = roothub_status (controller); ++ dbg ("roothub.status: %08x%s%s%s%s%s%s", ++ temp, ++ (temp & RH_HS_CRWE) ? " CRWE" : "", ++ (temp & RH_HS_OCIC) ? " OCIC" : "", ++ (temp & RH_HS_LPSC) ? " LPSC" : "", ++ (temp & RH_HS_DRWE) ? " DRWE" : "", ++ (temp & RH_HS_OCI) ? " OCI" : "", ++ (temp & RH_HS_LPS) ? " LPS" : "" ++ ); ++ } ++ ++ for (i = 0; i < ndp; i++) { ++ temp = roothub_portstatus (controller, i); ++ dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", ++ i, ++ temp, ++ (temp & RH_PS_PRSC) ? " PRSC" : "", ++ (temp & RH_PS_OCIC) ? " OCIC" : "", ++ (temp & RH_PS_PSSC) ? " PSSC" : "", ++ (temp & RH_PS_PESC) ? " PESC" : "", ++ (temp & RH_PS_CSC) ? " CSC" : "", ++ ++ (temp & RH_PS_LSDA) ? " LSDA" : "", ++ (temp & RH_PS_PPS) ? " PPS" : "", ++ (temp & RH_PS_PRS) ? " PRS" : "", ++ (temp & RH_PS_POCI) ? " POCI" : "", ++ (temp & RH_PS_PSS) ? " PSS" : "", ++ ++ (temp & RH_PS_PES) ? " PES" : "", ++ (temp & RH_PS_CCS) ? " CCS" : "" ++ ); ++ } ++} ++ ++static void ohci_dump (ohci_t *controller, int verbose) ++{ ++ dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); ++ ++ // dumps some of the state we know about ++ ohci_dump_status (controller); ++ if (verbose) ++ ep_print_int_eds (controller, "hcca"); ++ dbg ("hcca frame #%04x", controller->hcca->frame_no); ++ ohci_dump_roothub (controller, 1); ++} ++ ++ ++#endif ++ ++/*-------------------------------------------------------------------------* ++ * Interface functions (URB) ++ *-------------------------------------------------------------------------*/ ++ ++/* return a request to the completion handler */ ++ ++static int sohci_return_urb (struct ohci *hc, struct urb * urb) ++{ ++ urb_priv_t * urb_priv = urb->hcpriv; ++ struct urb * urbt; ++ int i; ++ ++ if (!urb_priv) ++ return -1; /* urb already unlinked */ ++ ++ /* just to be sure */ ++ if (!urb->complete) { ++ urb_rm_priv_locked (urb); ++ ohci_complete_add(hc, urb); /* Just usb_dec_dev_use */ ++ return -1; ++ } ++ ++#ifdef DEBUG ++ urb_print (urb, "RET", usb_pipeout (urb->pipe)); ++#endif ++ ++ switch (usb_pipetype (urb->pipe)) { ++ case PIPE_INTERRUPT: ++ pci_unmap_single (hc->ohci_dev, ++ urb_priv->td [0]->data_dma, ++ urb->transfer_buffer_length, ++ usb_pipeout (urb->pipe) ++ ? PCI_DMA_TODEVICE ++ : PCI_DMA_FROMDEVICE); ++ if (urb->interval) { ++ urb->complete (urb); ++ ++ /* implicitly requeued */ ++ urb->actual_length = 0; ++ urb->status = -EINPROGRESS; ++ td_submit_urb (urb); ++ } else { ++ urb_rm_priv_locked (urb); ++ ohci_complete_add(hc, urb); ++ } ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); ++ if (urbt) { /* send the reply and requeue URB */ ++ pci_unmap_single (hc->ohci_dev, ++ urb_priv->td [0]->data_dma, ++ urb->transfer_buffer_length, ++ usb_pipeout (urb->pipe) ++ ? PCI_DMA_TODEVICE ++ : PCI_DMA_FROMDEVICE); ++ urb->complete (urb); ++ urb->actual_length = 0; ++ urb->status = USB_ST_URB_PENDING; ++ urb->start_frame = urb_priv->ed->last_iso + 1; ++ if (urb_priv->state != URB_DEL) { ++ for (i = 0; i < urb->number_of_packets; i++) { ++ urb->iso_frame_desc[i].actual_length = 0; ++ urb->iso_frame_desc[i].status = -EXDEV; ++ } ++ td_submit_urb (urb); ++ } ++ ++ } else { /* unlink URB, call complete */ ++ urb_rm_priv_locked (urb); ++ ohci_complete_add(hc, urb); ++ } ++ break; ++ ++ case PIPE_BULK: ++ case PIPE_CONTROL: /* unlink URB, call complete */ ++ urb_rm_priv_locked (urb); ++ ohci_complete_add(hc, urb); ++ break; ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* get a transfer request */ ++ ++static int sohci_submit_urb (struct urb * urb) ++{ ++ ohci_t * ohci; ++ ed_t * ed; ++ urb_priv_t * urb_priv; ++ unsigned int pipe = urb->pipe; ++ int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); ++ int i, size = 0; ++ unsigned long flags; ++ int bustime = 0; ++ int mem_flags = GFP_ATOMIC; ++ ++ if (!urb->dev || !urb->dev->bus) ++ return -ENODEV; ++ ++ if (urb->hcpriv) /* urb already in use */ ++ return -EINVAL; ++ ++// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) ++// return -EPIPE; ++ ++ usb_inc_dev_use (urb->dev); ++ ohci = (ohci_t *) urb->dev->bus->hcpriv; ++ ++#ifdef DEBUG ++ urb_print (urb, "SUB", usb_pipein (pipe)); ++#endif ++ ++ /* handle a request to the virtual root hub */ ++ if (usb_pipedevice (pipe) == ohci->rh.devnum) ++ return rh_submit_urb (urb); ++ ++ spin_lock_irqsave(&ohci->ohci_lock, flags); ++ ++ /* when controller's hung, permit only roothub cleanup attempts ++ * such as powering down ports */ ++ if (ohci->disabled) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -ESHUTDOWN; ++ } ++ ++ /* every endpoint has a ed, locate and fill it */ ++ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -ENOMEM; ++ } ++ ++ /* for the private part of the URB we need the number of TDs (size) */ ++ switch (usb_pipetype (pipe)) { ++ case PIPE_BULK: /* one TD for every 4096 Byte */ ++ size = (urb->transfer_buffer_length - 1) / 4096 + 1; ++ ++ /* If the transfer size is multiple of the pipe mtu, ++ * we may need an extra TD to create a empty frame ++ * Jean II */ ++ if ((urb->transfer_flags & USB_ZERO_PACKET) && ++ usb_pipeout (pipe) && ++ (urb->transfer_buffer_length != 0) && ++ ((urb->transfer_buffer_length % maxps) == 0)) ++ size++; ++ break; ++ case PIPE_ISOCHRONOUS: /* number of packets from URB */ ++ size = urb->number_of_packets; ++ if (size <= 0) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -EINVAL; ++ } ++ for (i = 0; i < urb->number_of_packets; i++) { ++ urb->iso_frame_desc[i].actual_length = 0; ++ urb->iso_frame_desc[i].status = -EXDEV; ++ } ++ break; ++ case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ ++ size = (urb->transfer_buffer_length == 0)? 2: ++ (urb->transfer_buffer_length - 1) / 4096 + 3; ++ break; ++ case PIPE_INTERRUPT: /* one TD */ ++ size = 1; ++ break; ++ } ++ ++ /* allocate the private part of the URB */ ++ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), ++ GFP_ATOMIC); ++ if (!urb_priv) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -ENOMEM; ++ } ++ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); ++ ++ /* fill the private part of the URB */ ++ urb_priv->length = size; ++ urb_priv->ed = ed; ++ ++ /* allocate the TDs (updating hash chains) */ ++ for (i = 0; i < size; i++) { ++ urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); ++ if (!urb_priv->td[i]) { ++ urb_priv->length = i; ++ urb_free_priv (ohci, urb_priv); ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -ENOMEM; ++ } ++ } ++ ++ if (ed->state == ED_NEW || (ed->state & ED_DEL)) { ++ urb_free_priv (ohci, urb_priv); ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return -EINVAL; ++ } ++ ++ /* allocate and claim bandwidth if needed; ISO ++ * needs start frame index if it was't provided. ++ */ ++ switch (usb_pipetype (pipe)) { ++ case PIPE_ISOCHRONOUS: ++ if (urb->transfer_flags & USB_ISO_ASAP) { ++ urb->start_frame = ((ed->state == ED_OPER) ++ ? (ed->last_iso + 1) ++ : (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff; ++ } ++ /* FALLTHROUGH */ ++ case PIPE_INTERRUPT: ++ if (urb->bandwidth == 0) { ++ bustime = usb_check_bandwidth (urb->dev, urb); ++ } ++ if (bustime < 0) { ++ urb_free_priv (ohci, urb_priv); ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ return bustime; ++ } ++ usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe)); ++#ifdef DO_TIMEOUTS ++ urb->timeout = 0; ++#endif ++ } ++ ++ urb->actual_length = 0; ++ urb->hcpriv = urb_priv; ++ urb->status = USB_ST_URB_PENDING; ++ ++ /* link the ed into a chain if is not already */ ++ if (ed->state != ED_OPER) ++ ep_link (ohci, ed); ++ ++ /* fill the TDs and link it to the ed */ ++ td_submit_urb (urb); ++ ++#ifdef DO_TIMEOUTS ++ /* maybe add to ordered list of timeouts */ ++ if (urb->timeout) { ++ struct list_head *entry; ++ ++ urb->timeout += jiffies; ++ ++ list_for_each (entry, &ohci->timeout_list) { ++ struct urb *next_urb; ++ ++ next_urb = list_entry (entry, struct urb, urb_list); ++ if (time_after_eq (urb->timeout, next_urb->timeout)) ++ break; ++ } ++ list_add (&urb->urb_list, entry); ++ ++ /* drive timeouts by SF (messy, but works) */ ++ writel (OHCI_INTR_SF, &ohci->regs->intrenable); ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ } ++#endif ++ ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* deactivate all TDs and remove the private part of the URB */ ++/* interrupt callers must use async unlink mode */ ++ ++static int sohci_unlink_urb (struct urb * urb) ++{ ++ unsigned long flags; ++ ohci_t * ohci; ++ ++ if (!urb) /* just to be sure */ ++ return -EINVAL; ++ ++ if (!urb->dev || !urb->dev->bus) ++ return -ENODEV; ++ ++ ohci = (ohci_t *) urb->dev->bus->hcpriv; ++ ++#ifdef DEBUG ++ urb_print (urb, "UNLINK", 1); ++#endif ++ ++ /* handle a request to the virtual root hub */ ++ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) ++ return rh_unlink_urb (urb); ++ ++ spin_lock_irqsave(&ohci->ohci_lock, flags); ++ if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { ++ if (!ohci->disabled) { ++ urb_priv_t * urb_priv; ++ ++ /* interrupt code may not sleep; it must use ++ * async status return to unlink pending urbs. ++ */ ++ if (!(urb->transfer_flags & USB_ASYNC_UNLINK) ++ && in_interrupt ()) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ err ("bug in call from %p; use async!", ++ __builtin_return_address(0)); ++ return -EWOULDBLOCK; ++ } ++ ++ /* flag the urb and its TDs for deletion in some ++ * upcoming SF interrupt delete list processing ++ */ ++ urb_priv = urb->hcpriv; ++ ++ if (!urb_priv || (urb_priv->state == URB_DEL)) { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ return 0; ++ } ++ ++ urb_priv->state = URB_DEL; ++ ep_rm_ed (urb->dev, urb_priv->ed); ++ urb_priv->ed->state |= ED_URB_DEL; ++ ++ if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { ++ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); ++ DECLARE_WAITQUEUE (wait, current); ++ int timeout = OHCI_UNLINK_TIMEOUT; ++ ++ add_wait_queue (&unlink_wakeup, &wait); ++ urb_priv->wait = &unlink_wakeup; ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ ++ /* wait until all TDs are deleted */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ while (timeout && (urb->status == USB_ST_URB_PENDING)) { ++ timeout = schedule_timeout (timeout); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue (&unlink_wakeup, &wait); ++ if (urb->status == USB_ST_URB_PENDING) { ++ err ("unlink URB timeout"); ++ return -ETIMEDOUT; ++ } ++ ++ usb_dec_dev_use (urb->dev); ++ urb->dev = NULL; ++ if (urb->complete) ++ urb->complete (urb); ++ } else { ++ /* usb_dec_dev_use done in dl_del_list() */ ++ urb->status = -EINPROGRESS; ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ return -EINPROGRESS; ++ } ++ } else { ++ urb_rm_priv_locked (urb); ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (urb->dev); ++ urb->dev = NULL; ++ if (urb->transfer_flags & USB_ASYNC_UNLINK) { ++ urb->status = -ECONNRESET; ++ if (urb->complete) ++ urb->complete (urb); ++ } else ++ urb->status = -ENOENT; ++ } ++ } else { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* allocate private data space for a usb device */ ++ ++static int sohci_alloc_dev (struct usb_device *usb_dev) ++{ ++ struct ohci_device * dev; ++ ++ dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS); ++ if (!dev) ++ return -ENOMEM; ++ ++ usb_dev->hcpriv = dev; ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* may be called from interrupt context */ ++/* frees private data space of usb device */ ++ ++static int sohci_free_dev (struct usb_device * usb_dev) ++{ ++ unsigned long flags; ++ int i, cnt = 0; ++ ed_t * ed; ++ struct ohci_device * dev = usb_to_ohci (usb_dev); ++ ohci_t * ohci = usb_dev->bus->hcpriv; ++ ++ if (!dev) ++ return 0; ++ ++ if (usb_dev->devnum >= 0) { ++ ++ /* driver disconnects should have unlinked all urbs ++ * (freeing all the TDs, unlinking EDs) but we need ++ * to defend against bugs that prevent that. ++ */ ++ spin_lock_irqsave(&ohci->ohci_lock, flags); ++ for(i = 0; i < NUM_EDS; i++) { ++ ed = &(dev->ed[i]); ++ if (ed->state != ED_NEW) { ++ if (ed->state == ED_OPER) { ++ /* driver on that interface didn't unlink an urb */ ++ dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", ++ ohci->ohci_dev->slot_name, usb_dev->devnum, i); ++ ep_unlink (ohci, ed); ++ } ++ ep_rm_ed (usb_dev, ed); ++ ed->state = ED_DEL; ++ cnt++; ++ } ++ } ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ ++ /* if the controller is running, tds for those unlinked ++ * urbs get freed by dl_del_list at the next SF interrupt ++ */ ++ if (cnt > 0) { ++ ++ if (ohci->disabled) { ++ /* FIXME: Something like this should kick in, ++ * though it's currently an exotic case ... ++ * the controller won't ever be touching ++ * these lists again!! ++ dl_del_list (ohci, ++ le16_to_cpu (ohci->hcca->frame_no) & 1); ++ */ ++ warn ("TD leak, %d", cnt); ++ ++ } else if (!in_interrupt ()) { ++ DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); ++ DECLARE_WAITQUEUE (wait, current); ++ int timeout = OHCI_UNLINK_TIMEOUT; ++ ++ /* SF interrupt handler calls dl_del_list */ ++ add_wait_queue (&freedev_wakeup, &wait); ++ dev->wait = &freedev_wakeup; ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ while (timeout && dev->ed_cnt) ++ timeout = schedule_timeout (timeout); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue (&freedev_wakeup, &wait); ++ if (dev->ed_cnt) { ++ err ("free device %d timeout", usb_dev->devnum); ++ return -ETIMEDOUT; ++ } ++ } else { ++ /* likely some interface's driver has a refcount bug */ ++ err ("bus %s devnum %d deletion in interrupt", ++ ohci->ohci_dev->slot_name, usb_dev->devnum); ++ BUG (); ++ } ++ } ++ } ++ ++ /* free device, and associated EDs */ ++ dev_free (ohci, dev); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* tell us the current USB frame number */ ++ ++static int sohci_get_current_frame_number (struct usb_device *usb_dev) ++{ ++ ohci_t * ohci = usb_dev->bus->hcpriv; ++ ++ return le16_to_cpu (ohci->hcca->frame_no); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct usb_operations sohci_device_operations = { ++ sohci_alloc_dev, ++ sohci_free_dev, ++ sohci_get_current_frame_number, ++ sohci_submit_urb, ++ sohci_unlink_urb ++}; ++ ++/*-------------------------------------------------------------------------* ++ * ED handling functions ++ *-------------------------------------------------------------------------*/ ++ ++/* search for the right branch to insert an interrupt ed into the int tree ++ * do some load ballancing; ++ * returns the branch and ++ * sets the interval to interval = 2^integer (ld (interval)) */ ++ ++static int ep_int_ballance (ohci_t * ohci, int interval, int load) ++{ ++ int i, branch = 0; ++ ++ /* search for the least loaded interrupt endpoint branch of all 32 branches */ ++ for (i = 0; i < 32; i++) ++ if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; ++ ++ branch = branch % interval; ++ for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load; ++ ++ return branch; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* 2^int( ld (inter)) */ ++ ++static int ep_2_n_interval (int inter) ++{ ++ int i; ++ for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); ++ return 1 << i; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* the int tree is a binary tree ++ * in order to process it sequentially the indexes of the branches have to be mapped ++ * the mapping reverses the bits of a word of num_bits length */ ++ ++static int ep_rev (int num_bits, int word) ++{ ++ int i, wout = 0; ++ ++ for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); ++ return wout; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* link an ed into one of the HC chains */ ++ ++static int ep_link (ohci_t * ohci, ed_t * edi) ++{ ++ int int_branch; ++ int i; ++ int inter; ++ int interval; ++ int load; ++ __u32 * ed_p; ++ volatile ed_t * ed = edi; ++ ++ ed->state = ED_OPER; ++ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ ed->hwNextED = 0; ++ if (ohci->ed_controltail == NULL) { ++ writel (ed->dma, &ohci->regs->ed_controlhead); ++ } else { ++ ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); ++ } ++ ed->ed_prev = ohci->ed_controltail; ++ if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && ++ !ohci->ed_rm_list[1] && !ohci->sleeping) { ++ ohci->hc_control |= OHCI_CTRL_CLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ } ++ ohci->ed_controltail = edi; ++ break; ++ ++ case PIPE_BULK: ++ ed->hwNextED = 0; ++ if (ohci->ed_bulktail == NULL) { ++ writel (ed->dma, &ohci->regs->ed_bulkhead); ++ } else { ++ ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); ++ } ++ ed->ed_prev = ohci->ed_bulktail; ++ if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && ++ !ohci->ed_rm_list[1] && !ohci->sleeping) { ++ ohci->hc_control |= OHCI_CTRL_BLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ } ++ ohci->ed_bulktail = edi; ++ break; ++ ++ case PIPE_INTERRUPT: ++ load = ed->int_load; ++ interval = ep_2_n_interval (ed->int_period); ++ ed->int_interval = interval; ++ int_branch = ep_int_ballance (ohci, interval, load); ++ ed->int_branch = int_branch; ++ ++ for (i = 0; i < ep_rev (6, interval); i += inter) { ++ inter = 1; ++ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); ++ (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); ++ ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) ++ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); ++ ed->hwNextED = *ed_p; ++ *ed_p = cpu_to_le32 (ed->dma); ++ } ++#ifdef DEBUG ++ ep_print_int_eds (ohci, "LINK_INT"); ++#endif ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ ed->hwNextED = 0; ++ ed->int_interval = 1; ++ if (ohci->ed_isotail != NULL) { ++ ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma); ++ ed->ed_prev = ohci->ed_isotail; ++ } else { ++ for ( i = 0; i < 32; i += inter) { ++ inter = 1; ++ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); ++ *ed_p != 0; ++ ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) ++ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); ++ *ed_p = cpu_to_le32 (ed->dma); ++ } ++ ed->ed_prev = NULL; ++ } ++ ohci->ed_isotail = edi; ++#ifdef DEBUG ++ ep_print_int_eds (ohci, "LINK_ISO"); ++#endif ++ break; ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* scan the periodic table to find and unlink this ED */ ++static void periodic_unlink ( ++ struct ohci *ohci, ++ struct ed *ed, ++ unsigned index, ++ unsigned period ++) { ++ for (; index < NUM_INTS; index += period) { ++ __u32 *ed_p = &ohci->hcca->int_table [index]; ++ ++ /* ED might have been unlinked through another path */ ++ while (*ed_p != 0) { ++ if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { ++ *ed_p = ed->hwNextED; ++ break; ++ } ++ ed_p = & ((dma_to_ed (ohci, ++ le32_to_cpup (ed_p)))->hwNextED); ++ } ++ } ++} ++ ++/* unlink an ed from one of the HC chains. ++ * just the link to the ed is unlinked. ++ * the link from the ed still points to another operational ed or 0 ++ * so the HC can eventually finish the processing of the unlinked ed */ ++ ++static int ep_unlink (ohci_t * ohci, ed_t * ed) ++{ ++ int i; ++ ++ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); ++ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ if (ed->ed_prev == NULL) { ++ if (!ed->hwNextED) { ++ ohci->hc_control &= ~OHCI_CTRL_CLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ } ++ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); ++ } else { ++ ed->ed_prev->hwNextED = ed->hwNextED; ++ } ++ if (ohci->ed_controltail == ed) { ++ ohci->ed_controltail = ed->ed_prev; ++ } else { ++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; ++ } ++ break; ++ ++ case PIPE_BULK: ++ if (ed->ed_prev == NULL) { ++ if (!ed->hwNextED) { ++ ohci->hc_control &= ~OHCI_CTRL_BLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ } ++ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); ++ } else { ++ ed->ed_prev->hwNextED = ed->hwNextED; ++ } ++ if (ohci->ed_bulktail == ed) { ++ ohci->ed_bulktail = ed->ed_prev; ++ } else { ++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; ++ } ++ break; ++ ++ case PIPE_INTERRUPT: ++ periodic_unlink (ohci, ed, 0, 1); ++ for (i = ed->int_branch; i < 32; i += ed->int_interval) ++ ohci->ohci_int_load[i] -= ed->int_load; ++#ifdef DEBUG ++ ep_print_int_eds (ohci, "UNLINK_INT"); ++#endif ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ if (ohci->ed_isotail == ed) ++ ohci->ed_isotail = ed->ed_prev; ++ if (ed->hwNextED != 0) ++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) ++ ->ed_prev = ed->ed_prev; ++ ++ if (ed->ed_prev != NULL) ++ ed->ed_prev->hwNextED = ed->hwNextED; ++ else ++ periodic_unlink (ohci, ed, 0, 1); ++#ifdef DEBUG ++ ep_print_int_eds (ohci, "UNLINK_ISO"); ++#endif ++ break; ++ } ++ ed->state = ED_UNLINK; ++ return 0; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, ++ * but the USB stack is a little bit stateless so we do it at every transaction ++ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK ++ * in all other cases the state is left unchanged ++ * the ed info fields are setted anyway even though most of them should not change */ ++ ++static ed_t * ep_add_ed ( ++ struct usb_device * usb_dev, ++ unsigned int pipe, ++ int interval, ++ int load, ++ int mem_flags ++) ++{ ++ ohci_t * ohci = usb_dev->bus->hcpriv; ++ td_t * td; ++ ed_t * ed_ret; ++ volatile ed_t * ed; ++ ++ ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | ++ (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); ++ ++ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { ++ /* pending delete request */ ++ return NULL; ++ } ++ ++ if (ed->state == ED_NEW) { ++ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ ++ /* dummy td; end of td list for ed */ ++ td = td_alloc (ohci, SLAB_ATOMIC); ++ /* hash the ed for later reverse mapping */ ++ if (!td || !hash_add_ed (ohci, (ed_t *)ed)) { ++ /* out of memory */ ++ if (td) ++ td_free(ohci, td); ++ return NULL; ++ } ++ ed->hwTailP = cpu_to_le32 (td->td_dma); ++ ed->hwHeadP = ed->hwTailP; ++ ed->state = ED_UNLINK; ++ ed->type = usb_pipetype (pipe); ++ usb_to_ohci (usb_dev)->ed_cnt++; ++ } ++ ++ ohci->dev[usb_pipedevice (pipe)] = usb_dev; ++ ++ ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) ++ | usb_pipeendpoint (pipe) << 7 ++ | (usb_pipeisoc (pipe)? 0x8000: 0) ++ | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) ++ | usb_pipeslow (pipe) << 13 ++ | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); ++ ++ if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { ++ ed->int_period = interval; ++ ed->int_load = load; ++ } ++ ++ return ed_ret; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* request the removal of an endpoint ++ * put the ep on the rm_list and request a stop of the bulk or ctrl list ++ * real removal is done at the next start frame (SF) hardware interrupt */ ++ ++static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) ++{ ++ unsigned int frame; ++ ohci_t * ohci = usb_dev->bus->hcpriv; ++ ++ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) ++ return; ++ ++ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); ++ ++ if (!ohci->disabled) { ++ switch (ed->type) { ++ case PIPE_CONTROL: /* stop control list */ ++ ohci->hc_control &= ~OHCI_CTRL_CLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ break; ++ case PIPE_BULK: /* stop bulk list */ ++ ohci->hc_control &= ~OHCI_CTRL_BLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ break; ++ } ++ } ++ ++ frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1; ++ ed->ed_rm_list = ohci->ed_rm_list[frame]; ++ ohci->ed_rm_list[frame] = ed; ++ ++ if (!ohci->disabled && !ohci->sleeping) { ++ /* enable SOF interrupt */ ++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); ++ writel (OHCI_INTR_SF, &ohci->regs->intrenable); ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ } ++} ++ ++/*-------------------------------------------------------------------------* ++ * TD handling functions ++ *-------------------------------------------------------------------------*/ ++ ++/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ ++ ++static void ++td_fill (ohci_t * ohci, unsigned int info, ++ dma_addr_t data, int len, ++ struct urb * urb, int index) ++{ ++ volatile td_t * td, * td_pt; ++ urb_priv_t * urb_priv = urb->hcpriv; ++ ++ if (index >= urb_priv->length) { ++ err("internal OHCI error: TD index > length"); ++ return; ++ } ++ ++ /* use this td as the next dummy */ ++ td_pt = urb_priv->td [index]; ++ td_pt->hwNextTD = 0; ++ ++ /* fill the old dummy TD */ ++ td = urb_priv->td [index] = dma_to_td (ohci, ++ le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf); ++ ++ td->ed = urb_priv->ed; ++ td->next_dl_td = NULL; ++ td->index = index; ++ td->urb = urb; ++ td->data_dma = data; ++ if (!len) ++ data = 0; ++ ++ td->hwINFO = cpu_to_le32 (info); ++ if ((td->ed->type) == PIPE_ISOCHRONOUS) { ++ td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); ++ td->ed->last_iso = info & 0xffff; ++ } else { ++ td->hwCBP = cpu_to_le32 (data); ++ } ++ if (data) ++ td->hwBE = cpu_to_le32 (data + len - 1); ++ else ++ td->hwBE = 0; ++ td->hwNextTD = cpu_to_le32 (td_pt->td_dma); ++ td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000); ++ ++ /* append to queue */ ++ wmb(); ++ td->ed->hwTailP = td->hwNextTD; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* prepare all TDs of a transfer */ ++ ++static void td_submit_urb (struct urb * urb) ++{ ++ urb_priv_t * urb_priv = urb->hcpriv; ++ ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; ++ dma_addr_t data; ++ int data_len = urb->transfer_buffer_length; ++ int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); ++ int cnt = 0; ++ __u32 info = 0; ++ unsigned int toggle = 0; ++ ++ /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ ++ if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { ++ toggle = TD_T_TOGGLE; ++ } else { ++ toggle = TD_T_DATA0; ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); ++ } ++ ++ urb_priv->td_cnt = 0; ++ ++ if (data_len) { ++ data = pci_map_single (ohci->ohci_dev, ++ urb->transfer_buffer, data_len, ++ usb_pipeout (urb->pipe) ++ ? PCI_DMA_TODEVICE ++ : PCI_DMA_FROMDEVICE ++ ); ++ } else ++ data = 0; ++ ++ switch (usb_pipetype (urb->pipe)) { ++ case PIPE_BULK: ++ info = usb_pipeout (urb->pipe)? ++ TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; ++ while(data_len > 4096) { ++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); ++ data += 4096; data_len -= 4096; cnt++; ++ } ++ info = usb_pipeout (urb->pipe)? ++ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; ++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); ++ cnt++; ++ ++ /* If the transfer size is multiple of the pipe mtu, ++ * we may need an extra TD to create a empty frame ++ * Note : another way to check this condition is ++ * to test if(urb_priv->length > cnt) - Jean II */ ++ if ((urb->transfer_flags & USB_ZERO_PACKET) && ++ usb_pipeout (urb->pipe) && ++ (urb->transfer_buffer_length != 0) && ++ ((urb->transfer_buffer_length % maxps) == 0)) { ++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); ++ cnt++; ++ } ++ ++ if (!ohci->sleeping) { ++ wmb(); ++ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ } ++ break; ++ ++ case PIPE_INTERRUPT: ++ info = usb_pipeout (urb->pipe)? ++ TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; ++ td_fill (ohci, info, data, data_len, urb, cnt++); ++ break; ++ ++ case PIPE_CONTROL: ++ info = TD_CC | TD_DP_SETUP | TD_T_DATA0; ++ td_fill (ohci, info, ++ pci_map_single (ohci->ohci_dev, ++ urb->setup_packet, 8, ++ PCI_DMA_TODEVICE), ++ 8, urb, cnt++); ++ if (data_len > 0) { ++ info = usb_pipeout (urb->pipe)? ++ TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; ++ /* NOTE: mishandles transfers >8K, some >4K */ ++ td_fill (ohci, info, data, data_len, urb, cnt++); ++ } ++ info = usb_pipeout (urb->pipe)? ++ TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; ++ td_fill (ohci, info, data, 0, urb, cnt++); ++ if (!ohci->sleeping) { ++ wmb(); ++ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ } ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ for (cnt = 0; cnt < urb->number_of_packets; cnt++) { ++ td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), ++ data + urb->iso_frame_desc[cnt].offset, ++ urb->iso_frame_desc[cnt].length, urb, cnt); ++ } ++ break; ++ } ++ if (urb_priv->length != cnt) ++ dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt); ++} ++ ++/*-------------------------------------------------------------------------* ++ * Done List handling functions ++ *-------------------------------------------------------------------------*/ ++ ++ ++/* calculate the transfer length and update the urb */ ++ ++static void dl_transfer_length(td_t * td) ++{ ++ __u32 tdINFO, tdBE, tdCBP; ++ __u16 tdPSW; ++ struct urb * urb = td->urb; ++ urb_priv_t * urb_priv = urb->hcpriv; ++ int dlen = 0; ++ int cc = 0; ++ ++ tdINFO = le32_to_cpup (&td->hwINFO); ++ tdBE = le32_to_cpup (&td->hwBE); ++ tdCBP = le32_to_cpup (&td->hwCBP); ++ ++ ++ if (tdINFO & TD_ISO) { ++ tdPSW = le16_to_cpu (td->hwPSW[0]); ++ cc = (tdPSW >> 12) & 0xF; ++ if (cc < 0xE) { ++ if (usb_pipeout(urb->pipe)) { ++ dlen = urb->iso_frame_desc[td->index].length; ++ } else { ++ dlen = tdPSW & 0x3ff; ++ } ++ urb->actual_length += dlen; ++ urb->iso_frame_desc[td->index].actual_length = dlen; ++ if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) ++ cc = TD_CC_NOERROR; ++ ++ urb->iso_frame_desc[td->index].status = cc_to_error[cc]; ++ } ++ } else { /* BULK, INT, CONTROL DATA */ ++ if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && ++ ((td->index == 0) || (td->index == urb_priv->length - 1)))) { ++ if (tdBE != 0) { ++ if (td->hwCBP == 0) ++ urb->actual_length += tdBE - td->data_dma + 1; ++ else ++ urb->actual_length += tdCBP - td->data_dma; ++ } ++ } ++ } ++} ++ ++/* handle an urb that is being unlinked */ ++ ++static void dl_del_urb (ohci_t *ohci, struct urb * urb) ++{ ++ wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; ++ ++ urb_rm_priv_locked (urb); ++ ++ if (urb->transfer_flags & USB_ASYNC_UNLINK) { ++ urb->status = -ECONNRESET; ++ ohci_complete_add(ohci, urb); ++ } else { ++ urb->status = -ENOENT; ++ ++ /* unblock sohci_unlink_urb */ ++ if (wait_head) ++ wake_up (wait_head); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* replies to the request have to be on a FIFO basis so ++ * we reverse the reversed done-list */ ++ ++static td_t * dl_reverse_done_list (ohci_t * ohci) ++{ ++ __u32 td_list_hc; ++ td_t * td_rev = NULL; ++ td_t * td_list = NULL; ++ urb_priv_t * urb_priv = NULL; ++ ++ td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; ++ ohci->hcca->done_head = 0; ++ ++ while (td_list_hc) { ++ td_list = dma_to_td (ohci, td_list_hc); ++ ++ if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { ++ urb_priv = (urb_priv_t *) td_list->urb->hcpriv; ++ dbg(" USB-error/status: %x : %p", ++ TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); ++ if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { ++ if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { ++ td_list->ed->hwHeadP = ++ (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | ++ (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); ++ urb_priv->td_cnt += urb_priv->length - td_list->index - 1; ++ } else ++ td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); ++ } ++ } ++ ++ td_list->next_dl_td = td_rev; ++ td_rev = td_list; ++ td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; ++ } ++ return td_list; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* there are some pending requests to remove ++ * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) ++ * - some URBs/TDs if urb_priv->state == URB_DEL */ ++ ++static void dl_del_list (ohci_t * ohci, unsigned int frame) ++{ ++ ed_t * ed; ++ __u32 edINFO; ++ __u32 tdINFO; ++ td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; ++ __u32 * td_p; ++ int ctrl = 0, bulk = 0; ++ ++ for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { ++ ++ tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); ++ tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); ++ edINFO = le32_to_cpup (&ed->hwINFO); ++ td_p = &ed->hwHeadP; ++ ++ for (td = tdHeadP; td != tdTailP; td = td_next) { ++ struct urb * urb = td->urb; ++ urb_priv_t * urb_priv = td->urb->hcpriv; ++ ++ td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0); ++ if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { ++ tdINFO = le32_to_cpup (&td->hwINFO); ++ if (TD_CC_GET (tdINFO) < 0xE) ++ dl_transfer_length (td); ++ *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); ++ ++ /* URB is done; clean up */ ++ if (++(urb_priv->td_cnt) == urb_priv->length) ++ dl_del_urb (ohci, urb); ++ } else { ++ td_p = &td->hwNextTD; ++ } ++ } ++ ++ if (ed->state & ED_DEL) { /* set by sohci_free_dev */ ++ struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); ++ td_free (ohci, tdTailP); /* free dummy td */ ++ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); ++ ed->state = ED_NEW; ++ hash_free_ed(ohci, ed); ++ /* if all eds are removed wake up sohci_free_dev */ ++ if (!--dev->ed_cnt) { ++ wait_queue_head_t *wait_head = dev->wait; ++ ++ dev->wait = 0; ++ if (wait_head) ++ wake_up (wait_head); ++ } ++ } else { ++ ed->state &= ~ED_URB_DEL; ++ tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); ++ ++ if (tdHeadP == tdTailP) { ++ if (ed->state == ED_OPER) ++ ep_unlink(ohci, ed); ++ } else ++ ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); ++ } ++ ++ switch (ed->type) { ++ case PIPE_CONTROL: ++ ctrl = 1; ++ break; ++ case PIPE_BULK: ++ bulk = 1; ++ break; ++ } ++ } ++ ++ /* maybe reenable control and bulk lists */ ++ if (!ohci->disabled) { ++ if (ctrl) /* reset control list */ ++ writel (0, &ohci->regs->ed_controlcurrent); ++ if (bulk) /* reset bulk list */ ++ writel (0, &ohci->regs->ed_bulkcurrent); ++ if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) { ++ if (ohci->ed_controltail) ++ ohci->hc_control |= OHCI_CTRL_CLE; ++ if (ohci->ed_bulktail) ++ ohci->hc_control |= OHCI_CTRL_BLE; ++ writel (ohci->hc_control, &ohci->regs->control); ++ } ++ } ++ ++ ohci->ed_rm_list[frame] = NULL; ++} ++ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* td done list */ ++ ++static void dl_done_list (ohci_t * ohci, td_t * td_list) ++{ ++ td_t * td_list_next = NULL; ++ ed_t * ed; ++ int cc = 0; ++ struct urb * urb; ++ urb_priv_t * urb_priv; ++ __u32 tdINFO, edHeadP, edTailP; ++ ++ while (td_list) { ++ td_list_next = td_list->next_dl_td; ++ ++ urb = td_list->urb; ++ urb_priv = urb->hcpriv; ++ tdINFO = le32_to_cpup (&td_list->hwINFO); ++ ++ ed = td_list->ed; ++ ++ dl_transfer_length(td_list); ++ ++ /* error code of transfer */ ++ cc = TD_CC_GET (tdINFO); ++ if (cc == TD_CC_STALL) ++ usb_endpoint_halt(urb->dev, ++ usb_pipeendpoint(urb->pipe), ++ usb_pipeout(urb->pipe)); ++ ++ if (!(urb->transfer_flags & USB_DISABLE_SPD) ++ && (cc == TD_DATAUNDERRUN)) ++ cc = TD_CC_NOERROR; ++ ++ if (++(urb_priv->td_cnt) == urb_priv->length) { ++ if ((ed->state & (ED_OPER | ED_UNLINK)) ++ && (urb_priv->state != URB_DEL)) { ++ urb->status = cc_to_error[cc]; ++ sohci_return_urb (ohci, urb); ++ } else { ++ dl_del_urb (ohci, urb); ++ } ++ } ++ ++ if (ed->state != ED_NEW) { ++ edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; ++ edTailP = le32_to_cpup (&ed->hwTailP); ++ ++ /* unlink eds if they are not busy */ ++ if ((edHeadP == edTailP) && (ed->state == ED_OPER)) ++ ep_unlink (ohci, ed); ++ } ++ ++ td_list = td_list_next; ++ } ++} ++ ++ ++ ++ ++/*-------------------------------------------------------------------------* ++ * Virtual Root Hub ++ *-------------------------------------------------------------------------*/ ++ ++/* Device descriptor */ ++static __u8 root_hub_dev_des[] = ++{ ++ 0x12, /* __u8 bLength; */ ++ 0x01, /* __u8 bDescriptorType; Device */ ++ 0x10, /* __u16 bcdUSB; v1.1 */ ++ 0x01, ++ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 bDeviceSubClass; */ ++ 0x00, /* __u8 bDeviceProtocol; */ ++ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ ++ 0x00, /* __u16 idVendor; */ ++ 0x00, ++ 0x00, /* __u16 idProduct; */ ++ 0x00, ++ 0x00, /* __u16 bcdDevice; */ ++ 0x00, ++ 0x00, /* __u8 iManufacturer; */ ++ 0x02, /* __u8 iProduct; */ ++ 0x01, /* __u8 iSerialNumber; */ ++ 0x01 /* __u8 bNumConfigurations; */ ++}; ++ ++ ++/* Configuration descriptor */ ++static __u8 root_hub_config_des[] = ++{ ++ 0x09, /* __u8 bLength; */ ++ 0x02, /* __u8 bDescriptorType; Configuration */ ++ 0x19, /* __u16 wTotalLength; */ ++ 0x00, ++ 0x01, /* __u8 bNumInterfaces; */ ++ 0x01, /* __u8 bConfigurationValue; */ ++ 0x00, /* __u8 iConfiguration; */ ++ 0x40, /* __u8 bmAttributes; ++ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ ++ 0x00, /* __u8 MaxPower; */ ++ ++ /* interface */ ++ 0x09, /* __u8 if_bLength; */ ++ 0x04, /* __u8 if_bDescriptorType; Interface */ ++ 0x00, /* __u8 if_bInterfaceNumber; */ ++ 0x00, /* __u8 if_bAlternateSetting; */ ++ 0x01, /* __u8 if_bNumEndpoints; */ ++ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 if_bInterfaceSubClass; */ ++ 0x00, /* __u8 if_bInterfaceProtocol; */ ++ 0x00, /* __u8 if_iInterface; */ ++ ++ /* endpoint */ ++ 0x07, /* __u8 ep_bLength; */ ++ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ ++ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ ++ 0x03, /* __u8 ep_bmAttributes; Interrupt */ ++ 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ ++ 0x00, ++ 0xff /* __u8 ep_bInterval; 255 ms */ ++}; ++ ++/* Hub class-specific descriptor is constructed dynamically */ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ ++ ++static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) ++{ ++ int num_ports; ++ int i; ++ int ret; ++ int len; ++ ++ __u8 data[8]; ++ ++ num_ports = roothub_a (ohci) & RH_A_NDP; ++ if (num_ports > MAX_ROOT_PORTS) { ++ err ("bogus NDP=%d for OHCI usb-%s", num_ports, ++ ohci->ohci_dev->slot_name); ++ err ("rereads as NDP=%d", ++ readl (&ohci->regs->roothub.a) & RH_A_NDP); ++ /* retry later; "should not happen" */ ++ return 0; ++ } ++ *(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC)) ++ ? 1: 0; ++ ret = *(__u8 *) data; ++ ++ for ( i = 0; i < num_ports; i++) { ++ *(__u8 *) (data + (i + 1) / 8) |= ++ ((roothub_portstatus (ohci, i) & ++ (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) ++ ? 1: 0) << ((i + 1) % 8); ++ ret += *(__u8 *) (data + (i + 1) / 8); ++ } ++ len = i/8 + 1; ++ ++ if (ret > 0) { ++ memcpy(rh_data, data, ++ min_t(unsigned int, len, ++ min_t(unsigned int, rh_len, sizeof(data)))); ++ return len; ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ ++ ++static void rh_int_timer_do (unsigned long ptr) ++{ ++ int len; ++ ++ struct urb * urb = (struct urb *) ptr; ++ ohci_t * ohci = urb->dev->bus->hcpriv; ++ ++ if (ohci->disabled) ++ return; ++ ++ /* ignore timers firing during PM suspend, etc */ ++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) ++ goto out; ++ ++ if(ohci->rh.send) { ++ len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length); ++ if (len > 0) { ++ urb->actual_length = len; ++#ifdef DEBUG ++ urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe)); ++#endif ++ if (urb->complete) ++ urb->complete (urb); ++ } ++ } ++ out: ++ rh_init_int_timer (urb); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Root Hub INTs are polled by this timer */ ++ ++static int rh_init_int_timer (struct urb * urb) ++{ ++ ohci_t * ohci = urb->dev->bus->hcpriv; ++ ++ ohci->rh.interval = urb->interval; ++ init_timer (&ohci->rh.rh_int_timer); ++ ohci->rh.rh_int_timer.function = rh_int_timer_do; ++ ohci->rh.rh_int_timer.data = (unsigned long) urb; ++ ohci->rh.rh_int_timer.expires = ++ jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; ++ add_timer (&ohci->rh.rh_int_timer); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define OK(x) len = (x); break ++#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status) ++#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1]) ++#define RD_RH_STAT roothub_status(ohci) ++#define RD_RH_PORTSTAT roothub_portstatus(ohci,wIndex-1) ++ ++/* request to virtual root hub */ ++ ++static int rh_submit_urb (struct urb * urb) ++{ ++ struct usb_device * usb_dev = urb->dev; ++ ohci_t * ohci = usb_dev->bus->hcpriv; ++ unsigned int pipe = urb->pipe; ++ struct usb_ctrlrequest * cmd = (struct usb_ctrlrequest *) urb->setup_packet; ++ void * data = urb->transfer_buffer; ++ int leni = urb->transfer_buffer_length; ++ int len = 0; ++ int status = TD_CC_NOERROR; ++ unsigned long flags; ++ ++ __u32 datab[4]; ++ __u8 * data_buf = (__u8 *) datab; ++ ++ __u16 bmRType_bReq; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++ ++ spin_lock_irqsave(&ohci->ohci_lock, flags); ++ ++ if (usb_pipeint(pipe)) { ++ ohci->rh.urb = urb; ++ ohci->rh.send = 1; ++ ohci->rh.interval = urb->interval; ++ rh_init_int_timer(urb); ++ urb->status = cc_to_error [TD_CC_NOERROR]; ++ ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ return 0; ++ } ++ ++ bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); ++ wValue = le16_to_cpu (cmd->wValue); ++ wIndex = le16_to_cpu (cmd->wIndex); ++ wLength = le16_to_cpu (cmd->wLength); ++ ++ switch (bmRType_bReq) { ++ /* Request Destination: ++ without flags: Device, ++ RH_INTERFACE: interface, ++ RH_ENDPOINT: endpoint, ++ RH_CLASS means HUB here, ++ RH_OTHER | RH_CLASS almost ever means HUB_PORT here ++ */ ++ ++ case RH_GET_STATUS: ++ *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); ++ case RH_GET_STATUS | RH_INTERFACE: ++ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); ++ case RH_GET_STATUS | RH_ENDPOINT: ++ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); ++ case RH_GET_STATUS | RH_CLASS: ++ *(__u32 *) data_buf = cpu_to_le32 ( ++ RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); ++ OK (4); ++ case RH_GET_STATUS | RH_OTHER | RH_CLASS: ++ *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); ++ ++ case RH_CLEAR_FEATURE | RH_ENDPOINT: ++ switch (wValue) { ++ case (RH_ENDPOINT_STALL): OK (0); ++ } ++ break; ++ ++ case RH_CLEAR_FEATURE | RH_CLASS: ++ switch (wValue) { ++ case RH_C_HUB_LOCAL_POWER: ++ OK(0); ++ case (RH_C_HUB_OVER_CURRENT): ++ WR_RH_STAT(RH_HS_OCIC); OK (0); ++ } ++ break; ++ ++ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: ++ switch (wValue) { ++ case (RH_PORT_ENABLE): ++ WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); ++ case (RH_PORT_SUSPEND): ++ WR_RH_PORTSTAT (RH_PS_POCI); OK (0); ++ case (RH_PORT_POWER): ++ WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); ++ case (RH_C_PORT_CONNECTION): ++ WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); ++ case (RH_C_PORT_ENABLE): ++ WR_RH_PORTSTAT (RH_PS_PESC); OK (0); ++ case (RH_C_PORT_SUSPEND): ++ WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); ++ case (RH_C_PORT_OVER_CURRENT): ++ WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); ++ case (RH_C_PORT_RESET): ++ WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); ++ } ++ break; ++ ++ case RH_SET_FEATURE | RH_OTHER | RH_CLASS: ++ switch (wValue) { ++ case (RH_PORT_SUSPEND): ++ WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); ++ case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ ++ if (RD_RH_PORTSTAT & RH_PS_CCS) ++ WR_RH_PORTSTAT (RH_PS_PRS); ++ OK (0); ++ case (RH_PORT_POWER): ++ WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); ++ case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ ++ if (RD_RH_PORTSTAT & RH_PS_CCS) ++ WR_RH_PORTSTAT (RH_PS_PES ); ++ OK (0); ++ } ++ break; ++ ++ case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0); ++ ++ case RH_GET_DESCRIPTOR: ++ switch ((wValue & 0xff00) >> 8) { ++ case (0x01): /* device descriptor */ ++ len = min_t(unsigned int, ++ leni, ++ min_t(unsigned int, ++ sizeof (root_hub_dev_des), ++ wLength)); ++ data_buf = root_hub_dev_des; OK(len); ++ case (0x02): /* configuration descriptor */ ++ len = min_t(unsigned int, ++ leni, ++ min_t(unsigned int, ++ sizeof (root_hub_config_des), ++ wLength)); ++ data_buf = root_hub_config_des; OK(len); ++ case (0x03): /* string descriptors */ ++ len = usb_root_hub_string (wValue & 0xff, ++ (int)(long) ohci->regs, "OHCI", ++ data, wLength); ++ if (len > 0) { ++ data_buf = data; ++ OK(min_t(int, leni, len)); ++ } ++ // else fallthrough ++ default: ++ status = TD_CC_STALL; ++ } ++ break; ++ ++ case RH_GET_DESCRIPTOR | RH_CLASS: ++ { ++ __u32 temp = roothub_a (ohci); ++ ++ data_buf [0] = 9; // min length; ++ data_buf [1] = 0x29; ++ data_buf [2] = temp & RH_A_NDP; ++ data_buf [3] = 0; ++ if (temp & RH_A_PSM) /* per-port power switching? */ ++ data_buf [3] |= 0x1; ++ if (temp & RH_A_NOCP) /* no overcurrent reporting? */ ++ data_buf [3] |= 0x10; ++ else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ ++ data_buf [3] |= 0x8; ++ ++ datab [1] = 0; ++ data_buf [5] = (temp & RH_A_POTPGT) >> 24; ++ temp = roothub_b (ohci); ++ data_buf [7] = temp & RH_B_DR; ++ if (data_buf [2] < 7) { ++ data_buf [8] = 0xff; ++ } else { ++ data_buf [0] += 2; ++ data_buf [8] = (temp & RH_B_DR) >> 8; ++ data_buf [10] = data_buf [9] = 0xff; ++ } ++ ++ len = min_t(unsigned int, leni, ++ min_t(unsigned int, data_buf [0], wLength)); ++ OK (len); ++ } ++ ++ case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); ++ ++ case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); ++ ++ default: ++ dbg ("unsupported root hub command"); ++ status = TD_CC_STALL; ++ } ++ ++#ifdef DEBUG ++ // ohci_dump_roothub (ohci, 0); ++#endif ++ ++ len = min_t(int, len, leni); ++ if (data != data_buf) ++ memcpy (data, data_buf, len); ++ urb->actual_length = len; ++ urb->status = cc_to_error [status]; ++ ++#ifdef DEBUG ++ urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe)); ++#endif ++ ++ urb->hcpriv = NULL; ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use (usb_dev); ++ urb->dev = NULL; ++ if (urb->complete) ++ urb->complete (urb); ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int rh_unlink_urb (struct urb * urb) ++{ ++ ohci_t * ohci = urb->dev->bus->hcpriv; ++ unsigned int flags; ++ ++ spin_lock_irqsave(&ohci->ohci_lock, flags); ++ if (ohci->rh.urb == urb) { ++ ohci->rh.send = 0; ++ del_timer (&ohci->rh.rh_int_timer); ++ ohci->rh.urb = NULL; ++ ++ urb->hcpriv = NULL; ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ usb_dec_dev_use(urb->dev); ++ urb->dev = NULL; ++ if (urb->transfer_flags & USB_ASYNC_UNLINK) { ++ urb->status = -ECONNRESET; ++ if (urb->complete) ++ urb->complete (urb); ++ } else ++ urb->status = -ENOENT; ++ } else { ++ spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------* ++ * HC functions ++ *-------------------------------------------------------------------------*/ ++ ++/* reset the HC and BUS */ ++ ++static int hc_reset (ohci_t * ohci) ++{ ++ int timeout = 30; ++ int smm_timeout = 50; /* 0,5 sec */ ++ ++#ifndef __hppa__ ++ /* PA-RISC doesn't have SMM, but PDC might leave IR set */ ++ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ ++ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ ++ dbg("USB HC TakeOver from SMM"); ++ while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { ++ wait_ms (10); ++ if (--smm_timeout == 0) { ++ err("USB HC TakeOver failed!"); ++ return -1; ++ } ++ } ++ } ++#endif ++ ++ /* Disable HC interrupts */ ++ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); ++ ++ dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", ++ ohci->ohci_dev->slot_name, ++ readl (&ohci->regs->control)); ++ ++ /* Reset USB (needed by some controllers) */ ++ writel (0, &ohci->regs->control); ++ ++ /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ ++ (void) readl (&ohci->regs->control); /* PCI posting */ ++ writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control); ++ ++ /* HC Reset requires max 10 ms delay */ ++ writel (OHCI_HCR, &ohci->regs->cmdstatus); ++ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { ++ if (--timeout == 0) { ++ err("USB HC reset timed out!"); ++ return -1; ++ } ++ udelay (1); ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Start an OHCI controller, set the BUS operational ++ * enable interrupts ++ * connect the virtual root hub */ ++ ++static int hc_start (ohci_t * ohci) ++{ ++ __u32 mask; ++ unsigned int fminterval; ++ struct usb_device * usb_dev; ++ struct ohci_device * dev; ++ ++ ohci->disabled = 1; ++ ++ /* Tell the controller where the control and bulk lists are ++ * The lists are empty now. */ ++ ++ writel (0, &ohci->regs->ed_controlhead); ++ writel (0, &ohci->regs->ed_bulkhead); ++ ++ writel (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */ ++ ++ fminterval = 0x2edf; ++ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); ++ fminterval |= ((((fminterval - 210) * 6) / 7) << 16); ++ writel (fminterval, &ohci->regs->fminterval); ++ writel (0x628, &ohci->regs->lsthresh); ++ ++ /* start controller operations */ ++ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; ++ ohci->disabled = 0; ++ writel (ohci->hc_control, &ohci->regs->control); ++ ++ /* Choose the interrupts we care about now, others later on demand */ ++ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; ++ writel (mask, &ohci->regs->intrenable); ++ writel (mask, &ohci->regs->intrstatus); ++ ++#ifdef OHCI_USE_NPS ++ if(ohci->flags & OHCI_QUIRK_SUCKYIO) ++ { ++ /* NSC 87560 at least requires different setup .. */ ++ writel ((roothub_a (ohci) | RH_A_NOCP) & ++ ~(RH_A_OCPM | RH_A_POTPGT | RH_A_PSM | RH_A_NPS), ++ &ohci->regs->roothub.a); ++ } ++ else ++ { ++ /* required for AMD-756 and some Mac platforms */ ++ writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, ++ &ohci->regs->roothub.a); ++ } ++ writel (RH_HS_LPSC, &ohci->regs->roothub.status); ++#endif /* OHCI_USE_NPS */ ++ ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ ++ // POTPGT delay is bits 24-31, in 2 ms units. ++ mdelay ((roothub_a (ohci) >> 23) & 0x1fe); ++ ++ /* connect the virtual root hub */ ++ ohci->rh.devnum = 0; ++ usb_dev = usb_alloc_dev (NULL, ohci->bus); ++ if (!usb_dev) { ++ ohci->disabled = 1; ++ return -ENOMEM; ++ } ++ ++ dev = usb_to_ohci (usb_dev); ++ ohci->bus->root_hub = usb_dev; ++ usb_connect (usb_dev); ++ if (usb_new_device (usb_dev) != 0) { ++ usb_free_dev (usb_dev); ++ ohci->disabled = 1; ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* called only from interrupt handler */ ++ ++static void check_timeouts (struct ohci *ohci) ++{ ++ spin_lock (&ohci->ohci_lock); ++ while (!list_empty (&ohci->timeout_list)) { ++ struct urb *urb; ++ ++ urb = list_entry (ohci->timeout_list.next, struct urb, urb_list); ++ if (time_after (jiffies, urb->timeout)) ++ break; ++ ++ list_del_init (&urb->urb_list); ++ if (urb->status != -EINPROGRESS) ++ continue; ++ ++ urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; ++ spin_unlock (&ohci->ohci_lock); ++ ++ // outside the interrupt handler (in a timer...) ++ // this reference would race interrupts ++ sohci_unlink_urb (urb); ++ ++ spin_lock (&ohci->ohci_lock); ++ } ++ spin_unlock (&ohci->ohci_lock); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* an interrupt happens */ ++ ++static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) ++{ ++ ohci_t * ohci = __ohci; ++ struct ohci_regs * regs = ohci->regs; ++ int ints; ++ ++ spin_lock (&ohci->ohci_lock); ++ ++ /* avoid (slow) readl if only WDH happened */ ++ if ((ohci->hcca->done_head != 0) ++ && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { ++ ints = OHCI_INTR_WDH; ++ ++ /* cardbus/... hardware gone before remove() */ ++ } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { ++ ohci->disabled++; ++ spin_unlock (&ohci->ohci_lock); ++ err ("%s device removed!", ohci->ohci_dev->slot_name); ++ return; ++ ++ /* interrupt for some other device? */ ++ } else if ((ints &= readl (®s->intrenable)) == 0) { ++ spin_unlock (&ohci->ohci_lock); ++ return; ++ } ++ ++ // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); ++ ++ if (ints & OHCI_INTR_UE) { ++ ohci->disabled++; ++ err ("OHCI Unrecoverable Error, controller usb-%s disabled", ++ ohci->ohci_dev->slot_name); ++ // e.g. due to PCI Master/Target Abort ++ ++#ifdef DEBUG ++ ohci_dump (ohci, 1); ++#else ++ // FIXME: be optimistic, hope that bug won't repeat often. ++ // Make some non-interrupt context restart the controller. ++ // Count and limit the retries though; either hardware or ++ // software errors can go forever... ++#endif ++ hc_reset (ohci); ++ } ++ ++ if (ints & OHCI_INTR_WDH) { ++ writel (OHCI_INTR_WDH, ®s->intrdisable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ dl_done_list (ohci, dl_reverse_done_list (ohci)); ++ writel (OHCI_INTR_WDH, ®s->intrenable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ } ++ ++ if (ints & OHCI_INTR_SO) { ++ dbg("USB Schedule overrun"); ++ writel (OHCI_INTR_SO, ®s->intrenable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ } ++ ++ // FIXME: this assumes SOF (1/ms) interrupts don't get lost... ++ if (ints & OHCI_INTR_SF) { ++ unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1; ++ writel (OHCI_INTR_SF, ®s->intrdisable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ if (ohci->ed_rm_list[!frame] != NULL) { ++ dl_del_list (ohci, !frame); ++ } ++ if (ohci->ed_rm_list[frame] != NULL) { ++ writel (OHCI_INTR_SF, ®s->intrenable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ } ++ } ++ ++ /* ++ * Finally, we are done with trashing about our hardware lists ++ * and other CPUs are allowed in. The festive flipping of the lock ++ * ensues as we struggle with the check_timeouts disaster. ++ */ ++ spin_unlock (&ohci->ohci_lock); ++ ++ if (!list_empty (&ohci->timeout_list)) { ++ check_timeouts (ohci); ++// FIXME: enable SF as needed in a timer; ++// don't make lots of 1ms interrupts ++// On unloaded USB, think 4k ~= 4-5msec ++ if (!list_empty (&ohci->timeout_list)) ++ writel (OHCI_INTR_SF, ®s->intrenable); ++ } ++ ++ writel (ints, ®s->intrstatus); ++ writel (OHCI_INTR_MIE, ®s->intrenable); ++ (void)readl (®s->intrdisable); /* PCI posting flush */ ++ ++ ohci_complete(ohci); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* allocate OHCI */ ++ ++static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) ++{ ++ ohci_t * ohci; ++ ++ ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL); ++ if (!ohci) ++ return NULL; ++ ++ memset (ohci, 0, sizeof (ohci_t)); ++ ++ ohci->hcca = pci_alloc_consistent (dev, sizeof *ohci->hcca, ++ &ohci->hcca_dma); ++ if (!ohci->hcca) { ++ kfree (ohci); ++ return NULL; ++ } ++ memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); ++ ++ ohci->disabled = 1; ++ ohci->sleeping = 0; ++ ohci->irq = -1; ++ ohci->regs = mem_base; ++ ++ ohci->ohci_dev = dev; ++ pci_set_drvdata(dev, ohci); ++ ++ INIT_LIST_HEAD (&ohci->timeout_list); ++ spin_lock_init(&ohci->ohci_lock); ++ ++ ohci->bus = usb_alloc_bus (&sohci_device_operations); ++ if (!ohci->bus) { ++ pci_set_drvdata (dev, NULL); ++ pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, ++ ohci->hcca, ohci->hcca_dma); ++ kfree (ohci); ++ return NULL; ++ } ++ ohci->bus->bus_name = dev->slot_name; ++ ohci->bus->hcpriv = (void *) ohci; ++ ++ return ohci; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* De-allocate all resources.. */ ++ ++static void hc_release_ohci (ohci_t * ohci) ++{ ++ dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); ++ ++ /* disconnect all devices */ ++ if (ohci->bus->root_hub) ++ usb_disconnect (&ohci->bus->root_hub); ++ ++ if (!ohci->disabled) ++ hc_reset (ohci); ++ ++ if (ohci->irq >= 0) { ++ free_irq (ohci->irq, ohci); ++ ohci->irq = -1; ++ } ++ pci_set_drvdata(ohci->ohci_dev, NULL); ++ if (ohci->bus) { ++ if (ohci->bus->busnum != -1) ++ usb_deregister_bus (ohci->bus); ++ ++ usb_free_bus (ohci->bus); ++ } ++ ++ ohci_mem_cleanup (ohci); ++ ++ /* unmap the IO address space */ ++ iounmap (ohci->regs); ++ ++ pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, ++ ohci->hcca, ohci->hcca_dma); ++ kfree (ohci); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Increment the module usage count, start the control thread and ++ * return success. */ ++ ++static struct pci_driver ohci_pci_driver; ++ ++static int __devinit ++hc_found_ohci (struct pci_dev *dev, int irq, ++ void *mem_base, const struct pci_device_id *id) ++{ ++ ohci_t * ohci; ++ char buf[8], *bufp = buf; ++ int ret; ++ ++#ifndef __sparc__ ++ sprintf(buf, "%d", irq); ++#else ++ bufp = __irq_itoa(irq); ++#endif ++ printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", ++ (unsigned long) mem_base, bufp); ++ printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); ++ ++ ohci = hc_alloc_ohci (dev, mem_base); ++ if (!ohci) { ++ return -ENOMEM; ++ } ++ if ((ret = ohci_mem_init (ohci)) < 0) { ++ hc_release_ohci (ohci); ++ return ret; ++ } ++ ohci->flags = id->driver_data; ++ ++ /* Check for NSC87560. We have to look at the bridge (fn1) to identify ++ the USB (fn2). This quirk might apply to more or even all NSC stuff ++ I don't know.. */ ++ ++ if(dev->vendor == PCI_VENDOR_ID_NS) ++ { ++ struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); ++ if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO) ++ ohci->flags |= OHCI_QUIRK_SUCKYIO; ++ ++ } ++ ++ if (ohci->flags & OHCI_QUIRK_SUCKYIO) ++ printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); ++ if (ohci->flags & OHCI_QUIRK_AMD756) ++ printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); ++ ++ if (hc_reset (ohci) < 0) { ++ hc_release_ohci (ohci); ++ return -ENODEV; ++ } ++ ++ /* FIXME this is a second HC reset; why?? */ ++ writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); ++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ ++ wait_ms (10); ++ ++ usb_register_bus (ohci->bus); ++ ++ if (request_irq (irq, hc_interrupt, SA_SHIRQ, ++ ohci_pci_driver.name, ohci) != 0) { ++ err ("request interrupt %s failed", bufp); ++ hc_release_ohci (ohci); ++ return -EBUSY; ++ } ++ ohci->irq = irq; ++ ++ if (hc_start (ohci) < 0) { ++ err ("can't start usb-%s", dev->slot_name); ++ hc_release_ohci (ohci); ++ return -EBUSY; ++ } ++ ++#ifdef DEBUG ++ ohci_dump (ohci, 1); ++#endif ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_PM ++ ++/* controller died; cleanup debris, then restart */ ++/* must not be called from interrupt context */ ++ ++static void hc_restart (ohci_t *ohci) ++{ ++ int temp; ++ int i; ++ ++ if (ohci->pci_latency) ++ pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); ++ ++ ohci->disabled = 1; ++ ohci->sleeping = 0; ++ if (ohci->bus->root_hub) ++ usb_disconnect (&ohci->bus->root_hub); ++ ++ /* empty the interrupt branches */ ++ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; ++ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; ++ ++ /* no EDs to remove */ ++ ohci->ed_rm_list [0] = NULL; ++ ohci->ed_rm_list [1] = NULL; ++ ++ /* empty control and bulk lists */ ++ ohci->ed_isotail = NULL; ++ ohci->ed_controltail = NULL; ++ ohci->ed_bulktail = NULL; ++ ++ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { ++ err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); ++ } else ++ dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); ++} ++ ++#endif /* CONFIG_PM */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* configured so that an OHCI device is always provided */ ++/* always called with process context; sleeping is OK */ ++ ++static int __devinit ++ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ unsigned long mem_resource, mem_len; ++ void *mem_base; ++ int status; ++ ++ if (pci_enable_device(dev) < 0) ++ return -ENODEV; ++ ++ if (!dev->irq) { ++ err("found OHCI device with no IRQ assigned. check BIOS settings!"); ++ pci_disable_device (dev); ++ return -ENODEV; ++ } ++ ++ /* we read its hardware registers as memory */ ++ mem_resource = pci_resource_start(dev, 0); ++ mem_len = pci_resource_len(dev, 0); ++ if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { ++ dbg ("controller already in use"); ++ pci_disable_device (dev); ++ return -EBUSY; ++ } ++ ++ mem_base = ioremap_nocache (mem_resource, mem_len); ++ if (!mem_base) { ++ err("Error mapping OHCI memory"); ++ release_mem_region (mem_resource, mem_len); ++ pci_disable_device (dev); ++ return -EFAULT; ++ } ++ ++ /* controller writes into our memory */ ++ pci_set_master (dev); ++ ++ status = hc_found_ohci (dev, dev->irq, mem_base, id); ++ if (status < 0) { ++ iounmap (mem_base); ++ release_mem_region (mem_resource, mem_len); ++ pci_disable_device (dev); ++ } ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* may be called from interrupt context [interface spec] */ ++/* may be called without controller present */ ++/* may be called with controller, bus, and devices active */ ++ ++static void __devexit ++ohci_pci_remove (struct pci_dev *dev) ++{ ++ ohci_t *ohci = pci_get_drvdata(dev); ++ ++ dbg ("remove %s controller usb-%s%s%s", ++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), ++ dev->slot_name, ++ ohci->disabled ? " (disabled)" : "", ++ in_interrupt () ? " in interrupt" : "" ++ ); ++#ifdef DEBUG ++ ohci_dump (ohci, 1); ++#endif ++ ++ /* don't wake up sleeping controllers, or block in interrupt context */ ++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) { ++ dbg ("controller being disabled"); ++ ohci->disabled = 1; ++ } ++ ++ /* on return, USB will always be reset (if present) */ ++ if (ohci->disabled) ++ writel (ohci->hc_control = OHCI_USB_RESET, ++ &ohci->regs->control); ++ ++ hc_release_ohci (ohci); ++ ++ release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); ++ pci_disable_device (dev); ++} ++ ++ ++#ifdef CONFIG_PM ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++ohci_pci_suspend (struct pci_dev *dev, u32 state) ++{ ++ ohci_t *ohci = pci_get_drvdata(dev); ++ unsigned long flags; ++ u16 cmd; ++ ++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { ++ dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, ++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); ++ return -EIO; ++ } ++ ++ /* act as if usb suspend can always be used */ ++ info ("USB suspend: usb-%s", dev->slot_name); ++ ohci->sleeping = 1; ++ ++ /* First stop processing */ ++ spin_lock_irqsave (&ohci->ohci_lock, flags); ++ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); ++ writel (ohci->hc_control, &ohci->regs->control); ++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); ++ (void) readl (&ohci->regs->intrstatus); ++ spin_unlock_irqrestore (&ohci->ohci_lock, flags); ++ ++ /* Wait a frame or two */ ++ mdelay(1); ++ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) ++ mdelay (1); ++ ++#ifdef CONFIG_PMAC_PBOOK ++ if (_machine == _MACH_Pmac) ++ disable_irq (ohci->irq); ++ /* else, 2.4 assumes shared irqs -- don't disable */ ++#endif ++ /* Enable remote wakeup */ ++ writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); ++ ++ /* Suspend chip and let things settle down a bit */ ++ ohci->hc_control = OHCI_USB_SUSPEND; ++ writel (ohci->hc_control, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (500); /* No schedule here ! */ ++ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { ++ case OHCI_USB_RESET: ++ dbg("Bus in reset phase ???"); ++ break; ++ case OHCI_USB_RESUME: ++ dbg("Bus in resume phase ???"); ++ break; ++ case OHCI_USB_OPER: ++ dbg("Bus in operational phase ???"); ++ break; ++ case OHCI_USB_SUSPEND: ++ dbg("Bus suspended"); ++ break; ++ } ++ /* In some rare situations, Apple's OHCI have happily trashed ++ * memory during sleep. We disable it's bus master bit during ++ * suspend ++ */ ++ pci_read_config_word (dev, PCI_COMMAND, &cmd); ++ cmd &= ~PCI_COMMAND_MASTER; ++ pci_write_config_word (dev, PCI_COMMAND, cmd); ++#ifdef CONFIG_PMAC_PBOOK ++ { ++ struct device_node *of_node; ++ ++ /* Disable USB PAD & cell clock */ ++ of_node = pci_device_to_OF_node (ohci->ohci_dev); ++ if (of_node) ++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); ++ } ++#endif ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++ohci_pci_resume (struct pci_dev *dev) ++{ ++ ohci_t *ohci = pci_get_drvdata(dev); ++ int temp; ++ unsigned long flags; ++ ++ /* guard against multiple resumes */ ++ atomic_inc (&ohci->resume_count); ++ if (atomic_read (&ohci->resume_count) != 1) { ++ err ("concurrent PCI resumes for usb-%s", dev->slot_name); ++ atomic_dec (&ohci->resume_count); ++ return 0; ++ } ++ ++#ifdef CONFIG_PMAC_PBOOK ++ { ++ struct device_node *of_node; ++ ++ /* Re-enable USB PAD & cell clock */ ++ of_node = pci_device_to_OF_node (ohci->ohci_dev); ++ if (of_node) ++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); ++ } ++#endif ++ ++ /* did we suspend, or were we powered off? */ ++ ohci->hc_control = readl (&ohci->regs->control); ++ temp = ohci->hc_control & OHCI_CTRL_HCFS; ++ ++#ifdef DEBUG ++ /* the registers may look crazy here */ ++ ohci_dump_status (ohci); ++#endif ++ ++ /* Re-enable bus mastering */ ++ pci_set_master(ohci->ohci_dev); ++ ++ switch (temp) { ++ ++ case OHCI_USB_RESET: // lost power ++ info ("USB restart: usb-%s", dev->slot_name); ++ hc_restart (ohci); ++ break; ++ ++ case OHCI_USB_SUSPEND: // host wakeup ++ case OHCI_USB_RESUME: // remote wakeup ++ info ("USB continue: usb-%s from %s wakeup", dev->slot_name, ++ (temp == OHCI_USB_SUSPEND) ++ ? "host" : "remote"); ++ ohci->hc_control = OHCI_USB_RESUME; ++ writel (ohci->hc_control, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (20); /* no schedule here ! */ ++ /* Some controllers (lucent) need a longer delay here */ ++ mdelay (15); ++ temp = readl (&ohci->regs->control); ++ temp = ohci->hc_control & OHCI_CTRL_HCFS; ++ if (temp != OHCI_USB_RESUME) { ++ err ("controller usb-%s won't resume", dev->slot_name); ++ ohci->disabled = 1; ++ return -EIO; ++ } ++ ++ /* Some chips likes being resumed first */ ++ writel (OHCI_USB_OPER, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (3); ++ ++ /* Then re-enable operations */ ++ spin_lock_irqsave (&ohci->ohci_lock, flags); ++ ohci->disabled = 0; ++ ohci->sleeping = 0; ++ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; ++ if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { ++ if (ohci->ed_controltail) ++ ohci->hc_control |= OHCI_CTRL_CLE; ++ if (ohci->ed_bulktail) ++ ohci->hc_control |= OHCI_CTRL_BLE; ++ } ++ writel (ohci->hc_control, &ohci->regs->control); ++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); ++ writel (OHCI_INTR_SF, &ohci->regs->intrenable); ++ /* Check for a pending done list */ ++ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); ++ (void) readl (&ohci->regs->intrdisable); ++#ifdef CONFIG_PMAC_PBOOK ++ if (_machine == _MACH_Pmac) ++ enable_irq (ohci->irq); ++#endif ++ if (ohci->hcca->done_head) ++ dl_done_list (ohci, dl_reverse_done_list (ohci)); ++ writel (OHCI_INTR_WDH, &ohci->regs->intrenable); ++ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ ++ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ ++ spin_unlock_irqrestore (&ohci->ohci_lock, flags); ++ break; ++ ++ default: ++ warn ("odd PCI resume for usb-%s", dev->slot_name); ++ } ++ ++ /* controller is operational, extra resumes are harmless */ ++ atomic_dec (&ohci->resume_count); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_PM */ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { ++ ++ /* ++ * AMD-756 [Viper] USB has a serious erratum when used with ++ * lowspeed devices like mice. ++ */ ++ vendor: 0x1022, ++ device: 0x740c, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ ++ driver_data: OHCI_QUIRK_AMD756, ++ ++} , { ++ ++ /* handle any USB OHCI controller */ ++ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), ++ class_mask: ~0, ++ ++ /* no matter who makes it */ ++ vendor: PCI_ANY_ID, ++ device: PCI_ANY_ID, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ ++ }, { /* end: all zeroes */ } ++}; ++ ++MODULE_DEVICE_TABLE (pci, ohci_pci_ids); ++ ++static struct pci_driver ohci_pci_driver = { ++ name: "usb-ohci", ++ id_table: &ohci_pci_ids [0], ++ ++ probe: ohci_pci_probe, ++ remove: __devexit_p(ohci_pci_remove), ++ ++#ifdef CONFIG_PM ++ suspend: ohci_pci_suspend, ++ resume: ohci_pci_resume, ++#endif /* PM */ ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init ohci_hcd_init (void) ++{ ++ return pci_module_init (&ohci_pci_driver); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void __exit ohci_hcd_cleanup (void) ++{ ++ pci_unregister_driver (&ohci_pci_driver); ++} ++ ++module_init (ohci_hcd_init); ++module_exit (ohci_hcd_cleanup); ++ ++ ++MODULE_AUTHOR( DRIVER_AUTHOR ); ++MODULE_DESCRIPTION( DRIVER_DESC ); ++MODULE_LICENSE("GPL"); diff --git a/packages/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff b/packages/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff index e69de29bb2..2f09c02d5a 100644 --- a/packages/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff +++ b/packages/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff @@ -0,0 +1,10 @@ +--- linux/drivers/net/au1000_eth.c.orig 2004-11-18 13:44:52.163605416 +0100 ++++ linux/drivers/net/au1000_eth.c 2004-11-18 13:51:42.096286176 +0100 +@@ -1389,6 +1389,7 @@ + control |= MAC_FULL_DUPLEX; + } + aup->mac->control = control; ++ aup->mac->vlan1_tag = 0x8100; /* activate vlan support */ + au_sync(); + + spin_unlock_irqrestore(&aup->lock, flags); diff --git a/packages/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff b/packages/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff index e69de29bb2..e782d3d3dc 100644 --- a/packages/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff +++ b/packages/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff @@ -0,0 +1,11 @@ +--- linux-mips-2.4.24-pre2/include/linux/wireless.h 2004-11-17 18:05:09.000000000 +0100 ++++ linux/include/linux/wireless.h 2004-11-17 19:02:44.370081592 +0100 +@@ -334,7 +334,7 @@ + * a few of them in the struct iw_range. */ + + /* Maximum of address that you may set with SPY */ +-#define IW_MAX_SPY 8 ++#define IW_MAX_SPY 32 + + /* Maximum of address that you may get in the + list of access points in range */ diff --git a/packages/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff b/packages/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff index e69de29bb2..54ba5fff98 100644 --- a/packages/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff +++ b/packages/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff @@ -0,0 +1,173 @@ +diff -Nurb linux/drivers/mtd/mtdcore.c linux-mtd-rw/drivers/mtd/mtdcore.c +--- linux/drivers/mtd/mtdcore.c 2004-11-18 13:16:00.000000000 +0100 ++++ linux-mtd-rw/drivers/mtd/mtdcore.c 2004-11-18 15:27:13.130036616 +0100 +@@ -25,6 +25,10 @@ + + #include + ++/* this symbol is exported by the procfs. */ ++extern struct proc_dir_entry *proc_sys_root; ++ ++ + /* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ + DECLARE_MUTEX(mtd_table_mutex); +@@ -336,8 +340,83 @@ + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + static struct proc_dir_entry *proc_mtd; ++ ++static struct proc_dir_entry *proc_sys_mtd; ++static struct proc_dir_entry *proc_sys_mtd_partition[MAX_MTD_DEVICES]; ++static struct proc_dir_entry *proc_sys_mtd_partition_rw[MAX_MTD_DEVICES]; + #endif + ++/*===================================0 ++ * mtdproc_read_partition_access ++ */ ++static int mtdproc_read_partition_access ( char *page, char **start, off_t off,int count, ++ int *eof, void *data ++ ) ++{ ++ int partid = (unsigned int)data; ++ int len = 0; ++ ++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)". ++ down(&mtd_table_mutex); ++ ++ if (partid < MAX_MTD_DEVICES) ++ { ++ struct mtd_info *this = mtd_table[partid]; ++ if (this) ++ { ++ page[len] = (this->flags & MTD_WRITEABLE) ? '1' : '0'; ++ len++; ++ } ++ } ++ ++ up(&mtd_table_mutex); ++ ++ if (off >= len) ++ return 0; ++ *start = page + off; ++ return ((count < len-off) ? count : len-off); ++} ++ ++ ++static int mtdproc_write_partition_access (struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ int partid = (unsigned int)data; ++ int len = 0; ++ ++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)". ++ down(&mtd_table_mutex); ++ ++ if (partid < MAX_MTD_DEVICES) ++ { ++ struct mtd_info *this = mtd_table[partid]; ++ if (this && count > 0) ++ { ++ switch (*buffer) ++ { ++ case '0': ++ this->flags &= ~(this->master_flags & MTD_WRITEABLE); ++ break; ++ ++ case '1': ++ this->flags |= ~(this->master_flags & MTD_WRITEABLE); ++ break; ++ ++ default: ++ break; ++ } ++ } ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return count; ++} ++ ++ ++ ++ ++ + static inline int mtd_proc_info (char *buf, int i) + { + struct mtd_info *this = mtd_table[i]; +@@ -349,6 +428,7 @@ + this->erasesize, this->name); + } + ++ + static int mtd_read_proc ( char *page, char **start, off_t off,int count + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +@@ -404,12 +484,31 @@ + /*====================================================================*/ + /* Init code */ + ++ + int __init init_mtd(void) + { + #ifdef CONFIG_PROC_FS + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) ++ int i; ++ + if ((proc_mtd = create_proc_entry( "mtd", 0, 0 ))) + proc_mtd->read_proc = mtd_read_proc; ++ ++ proc_sys_mtd = proc_mkdir("mtd", proc_sys_root); ++ for (i=0; iread_proc = mtdproc_read_partition_access; ++ proc_sys_mtd_partition_rw[i]->write_proc = mtdproc_write_partition_access; ++ proc_sys_mtd_partition_rw[i]->data = (void *)i; ++ } ++ } + #else + proc_register_dynamic(&proc_root,&mtd_proc_entry); + #endif +@@ -425,6 +524,8 @@ + return 0; + } + ++ ++ + static void __exit cleanup_mtd(void) + { + #ifdef CONFIG_PM +diff -Nurb linux/drivers/mtd/mtdpart.c linux-mtd-rw/drivers/mtd/mtdpart.c +--- linux/drivers/mtd/mtdpart.c 2004-11-18 13:16:00.000000000 +0100 ++++ linux-mtd-rw/drivers/mtd/mtdpart.c 2004-11-18 15:27:13.131036464 +0100 +@@ -341,6 +341,9 @@ + /* set up the MTD object for this partition */ + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~parts[i].mask_flags; ++ slave->mtd.master_flags = master->flags; ++ slave->mtd.mask_flags = parts[i].mask_flags; ++ + slave->mtd.size = parts[i].size; + slave->mtd.oobblock = master->oobblock; + slave->mtd.oobsize = master->oobsize; +diff -Nurb linux/include/linux/mtd/mtd.h linux-mtd-rw/include/linux/mtd/mtd.h +--- linux/include/linux/mtd/mtd.h 2004-11-18 13:16:31.000000000 +0100 ++++ linux-mtd-rw/include/linux/mtd/mtd.h 2004-11-18 15:27:13.000000000 +0100 +@@ -232,6 +232,9 @@ + + struct module *owner; + int usecount; ++ ++ u_int32_t master_flags; ++ u_int32_t mask_flags; + }; + + diff --git a/packages/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff b/packages/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff index e69de29bb2..2a18d605bf 100644 --- a/packages/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff +++ b/packages/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff @@ -0,0 +1,143 @@ +diff -Nurb linux-08/include/net/sock.h linux/include/net/sock.h +--- linux-08/include/net/sock.h 2004-11-19 11:37:23.376715120 +0100 ++++ linux/include/net/sock.h 2004-11-19 11:39:29.874484536 +0100 +@@ -256,6 +256,13 @@ + __u32 end_seq; + }; + ++#if 1 ++#define UDP_OPT_IN_SOCK 1 ++struct udp_opt { ++ __u32 esp_in_udp; ++}; ++#endif ++ + struct tcp_opt { + int tcp_header_len; /* Bytes of tcp header to send */ + +@@ -648,6 +655,9 @@ + #if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE) + struct spx_opt af_spx; + #endif /* CONFIG_SPX */ ++#if 1 ++ struct udp_opt af_udp; ++#endif + + } tp_pinfo; + +diff -Nurb linux-08/net/Config.in linux/net/Config.in +--- linux-08/net/Config.in 2004-11-19 11:37:23.770655232 +0100 ++++ linux/net/Config.in 2004-11-19 11:39:29.874484536 +0100 +@@ -102,4 +102,6 @@ + dep_tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN $CONFIG_PROC_FS + endmenu + ++bool 'IPSEC NAT-Traversal' CONFIG_IPSEC_NAT_TRAVERSAL ++ + endmenu +diff -Nurb linux-08/net/ipv4/udp.c linux/net/ipv4/udp.c +--- linux-08/net/ipv4/udp.c 2004-11-19 11:37:23.897635928 +0100 ++++ linux/net/ipv4/udp.c 2004-11-19 11:39:29.875484384 +0100 +@@ -810,6 +810,9 @@ + + static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) + { ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp); ++#endif + /* + * Charge it to the socket, dropping if the queue is full. + */ +@@ -827,6 +830,40 @@ + } + #endif + ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (tp->esp_in_udp) { ++ /* ++ * Set skb->sk and xmit packet to ipsec_rcv. ++ * ++ * If ret != 0, ipsec_rcv refused the packet (not ESPinUDP), ++ * restore skb->sk and fall back to sock_queue_rcv_skb ++ */ ++ struct inet_protocol *esp = NULL; ++ ++#if defined(CONFIG_IPSEC) && !defined(CONFIG_IPSEC_MODULE) ++ /* optomize only when we know it is statically linked */ ++ extern struct inet_protocol esp_protocol; ++ esp = &esp_protocol; ++#else ++ for (esp = (struct inet_protocol *)inet_protos[IPPROTO_ESP & (MAX_INET_PROTOS - 1)]; ++ (esp) && (esp->protocol != IPPROTO_ESP); ++ esp = esp->next); ++#endif ++ ++ if (esp && esp->handler) { ++ struct sock *sav_sk = skb->sk; ++ skb->sk = sk; ++ if (esp->handler(skb) == 0) { ++ skb->sk = sav_sk; ++ /*not sure we might count ESPinUDP as UDP...*/ ++ UDP_INC_STATS_BH(UdpInDatagrams); ++ return 0; ++ } ++ skb->sk = sav_sk; ++ } ++ } ++#endif ++ + if (sock_queue_rcv_skb(sk,skb)<0) { + UDP_INC_STATS_BH(UdpInErrors); + IP_INC_STATS_BH(IpInDiscards); +@@ -1050,13 +1087,49 @@ + return len; + } + ++static int udp_setsockopt(struct sock *sk, int level, int optname, ++ char *optval, int optlen) ++{ ++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp); ++ int val; ++ int err = 0; ++ ++ if (level != SOL_UDP) ++ return ip_setsockopt(sk, level, optname, optval, optlen); ++ ++ if(optlenesp_in_udp = val; ++ break; ++#endif ++ default: ++ err = -ENOPROTOOPT; ++ break; ++ } ++ ++ release_sock(sk); ++ return err; ++} ++ + struct proto udp_prot = { + name: "UDP", + close: udp_close, + connect: udp_connect, + disconnect: udp_disconnect, + ioctl: udp_ioctl, +- setsockopt: ip_setsockopt, ++ setsockopt: udp_setsockopt, + getsockopt: ip_getsockopt, + sendmsg: udp_sendmsg, + recvmsg: udp_recvmsg, + diff --git a/packages/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch b/packages/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch index e69de29bb2..4995be5ef6 100644 --- a/packages/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch +++ b/packages/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch @@ -0,0 +1,61293 @@ +make[1]: Entering directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0' +packaging/utils/kernelpatch 2.4 +--- linux/Documentation/Configure.help.orig Fri Dec 21 12:41:53 2001 ++++ linux/Documentation/Configure.help Mon Jul 29 16:35:32 2002 +@@ -24237,5 +24237,65 @@ + +-# ++IP Security Protocol (IPSEC) (EXPERIMENTAL) ++CONFIG_IPSEC ++ This unit is experimental code. ++ Pick 'y' for static linking, 'm' for module support or 'n' for none. ++ This option adds support for network layer packet encryption and/or ++ authentication with participating hosts. The standards start with: ++ RFCs 2411, 2407 and 2401. Others are mentioned where they refer to ++ specific features below. There are more pending which can be found ++ at: ftp://ftp.ietf.org/internet-drafts/draft-ietf-ipsec-*. ++ A description of each document can also be found at: ++ http://ietf.org/ids.by.wg/ipsec.html. ++ Their charter can be found at: ++ http://www.ietf.org/html.charters/ipsec-charter.html ++ Snapshots and releases of the current work can be found at: ++ http://www.freeswan.org/ ++ ++IPSEC: IP-in-IP encapsulation ++CONFIG_IPSEC_IPIP ++ This option provides support for tunnel mode IPSEC. It is recommended ++ to enable this. ++ ++IPSEC: Authentication Header ++CONFIG_IPSEC_AH ++ This option provides support for the IPSEC Authentication Header ++ (IP protocol 51) which provides packet layer sender and content ++ authentication. It is recommended to enable this. RFC2402 ++ ++HMAC-MD5 algorithm ++CONFIG_IPSEC_AUTH_HMAC_MD5 ++ Provides support for authentication using the HMAC MD5 ++ algorithm with 96 bits of hash used as the authenticator. RFC2403 ++ ++HMAC-SHA1 algorithm ++CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ Provides support for Authentication Header using the HMAC SHA1 ++ algorithm with 96 bits of hash used as the authenticator. RFC2404 ++ ++IPSEC: Encapsulating Security Payload ++CONFIG_IPSEC_ESP ++ This option provides support for the IPSEC Encapsulation Security ++ Payload (IP protocol 50) which provides packet layer content ++ hiding. It is recommended to enable this. RFC2406 ++ ++3DES algorithm ++CONFIG_IPSEC_ENC_3DES ++ Provides support for Encapsulation Security Payload protocol, using ++ the triple DES encryption algorithm. RFC2451 ++ ++IPSEC Debugging Option ++CONFIG_IPSEC_DEBUG ++ Enables IPSEC kernel debugging. It is further controlled by the ++ user space utility 'klipsdebug'. ++ ++IPSEC Regression Testing option ++CONFIG_IPSEC_REGRESS ++ Enables IPSEC regression testing. Creates a number of switches in ++ /proc/sys/net/ipsec which cause various failure modes in KLIPS. ++ For more details see FreeSWAN source under ++ testing/doc/regression_options.txt. ++ ++# + # A couple of things I keep forgetting: + # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, + # Intel, IRQ, ISDN, Linux, MSDOS, NetWare, NetWinder, +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/README.openswan-2 Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,112 @@ ++* ++* RCSID $Id: README.openswan-2,v 1.1 2003/12/10 01:07:49 mcr Exp $ ++* ++ ++ **************************************** ++ * IPSEC for Linux, Release 2.xx series * ++ **************************************** ++ ++ ++ ++1. Files ++ ++The contents of linux/net/ipsec/ (see below) join the linux kernel source tree. ++as provided for higher up. ++ ++The programs/ directory contains the user-level utilities which you need ++to run IPSEC. See the top-level top/INSTALL to compile and install them. ++ ++The testing/ directory contains test scripts. ++ ++The doc/ directory contains -- what else -- documentation. ++ ++1.1. Kernel files ++ ++The following are found in net/ipsec/: ++ ++Makefile The Makefile ++Config.in The configuration script for make menuconfig ++defconfig Configuration defaults for first time. ++ ++radij.c General-purpose radix-tree operations ++ ++ipsec_ipcomp.c IPCOMP encapsulate/decapsulate code. ++ipsec_ah.c Authentication Header (AH) encapsulate/decapsulate code. ++ipsec_esp.c Encapsulated Security Payload (ESP) encap/decap code. ++ ++pfkey_v2.c PF_KEYv2 socket interface code. ++pfkey_v2_parser.c PF_KEYv2 message parsing and processing code. ++ ++ipsec_init.c Initialization code, /proc interface. ++ipsec_radij.c Interface with the radix tree code. ++ipsec_netlink.c Interface with the netlink code. ++ipsec_xform.c Routines and structures common to transforms. ++ipsec_tunnel.c The outgoing packet processing code. ++ipsec_rcv.c The incoming packet processing code. ++ipsec_md5c.c Somewhat modified RSADSI MD5 C code. ++ipsec_sha1.c Somewhat modified Steve Reid SHA-1 C code. ++ ++sysctl_net_ipsec.c /proc/sys/net/ipsec/* variable definitions. ++ ++version.c symbolic link to project version. ++ ++radij.h Headers for radij.c ++ ++ipcomp.h Headers used by IPCOMP code. ++ ++ipsec_radij.h Interface with the radix tree code. ++ipsec_netlink.h Headers used by the netlink interface. ++ipsec_encap.h Headers defining encapsulation structures. ++ipsec_xform.h Transform headers. ++ipsec_tunnel.h Headers used by tunneling code. ++ipsec_ipe4.h Headers for the IP-in-IP code. ++ipsec_ah.h Headers common to AH transforms. ++ipsec_md5h.h RSADSI MD5 headers. ++ipsec_sha1.h SHA-1 headers. ++ipsec_esp.h Headers common to ESP transfroms. ++ipsec_rcv.h Headers for incoming packet processing code. ++ ++1.2. User-level files. ++ ++The following are found in utils/: ++ ++eroute.c Create an "extended route" source code ++spi.c Set up Security Associations source code ++spigrp.c Link SPIs together source code. ++tncfg.c Configure the tunneling features of the virtual interface ++ source code ++klipsdebug.c Set/reset klips debugging features source code. ++version.c symbolic link to project version. ++ ++eroute.8 Create an "extended route" manual page ++spi.8 Set up Security Associations manual page ++spigrp.8 Link SPIs together manual page ++tncfg.8 Configure the tunneling features of the virtual interface ++ manual page ++klipsdebug.8 Set/reset klips debugging features manual page ++ ++eroute.5 /proc/net/ipsec_eroute format manual page ++spi.5 /proc/net/ipsec_spi format manual page ++spigrp.5 /proc/net/ipsec_spigrp format manual page ++tncfg.5 /proc/net/ipsec_tncfg format manual page ++klipsdebug.5 /proc/net/ipsec_klipsdebug format manual page ++version.5 /proc/net/ipsec_version format manual page ++pf_key.5 /proc/net/pf_key format manual page ++ ++Makefile Utilities makefile. ++ ++*.8 Manpages for the respective utils. ++ ++ ++1.3. Test files ++ ++The test scripts are locate in testing/ and and documentation is found ++at doc/src/umltesting.html. Automated testing via "make check" is available ++provided that the User-Mode-Linux patches are available. ++ ++* ++* $Log: README.openswan-2,v $ ++* Revision 1.1 2003/12/10 01:07:49 mcr ++* documentation for additions. ++* ++* +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/Makefile.objs Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,13 @@ ++ ++ASM-$(ARCH_ASM):=1 ++ASM_X86:=$(ASM-i586)$(ASM-i686) ++ ++ifneq ($(strip $(ASM_X86)),) ++obj-$(CONFIG_IPSEC_ENC_AES) += aes-i586.o ++else ++obj-$(CONFIG_IPSEC_ENC_AES) += aes.o ++endif ++ ++obj-$(CONFIG_IPSEC_ENC_AES) += aes_cbc.o ++obj-$(CONFIG_IPSEC_ENC_AES) += aes_xcbc_mac.o ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/aes-i586.S Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,892 @@ ++// ++// Copyright (c) 2001, Dr Brian Gladman , Worcester, UK. ++// All rights reserved. ++// ++// TERMS ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted subject to the following conditions: ++// ++// 1. Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// ++// 2. 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. ++// ++// 3. The copyright holder's name must not be used to endorse or promote ++// any products derived from this software without his specific prior ++// written permission. ++// ++// This software is provided 'as is' with no express or implied warranties ++// of correctness or fitness for purpose. ++ ++// Modified by Jari Ruusu, December 24 2001 ++// - Converted syntax to GNU CPP/assembler syntax ++// - C programming interface converted back to "old" API ++// - Minor portability cleanups and speed optimizations ++ ++// An AES (Rijndael) implementation for the Pentium. This version only ++// implements the standard AES block length (128 bits, 16 bytes). This code ++// does not preserve the eax, ecx or edx registers or the artihmetic status ++// flags. However, the ebx, esi, edi, and ebp registers are preserved across ++// calls. ++ ++// void aes_set_key(aes_context *cx, const unsigned char key[], const int key_len, const int f) ++// void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) ++// void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) ++ ++#if defined(USE_UNDERLINE) ++# define aes_set_key _aes_set_key ++# define aes_encrypt _aes_encrypt ++# define aes_decrypt _aes_decrypt ++#endif ++#if !defined(ALIGN32BYTES) ++# define ALIGN32BYTES 32 ++#endif ++ ++ .file "aes-i586.S" ++ .globl aes_set_key ++ .globl aes_encrypt ++ .globl aes_decrypt ++ ++#define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words) ++ ++// offsets to parameters with one register pushed onto stack ++ ++#define ctx 8 // AES context structure ++#define in_blk 12 // input byte array address parameter ++#define out_blk 16 // output byte array address parameter ++ ++// offsets in context structure ++ ++#define nkey 0 // key length, size 4 ++#define nrnd 4 // number of rounds, size 4 ++#define ekey 8 // encryption key schedule base address, size 256 ++#define dkey 264 // decryption key schedule base address, size 256 ++ ++// This macro performs a forward encryption cycle. It is entered with ++// the first previous round column values in %eax, %ebx, %esi and %edi and ++// exits with the final values in the same registers. ++ ++#define fwd_rnd(p1,p2) \ ++ mov %ebx,(%esp) ;\ ++ movzbl %al,%edx ;\ ++ mov %eax,%ecx ;\ ++ mov p2(%ebp),%eax ;\ ++ mov %edi,4(%esp) ;\ ++ mov p2+12(%ebp),%edi ;\ ++ xor p1(,%edx,4),%eax ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ mov p2+4(%ebp),%ebx ;\ ++ xor p1+tlen(,%edx,4),%edi ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+3*tlen(,%ecx,4),%ebx ;\ ++ mov %esi,%ecx ;\ ++ mov p1+2*tlen(,%edx,4),%esi ;\ ++ movzbl %cl,%edx ;\ ++ xor p1(,%edx,4),%esi ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ xor p1+tlen(,%edx,4),%ebx ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+2*tlen(,%edx,4),%eax ;\ ++ mov (%esp),%edx ;\ ++ xor p1+3*tlen(,%ecx,4),%edi ;\ ++ movzbl %dl,%ecx ;\ ++ xor p2+8(%ebp),%esi ;\ ++ xor p1(,%ecx,4),%ebx ;\ ++ movzbl %dh,%ecx ;\ ++ shr $16,%edx ;\ ++ xor p1+tlen(,%ecx,4),%eax ;\ ++ movzbl %dl,%ecx ;\ ++ movzbl %dh,%edx ;\ ++ xor p1+2*tlen(,%ecx,4),%edi ;\ ++ mov 4(%esp),%ecx ;\ ++ xor p1+3*tlen(,%edx,4),%esi ;\ ++ movzbl %cl,%edx ;\ ++ xor p1(,%edx,4),%edi ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ xor p1+tlen(,%edx,4),%esi ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+2*tlen(,%edx,4),%ebx ;\ ++ xor p1+3*tlen(,%ecx,4),%eax ++ ++// This macro performs an inverse encryption cycle. It is entered with ++// the first previous round column values in %eax, %ebx, %esi and %edi and ++// exits with the final values in the same registers. ++ ++#define inv_rnd(p1,p2) \ ++ movzbl %al,%edx ;\ ++ mov %ebx,(%esp) ;\ ++ mov %eax,%ecx ;\ ++ mov p2(%ebp),%eax ;\ ++ mov %edi,4(%esp) ;\ ++ mov p2+4(%ebp),%ebx ;\ ++ xor p1(,%edx,4),%eax ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ mov p2+12(%ebp),%edi ;\ ++ xor p1+tlen(,%edx,4),%ebx ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+3*tlen(,%ecx,4),%edi ;\ ++ mov %esi,%ecx ;\ ++ mov p1+2*tlen(,%edx,4),%esi ;\ ++ movzbl %cl,%edx ;\ ++ xor p1(,%edx,4),%esi ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ xor p1+tlen(,%edx,4),%edi ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+2*tlen(,%edx,4),%eax ;\ ++ mov (%esp),%edx ;\ ++ xor p1+3*tlen(,%ecx,4),%ebx ;\ ++ movzbl %dl,%ecx ;\ ++ xor p2+8(%ebp),%esi ;\ ++ xor p1(,%ecx,4),%ebx ;\ ++ movzbl %dh,%ecx ;\ ++ shr $16,%edx ;\ ++ xor p1+tlen(,%ecx,4),%esi ;\ ++ movzbl %dl,%ecx ;\ ++ movzbl %dh,%edx ;\ ++ xor p1+2*tlen(,%ecx,4),%edi ;\ ++ mov 4(%esp),%ecx ;\ ++ xor p1+3*tlen(,%edx,4),%eax ;\ ++ movzbl %cl,%edx ;\ ++ xor p1(,%edx,4),%edi ;\ ++ movzbl %ch,%edx ;\ ++ shr $16,%ecx ;\ ++ xor p1+tlen(,%edx,4),%eax ;\ ++ movzbl %cl,%edx ;\ ++ movzbl %ch,%ecx ;\ ++ xor p1+2*tlen(,%edx,4),%ebx ;\ ++ xor p1+3*tlen(,%ecx,4),%esi ++ ++// AES (Rijndael) Encryption Subroutine ++ ++ .text ++ .align ALIGN32BYTES ++aes_encrypt: ++ push %ebp ++ mov ctx(%esp),%ebp // pointer to context ++ mov in_blk(%esp),%ecx ++ push %ebx ++ push %esi ++ push %edi ++ mov nrnd(%ebp),%edx // number of rounds ++ lea ekey+16(%ebp),%ebp // key pointer ++ ++// input four columns and xor in first round key ++ ++ mov (%ecx),%eax ++ mov 4(%ecx),%ebx ++ mov 8(%ecx),%esi ++ mov 12(%ecx),%edi ++ xor -16(%ebp),%eax ++ xor -12(%ebp),%ebx ++ xor -8(%ebp),%esi ++ xor -4(%ebp),%edi ++ ++ sub $8,%esp // space for register saves on stack ++ ++ sub $10,%edx ++ je aes_15 ++ add $32,%ebp ++ sub $2,%edx ++ je aes_13 ++ add $32,%ebp ++ ++ fwd_rnd(aes_ft_tab,-64) // 14 rounds for 256-bit key ++ fwd_rnd(aes_ft_tab,-48) ++aes_13: fwd_rnd(aes_ft_tab,-32) // 12 rounds for 192-bit key ++ fwd_rnd(aes_ft_tab,-16) ++aes_15: fwd_rnd(aes_ft_tab,0) // 10 rounds for 128-bit key ++ fwd_rnd(aes_ft_tab,16) ++ fwd_rnd(aes_ft_tab,32) ++ fwd_rnd(aes_ft_tab,48) ++ fwd_rnd(aes_ft_tab,64) ++ fwd_rnd(aes_ft_tab,80) ++ fwd_rnd(aes_ft_tab,96) ++ fwd_rnd(aes_ft_tab,112) ++ fwd_rnd(aes_ft_tab,128) ++ fwd_rnd(aes_fl_tab,144) // last round uses a different table ++ ++// move final values to the output array. ++ ++ mov out_blk+20(%esp),%ebp ++ add $8,%esp ++ mov %eax,(%ebp) ++ mov %ebx,4(%ebp) ++ mov %esi,8(%ebp) ++ mov %edi,12(%ebp) ++ pop %edi ++ pop %esi ++ pop %ebx ++ pop %ebp ++ ret ++ ++ ++// AES (Rijndael) Decryption Subroutine ++ ++ .align ALIGN32BYTES ++aes_decrypt: ++ push %ebp ++ mov ctx(%esp),%ebp // pointer to context ++ mov in_blk(%esp),%ecx ++ push %ebx ++ push %esi ++ push %edi ++ mov nrnd(%ebp),%edx // number of rounds ++ lea dkey+16(%ebp),%ebp // key pointer ++ ++// input four columns and xor in first round key ++ ++ mov (%ecx),%eax ++ mov 4(%ecx),%ebx ++ mov 8(%ecx),%esi ++ mov 12(%ecx),%edi ++ xor -16(%ebp),%eax ++ xor -12(%ebp),%ebx ++ xor -8(%ebp),%esi ++ xor -4(%ebp),%edi ++ ++ sub $8,%esp // space for register saves on stack ++ ++ sub $10,%edx ++ je aes_25 ++ add $32,%ebp ++ sub $2,%edx ++ je aes_23 ++ add $32,%ebp ++ ++ inv_rnd(aes_it_tab,-64) // 14 rounds for 256-bit key ++ inv_rnd(aes_it_tab,-48) ++aes_23: inv_rnd(aes_it_tab,-32) // 12 rounds for 192-bit key ++ inv_rnd(aes_it_tab,-16) ++aes_25: inv_rnd(aes_it_tab,0) // 10 rounds for 128-bit key ++ inv_rnd(aes_it_tab,16) ++ inv_rnd(aes_it_tab,32) ++ inv_rnd(aes_it_tab,48) ++ inv_rnd(aes_it_tab,64) ++ inv_rnd(aes_it_tab,80) ++ inv_rnd(aes_it_tab,96) ++ inv_rnd(aes_it_tab,112) ++ inv_rnd(aes_it_tab,128) ++ inv_rnd(aes_il_tab,144) // last round uses a different table ++ ++// move final values to the output array. ++ ++ mov out_blk+20(%esp),%ebp ++ add $8,%esp ++ mov %eax,(%ebp) ++ mov %ebx,4(%ebp) ++ mov %esi,8(%ebp) ++ mov %edi,12(%ebp) ++ pop %edi ++ pop %esi ++ pop %ebx ++ pop %ebp ++ ret ++ ++// AES (Rijndael) Key Schedule Subroutine ++ ++// input/output parameters ++ ++#define aes_cx 12 // AES context ++#define in_key 16 // key input array address ++#define key_ln 20 // key length, bytes (16,24,32) or bits (128,192,256) ++#define ed_flg 24 // 0=create both encr/decr keys, 1=create encr key only ++ ++// offsets for locals ++ ++#define cnt -4 ++#define kpf -8 ++#define slen 8 ++ ++// This macro performs a column mixing operation on an input 32-bit ++// word to give a 32-bit result. It uses each of the 4 bytes in the ++// the input column to index 4 different tables of 256 32-bit words ++// that are xored together to form the output value. ++ ++#define mix_col(p1) \ ++ movzbl %bl,%ecx ;\ ++ mov p1(,%ecx,4),%eax ;\ ++ movzbl %bh,%ecx ;\ ++ ror $16,%ebx ;\ ++ xor p1+tlen(,%ecx,4),%eax ;\ ++ movzbl %bl,%ecx ;\ ++ xor p1+2*tlen(,%ecx,4),%eax ;\ ++ movzbl %bh,%ecx ;\ ++ xor p1+3*tlen(,%ecx,4),%eax ++ ++// Key Schedule Macros ++ ++#define ksc4(p1) \ ++ rol $24,%ebx ;\ ++ mix_col(aes_fl_tab) ;\ ++ ror $8,%ebx ;\ ++ xor 4*p1+aes_rcon_tab,%eax ;\ ++ xor %eax,%esi ;\ ++ xor %esi,%ebp ;\ ++ mov %esi,16*p1(%edi) ;\ ++ mov %ebp,16*p1+4(%edi) ;\ ++ xor %ebp,%edx ;\ ++ xor %edx,%ebx ;\ ++ mov %edx,16*p1+8(%edi) ;\ ++ mov %ebx,16*p1+12(%edi) ++ ++#define ksc6(p1) \ ++ rol $24,%ebx ;\ ++ mix_col(aes_fl_tab) ;\ ++ ror $8,%ebx ;\ ++ xor 4*p1+aes_rcon_tab,%eax ;\ ++ xor 24*p1-24(%edi),%eax ;\ ++ mov %eax,24*p1(%edi) ;\ ++ xor 24*p1-20(%edi),%eax ;\ ++ mov %eax,24*p1+4(%edi) ;\ ++ xor %eax,%esi ;\ ++ xor %esi,%ebp ;\ ++ mov %esi,24*p1+8(%edi) ;\ ++ mov %ebp,24*p1+12(%edi) ;\ ++ xor %ebp,%edx ;\ ++ xor %edx,%ebx ;\ ++ mov %edx,24*p1+16(%edi) ;\ ++ mov %ebx,24*p1+20(%edi) ++ ++#define ksc8(p1) \ ++ rol $24,%ebx ;\ ++ mix_col(aes_fl_tab) ;\ ++ ror $8,%ebx ;\ ++ xor 4*p1+aes_rcon_tab,%eax ;\ ++ xor 32*p1-32(%edi),%eax ;\ ++ mov %eax,32*p1(%edi) ;\ ++ xor 32*p1-28(%edi),%eax ;\ ++ mov %eax,32*p1+4(%edi) ;\ ++ xor 32*p1-24(%edi),%eax ;\ ++ mov %eax,32*p1+8(%edi) ;\ ++ xor 32*p1-20(%edi),%eax ;\ ++ mov %eax,32*p1+12(%edi) ;\ ++ push %ebx ;\ ++ mov %eax,%ebx ;\ ++ mix_col(aes_fl_tab) ;\ ++ pop %ebx ;\ ++ xor %eax,%esi ;\ ++ xor %esi,%ebp ;\ ++ mov %esi,32*p1+16(%edi) ;\ ++ mov %ebp,32*p1+20(%edi) ;\ ++ xor %ebp,%edx ;\ ++ xor %edx,%ebx ;\ ++ mov %edx,32*p1+24(%edi) ;\ ++ mov %ebx,32*p1+28(%edi) ++ ++ .align ALIGN32BYTES ++aes_set_key: ++ pushfl ++ push %ebp ++ mov %esp,%ebp ++ sub $slen,%esp ++ push %ebx ++ push %esi ++ push %edi ++ ++ mov aes_cx(%ebp),%edx // edx -> AES context ++ ++ mov key_ln(%ebp),%ecx // key length ++ cmpl $128,%ecx ++ jb aes_30 ++ shr $3,%ecx ++aes_30: cmpl $32,%ecx ++ je aes_32 ++ cmpl $24,%ecx ++ je aes_32 ++ mov $16,%ecx ++aes_32: shr $2,%ecx ++ mov %ecx,nkey(%edx) ++ ++ lea 6(%ecx),%eax // 10/12/14 for 4/6/8 32-bit key length ++ mov %eax,nrnd(%edx) ++ ++ mov in_key(%ebp),%esi // key input array ++ lea ekey(%edx),%edi // key position in AES context ++ cld ++ push %ebp ++ mov %ecx,%eax // save key length in eax ++ rep ; movsl // words in the key schedule ++ mov -4(%esi),%ebx // put some values in registers ++ mov -8(%esi),%edx // to allow faster code ++ mov -12(%esi),%ebp ++ mov -16(%esi),%esi ++ ++ cmpl $4,%eax // jump on key size ++ je aes_36 ++ cmpl $6,%eax ++ je aes_35 ++ ++ ksc8(0) ++ ksc8(1) ++ ksc8(2) ++ ksc8(3) ++ ksc8(4) ++ ksc8(5) ++ ksc8(6) ++ jmp aes_37 ++aes_35: ksc6(0) ++ ksc6(1) ++ ksc6(2) ++ ksc6(3) ++ ksc6(4) ++ ksc6(5) ++ ksc6(6) ++ ksc6(7) ++ jmp aes_37 ++aes_36: ksc4(0) ++ ksc4(1) ++ ksc4(2) ++ ksc4(3) ++ ksc4(4) ++ ksc4(5) ++ ksc4(6) ++ ksc4(7) ++ ksc4(8) ++ ksc4(9) ++aes_37: pop %ebp ++ mov aes_cx(%ebp),%edx // edx -> AES context ++ cmpl $0,ed_flg(%ebp) ++ jne aes_39 ++ ++// compile decryption key schedule from encryption schedule - reverse ++// order and do mix_column operation on round keys except first and last ++ ++ mov nrnd(%edx),%eax // kt = cx->d_key + nc * cx->Nrnd ++ shl $2,%eax ++ lea dkey(%edx,%eax,4),%edi ++ lea ekey(%edx),%esi // kf = cx->e_key ++ ++ movsl // copy first round key (unmodified) ++ movsl ++ movsl ++ movsl ++ sub $32,%edi ++ movl $1,cnt(%ebp) ++aes_38: // do mix column on each column of ++ lodsl // each round key ++ mov %eax,%ebx ++ mix_col(aes_im_tab) ++ stosl ++ lodsl ++ mov %eax,%ebx ++ mix_col(aes_im_tab) ++ stosl ++ lodsl ++ mov %eax,%ebx ++ mix_col(aes_im_tab) ++ stosl ++ lodsl ++ mov %eax,%ebx ++ mix_col(aes_im_tab) ++ stosl ++ sub $32,%edi ++ ++ incl cnt(%ebp) ++ mov cnt(%ebp),%eax ++ cmp nrnd(%edx),%eax ++ jb aes_38 ++ ++ movsl // copy last round key (unmodified) ++ movsl ++ movsl ++ movsl ++aes_39: pop %edi ++ pop %esi ++ pop %ebx ++ mov %ebp,%esp ++ pop %ebp ++ popfl ++ ret ++ ++ ++// finite field multiplies by {02}, {04} and {08} ++ ++#define f2(x) ((x<<1)^(((x>>7)&1)*0x11b)) ++#define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b)) ++#define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b)) ++ ++// finite field multiplies required in table generation ++ ++#define f3(x) (f2(x) ^ x) ++#define f9(x) (f8(x) ^ x) ++#define fb(x) (f8(x) ^ f2(x) ^ x) ++#define fd(x) (f8(x) ^ f4(x) ^ x) ++#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) ++ ++// These defines generate the forward table entries ++ ++#define u0(x) ((f3(x) << 24) | (x << 16) | (x << 8) | f2(x)) ++#define u1(x) ((x << 24) | (x << 16) | (f2(x) << 8) | f3(x)) ++#define u2(x) ((x << 24) | (f2(x) << 16) | (f3(x) << 8) | x) ++#define u3(x) ((f2(x) << 24) | (f3(x) << 16) | (x << 8) | x) ++ ++// These defines generate the inverse table entries ++ ++#define v0(x) ((fb(x) << 24) | (fd(x) << 16) | (f9(x) << 8) | fe(x)) ++#define v1(x) ((fd(x) << 24) | (f9(x) << 16) | (fe(x) << 8) | fb(x)) ++#define v2(x) ((f9(x) << 24) | (fe(x) << 16) | (fb(x) << 8) | fd(x)) ++#define v3(x) ((fe(x) << 24) | (fb(x) << 16) | (fd(x) << 8) | f9(x)) ++ ++// These defines generate entries for the last round tables ++ ++#define w0(x) (x) ++#define w1(x) (x << 8) ++#define w2(x) (x << 16) ++#define w3(x) (x << 24) ++ ++// macro to generate inverse mix column tables (needed for the key schedule) ++ ++#define im_data0(p1) \ ++ .long p1(0x00),p1(0x01),p1(0x02),p1(0x03),p1(0x04),p1(0x05),p1(0x06),p1(0x07) ;\ ++ .long p1(0x08),p1(0x09),p1(0x0a),p1(0x0b),p1(0x0c),p1(0x0d),p1(0x0e),p1(0x0f) ;\ ++ .long p1(0x10),p1(0x11),p1(0x12),p1(0x13),p1(0x14),p1(0x15),p1(0x16),p1(0x17) ;\ ++ .long p1(0x18),p1(0x19),p1(0x1a),p1(0x1b),p1(0x1c),p1(0x1d),p1(0x1e),p1(0x1f) ++#define im_data1(p1) \ ++ .long p1(0x20),p1(0x21),p1(0x22),p1(0x23),p1(0x24),p1(0x25),p1(0x26),p1(0x27) ;\ ++ .long p1(0x28),p1(0x29),p1(0x2a),p1(0x2b),p1(0x2c),p1(0x2d),p1(0x2e),p1(0x2f) ;\ ++ .long p1(0x30),p1(0x31),p1(0x32),p1(0x33),p1(0x34),p1(0x35),p1(0x36),p1(0x37) ;\ ++ .long p1(0x38),p1(0x39),p1(0x3a),p1(0x3b),p1(0x3c),p1(0x3d),p1(0x3e),p1(0x3f) ++#define im_data2(p1) \ ++ .long p1(0x40),p1(0x41),p1(0x42),p1(0x43),p1(0x44),p1(0x45),p1(0x46),p1(0x47) ;\ ++ .long p1(0x48),p1(0x49),p1(0x4a),p1(0x4b),p1(0x4c),p1(0x4d),p1(0x4e),p1(0x4f) ;\ ++ .long p1(0x50),p1(0x51),p1(0x52),p1(0x53),p1(0x54),p1(0x55),p1(0x56),p1(0x57) ;\ ++ .long p1(0x58),p1(0x59),p1(0x5a),p1(0x5b),p1(0x5c),p1(0x5d),p1(0x5e),p1(0x5f) ++#define im_data3(p1) \ ++ .long p1(0x60),p1(0x61),p1(0x62),p1(0x63),p1(0x64),p1(0x65),p1(0x66),p1(0x67) ;\ ++ .long p1(0x68),p1(0x69),p1(0x6a),p1(0x6b),p1(0x6c),p1(0x6d),p1(0x6e),p1(0x6f) ;\ ++ .long p1(0x70),p1(0x71),p1(0x72),p1(0x73),p1(0x74),p1(0x75),p1(0x76),p1(0x77) ;\ ++ .long p1(0x78),p1(0x79),p1(0x7a),p1(0x7b),p1(0x7c),p1(0x7d),p1(0x7e),p1(0x7f) ++#define im_data4(p1) \ ++ .long p1(0x80),p1(0x81),p1(0x82),p1(0x83),p1(0x84),p1(0x85),p1(0x86),p1(0x87) ;\ ++ .long p1(0x88),p1(0x89),p1(0x8a),p1(0x8b),p1(0x8c),p1(0x8d),p1(0x8e),p1(0x8f) ;\ ++ .long p1(0x90),p1(0x91),p1(0x92),p1(0x93),p1(0x94),p1(0x95),p1(0x96),p1(0x97) ;\ ++ .long p1(0x98),p1(0x99),p1(0x9a),p1(0x9b),p1(0x9c),p1(0x9d),p1(0x9e),p1(0x9f) ++#define im_data5(p1) \ ++ .long p1(0xa0),p1(0xa1),p1(0xa2),p1(0xa3),p1(0xa4),p1(0xa5),p1(0xa6),p1(0xa7) ;\ ++ .long p1(0xa8),p1(0xa9),p1(0xaa),p1(0xab),p1(0xac),p1(0xad),p1(0xae),p1(0xaf) ;\ ++ .long p1(0xb0),p1(0xb1),p1(0xb2),p1(0xb3),p1(0xb4),p1(0xb5),p1(0xb6),p1(0xb7) ;\ ++ .long p1(0xb8),p1(0xb9),p1(0xba),p1(0xbb),p1(0xbc),p1(0xbd),p1(0xbe),p1(0xbf) ++#define im_data6(p1) \ ++ .long p1(0xc0),p1(0xc1),p1(0xc2),p1(0xc3),p1(0xc4),p1(0xc5),p1(0xc6),p1(0xc7) ;\ ++ .long p1(0xc8),p1(0xc9),p1(0xca),p1(0xcb),p1(0xcc),p1(0xcd),p1(0xce),p1(0xcf) ;\ ++ .long p1(0xd0),p1(0xd1),p1(0xd2),p1(0xd3),p1(0xd4),p1(0xd5),p1(0xd6),p1(0xd7) ;\ ++ .long p1(0xd8),p1(0xd9),p1(0xda),p1(0xdb),p1(0xdc),p1(0xdd),p1(0xde),p1(0xdf) ++#define im_data7(p1) \ ++ .long p1(0xe0),p1(0xe1),p1(0xe2),p1(0xe3),p1(0xe4),p1(0xe5),p1(0xe6),p1(0xe7) ;\ ++ .long p1(0xe8),p1(0xe9),p1(0xea),p1(0xeb),p1(0xec),p1(0xed),p1(0xee),p1(0xef) ;\ ++ .long p1(0xf0),p1(0xf1),p1(0xf2),p1(0xf3),p1(0xf4),p1(0xf5),p1(0xf6),p1(0xf7) ;\ ++ .long p1(0xf8),p1(0xf9),p1(0xfa),p1(0xfb),p1(0xfc),p1(0xfd),p1(0xfe),p1(0xff) ++ ++// S-box data - 256 entries ++ ++#define sb_data0(p1) \ ++ .long p1(0x63),p1(0x7c),p1(0x77),p1(0x7b),p1(0xf2),p1(0x6b),p1(0x6f),p1(0xc5) ;\ ++ .long p1(0x30),p1(0x01),p1(0x67),p1(0x2b),p1(0xfe),p1(0xd7),p1(0xab),p1(0x76) ;\ ++ .long p1(0xca),p1(0x82),p1(0xc9),p1(0x7d),p1(0xfa),p1(0x59),p1(0x47),p1(0xf0) ;\ ++ .long p1(0xad),p1(0xd4),p1(0xa2),p1(0xaf),p1(0x9c),p1(0xa4),p1(0x72),p1(0xc0) ++#define sb_data1(p1) \ ++ .long p1(0xb7),p1(0xfd),p1(0x93),p1(0x26),p1(0x36),p1(0x3f),p1(0xf7),p1(0xcc) ;\ ++ .long p1(0x34),p1(0xa5),p1(0xe5),p1(0xf1),p1(0x71),p1(0xd8),p1(0x31),p1(0x15) ;\ ++ .long p1(0x04),p1(0xc7),p1(0x23),p1(0xc3),p1(0x18),p1(0x96),p1(0x05),p1(0x9a) ;\ ++ .long p1(0x07),p1(0x12),p1(0x80),p1(0xe2),p1(0xeb),p1(0x27),p1(0xb2),p1(0x75) ++#define sb_data2(p1) \ ++ .long p1(0x09),p1(0x83),p1(0x2c),p1(0x1a),p1(0x1b),p1(0x6e),p1(0x5a),p1(0xa0) ;\ ++ .long p1(0x52),p1(0x3b),p1(0xd6),p1(0xb3),p1(0x29),p1(0xe3),p1(0x2f),p1(0x84) ;\ ++ .long p1(0x53),p1(0xd1),p1(0x00),p1(0xed),p1(0x20),p1(0xfc),p1(0xb1),p1(0x5b) ;\ ++ .long p1(0x6a),p1(0xcb),p1(0xbe),p1(0x39),p1(0x4a),p1(0x4c),p1(0x58),p1(0xcf) ++#define sb_data3(p1) \ ++ .long p1(0xd0),p1(0xef),p1(0xaa),p1(0xfb),p1(0x43),p1(0x4d),p1(0x33),p1(0x85) ;\ ++ .long p1(0x45),p1(0xf9),p1(0x02),p1(0x7f),p1(0x50),p1(0x3c),p1(0x9f),p1(0xa8) ;\ ++ .long p1(0x51),p1(0xa3),p1(0x40),p1(0x8f),p1(0x92),p1(0x9d),p1(0x38),p1(0xf5) ;\ ++ .long p1(0xbc),p1(0xb6),p1(0xda),p1(0x21),p1(0x10),p1(0xff),p1(0xf3),p1(0xd2) ++#define sb_data4(p1) \ ++ .long p1(0xcd),p1(0x0c),p1(0x13),p1(0xec),p1(0x5f),p1(0x97),p1(0x44),p1(0x17) ;\ ++ .long p1(0xc4),p1(0xa7),p1(0x7e),p1(0x3d),p1(0x64),p1(0x5d),p1(0x19),p1(0x73) ;\ ++ .long p1(0x60),p1(0x81),p1(0x4f),p1(0xdc),p1(0x22),p1(0x2a),p1(0x90),p1(0x88) ;\ ++ .long p1(0x46),p1(0xee),p1(0xb8),p1(0x14),p1(0xde),p1(0x5e),p1(0x0b),p1(0xdb) ++#define sb_data5(p1) \ ++ .long p1(0xe0),p1(0x32),p1(0x3a),p1(0x0a),p1(0x49),p1(0x06),p1(0x24),p1(0x5c) ;\ ++ .long p1(0xc2),p1(0xd3),p1(0xac),p1(0x62),p1(0x91),p1(0x95),p1(0xe4),p1(0x79) ;\ ++ .long p1(0xe7),p1(0xc8),p1(0x37),p1(0x6d),p1(0x8d),p1(0xd5),p1(0x4e),p1(0xa9) ;\ ++ .long p1(0x6c),p1(0x56),p1(0xf4),p1(0xea),p1(0x65),p1(0x7a),p1(0xae),p1(0x08) ++#define sb_data6(p1) \ ++ .long p1(0xba),p1(0x78),p1(0x25),p1(0x2e),p1(0x1c),p1(0xa6),p1(0xb4),p1(0xc6) ;\ ++ .long p1(0xe8),p1(0xdd),p1(0x74),p1(0x1f),p1(0x4b),p1(0xbd),p1(0x8b),p1(0x8a) ;\ ++ .long p1(0x70),p1(0x3e),p1(0xb5),p1(0x66),p1(0x48),p1(0x03),p1(0xf6),p1(0x0e) ;\ ++ .long p1(0x61),p1(0x35),p1(0x57),p1(0xb9),p1(0x86),p1(0xc1),p1(0x1d),p1(0x9e) ++#define sb_data7(p1) \ ++ .long p1(0xe1),p1(0xf8),p1(0x98),p1(0x11),p1(0x69),p1(0xd9),p1(0x8e),p1(0x94) ;\ ++ .long p1(0x9b),p1(0x1e),p1(0x87),p1(0xe9),p1(0xce),p1(0x55),p1(0x28),p1(0xdf) ;\ ++ .long p1(0x8c),p1(0xa1),p1(0x89),p1(0x0d),p1(0xbf),p1(0xe6),p1(0x42),p1(0x68) ;\ ++ .long p1(0x41),p1(0x99),p1(0x2d),p1(0x0f),p1(0xb0),p1(0x54),p1(0xbb),p1(0x16) ++ ++// Inverse S-box data - 256 entries ++ ++#define ib_data0(p1) \ ++ .long p1(0x52),p1(0x09),p1(0x6a),p1(0xd5),p1(0x30),p1(0x36),p1(0xa5),p1(0x38) ;\ ++ .long p1(0xbf),p1(0x40),p1(0xa3),p1(0x9e),p1(0x81),p1(0xf3),p1(0xd7),p1(0xfb) ;\ ++ .long p1(0x7c),p1(0xe3),p1(0x39),p1(0x82),p1(0x9b),p1(0x2f),p1(0xff),p1(0x87) ;\ ++ .long p1(0x34),p1(0x8e),p1(0x43),p1(0x44),p1(0xc4),p1(0xde),p1(0xe9),p1(0xcb) ++#define ib_data1(p1) \ ++ .long p1(0x54),p1(0x7b),p1(0x94),p1(0x32),p1(0xa6),p1(0xc2),p1(0x23),p1(0x3d) ;\ ++ .long p1(0xee),p1(0x4c),p1(0x95),p1(0x0b),p1(0x42),p1(0xfa),p1(0xc3),p1(0x4e) ;\ ++ .long p1(0x08),p1(0x2e),p1(0xa1),p1(0x66),p1(0x28),p1(0xd9),p1(0x24),p1(0xb2) ;\ ++ .long p1(0x76),p1(0x5b),p1(0xa2),p1(0x49),p1(0x6d),p1(0x8b),p1(0xd1),p1(0x25) ++#define ib_data2(p1) \ ++ .long p1(0x72),p1(0xf8),p1(0xf6),p1(0x64),p1(0x86),p1(0x68),p1(0x98),p1(0x16) ;\ ++ .long p1(0xd4),p1(0xa4),p1(0x5c),p1(0xcc),p1(0x5d),p1(0x65),p1(0xb6),p1(0x92) ;\ ++ .long p1(0x6c),p1(0x70),p1(0x48),p1(0x50),p1(0xfd),p1(0xed),p1(0xb9),p1(0xda) ;\ ++ .long p1(0x5e),p1(0x15),p1(0x46),p1(0x57),p1(0xa7),p1(0x8d),p1(0x9d),p1(0x84) ++#define ib_data3(p1) \ ++ .long p1(0x90),p1(0xd8),p1(0xab),p1(0x00),p1(0x8c),p1(0xbc),p1(0xd3),p1(0x0a) ;\ ++ .long p1(0xf7),p1(0xe4),p1(0x58),p1(0x05),p1(0xb8),p1(0xb3),p1(0x45),p1(0x06) ;\ ++ .long p1(0xd0),p1(0x2c),p1(0x1e),p1(0x8f),p1(0xca),p1(0x3f),p1(0x0f),p1(0x02) ;\ ++ .long p1(0xc1),p1(0xaf),p1(0xbd),p1(0x03),p1(0x01),p1(0x13),p1(0x8a),p1(0x6b) ++#define ib_data4(p1) \ ++ .long p1(0x3a),p1(0x91),p1(0x11),p1(0x41),p1(0x4f),p1(0x67),p1(0xdc),p1(0xea) ;\ ++ .long p1(0x97),p1(0xf2),p1(0xcf),p1(0xce),p1(0xf0),p1(0xb4),p1(0xe6),p1(0x73) ;\ ++ .long p1(0x96),p1(0xac),p1(0x74),p1(0x22),p1(0xe7),p1(0xad),p1(0x35),p1(0x85) ;\ ++ .long p1(0xe2),p1(0xf9),p1(0x37),p1(0xe8),p1(0x1c),p1(0x75),p1(0xdf),p1(0x6e) ++#define ib_data5(p1) \ ++ .long p1(0x47),p1(0xf1),p1(0x1a),p1(0x71),p1(0x1d),p1(0x29),p1(0xc5),p1(0x89) ;\ ++ .long p1(0x6f),p1(0xb7),p1(0x62),p1(0x0e),p1(0xaa),p1(0x18),p1(0xbe),p1(0x1b) ;\ ++ .long p1(0xfc),p1(0x56),p1(0x3e),p1(0x4b),p1(0xc6),p1(0xd2),p1(0x79),p1(0x20) ;\ ++ .long p1(0x9a),p1(0xdb),p1(0xc0),p1(0xfe),p1(0x78),p1(0xcd),p1(0x5a),p1(0xf4) ++#define ib_data6(p1) \ ++ .long p1(0x1f),p1(0xdd),p1(0xa8),p1(0x33),p1(0x88),p1(0x07),p1(0xc7),p1(0x31) ;\ ++ .long p1(0xb1),p1(0x12),p1(0x10),p1(0x59),p1(0x27),p1(0x80),p1(0xec),p1(0x5f) ;\ ++ .long p1(0x60),p1(0x51),p1(0x7f),p1(0xa9),p1(0x19),p1(0xb5),p1(0x4a),p1(0x0d) ;\ ++ .long p1(0x2d),p1(0xe5),p1(0x7a),p1(0x9f),p1(0x93),p1(0xc9),p1(0x9c),p1(0xef) ++#define ib_data7(p1) \ ++ .long p1(0xa0),p1(0xe0),p1(0x3b),p1(0x4d),p1(0xae),p1(0x2a),p1(0xf5),p1(0xb0) ;\ ++ .long p1(0xc8),p1(0xeb),p1(0xbb),p1(0x3c),p1(0x83),p1(0x53),p1(0x99),p1(0x61) ;\ ++ .long p1(0x17),p1(0x2b),p1(0x04),p1(0x7e),p1(0xba),p1(0x77),p1(0xd6),p1(0x26) ;\ ++ .long p1(0xe1),p1(0x69),p1(0x14),p1(0x63),p1(0x55),p1(0x21),p1(0x0c),p1(0x7d) ++ ++// The rcon_table (needed for the key schedule) ++// ++// Here is original Dr Brian Gladman's source code: ++// _rcon_tab: ++// %assign x 1 ++// %rep 29 ++// dd x ++// %assign x f2(x) ++// %endrep ++// ++// Here is precomputed output (it's more portable this way): ++ ++ .align ALIGN32BYTES ++aes_rcon_tab: ++ .long 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 ++ .long 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f ++ .long 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4 ++ .long 0xb3,0x7d,0xfa,0xef,0xc5 ++ ++// The forward xor tables ++ ++ .align ALIGN32BYTES ++aes_ft_tab: ++ sb_data0(u0) ++ sb_data1(u0) ++ sb_data2(u0) ++ sb_data3(u0) ++ sb_data4(u0) ++ sb_data5(u0) ++ sb_data6(u0) ++ sb_data7(u0) ++ ++ sb_data0(u1) ++ sb_data1(u1) ++ sb_data2(u1) ++ sb_data3(u1) ++ sb_data4(u1) ++ sb_data5(u1) ++ sb_data6(u1) ++ sb_data7(u1) ++ ++ sb_data0(u2) ++ sb_data1(u2) ++ sb_data2(u2) ++ sb_data3(u2) ++ sb_data4(u2) ++ sb_data5(u2) ++ sb_data6(u2) ++ sb_data7(u2) ++ ++ sb_data0(u3) ++ sb_data1(u3) ++ sb_data2(u3) ++ sb_data3(u3) ++ sb_data4(u3) ++ sb_data5(u3) ++ sb_data6(u3) ++ sb_data7(u3) ++ ++ .align ALIGN32BYTES ++aes_fl_tab: ++ sb_data0(w0) ++ sb_data1(w0) ++ sb_data2(w0) ++ sb_data3(w0) ++ sb_data4(w0) ++ sb_data5(w0) ++ sb_data6(w0) ++ sb_data7(w0) ++ ++ sb_data0(w1) ++ sb_data1(w1) ++ sb_data2(w1) ++ sb_data3(w1) ++ sb_data4(w1) ++ sb_data5(w1) ++ sb_data6(w1) ++ sb_data7(w1) ++ ++ sb_data0(w2) ++ sb_data1(w2) ++ sb_data2(w2) ++ sb_data3(w2) ++ sb_data4(w2) ++ sb_data5(w2) ++ sb_data6(w2) ++ sb_data7(w2) ++ ++ sb_data0(w3) ++ sb_data1(w3) ++ sb_data2(w3) ++ sb_data3(w3) ++ sb_data4(w3) ++ sb_data5(w3) ++ sb_data6(w3) ++ sb_data7(w3) ++ ++// The inverse xor tables ++ ++ .align ALIGN32BYTES ++aes_it_tab: ++ ib_data0(v0) ++ ib_data1(v0) ++ ib_data2(v0) ++ ib_data3(v0) ++ ib_data4(v0) ++ ib_data5(v0) ++ ib_data6(v0) ++ ib_data7(v0) ++ ++ ib_data0(v1) ++ ib_data1(v1) ++ ib_data2(v1) ++ ib_data3(v1) ++ ib_data4(v1) ++ ib_data5(v1) ++ ib_data6(v1) ++ ib_data7(v1) ++ ++ ib_data0(v2) ++ ib_data1(v2) ++ ib_data2(v2) ++ ib_data3(v2) ++ ib_data4(v2) ++ ib_data5(v2) ++ ib_data6(v2) ++ ib_data7(v2) ++ ++ ib_data0(v3) ++ ib_data1(v3) ++ ib_data2(v3) ++ ib_data3(v3) ++ ib_data4(v3) ++ ib_data5(v3) ++ ib_data6(v3) ++ ib_data7(v3) ++ ++ .align ALIGN32BYTES ++aes_il_tab: ++ ib_data0(w0) ++ ib_data1(w0) ++ ib_data2(w0) ++ ib_data3(w0) ++ ib_data4(w0) ++ ib_data5(w0) ++ ib_data6(w0) ++ ib_data7(w0) ++ ++ ib_data0(w1) ++ ib_data1(w1) ++ ib_data2(w1) ++ ib_data3(w1) ++ ib_data4(w1) ++ ib_data5(w1) ++ ib_data6(w1) ++ ib_data7(w1) ++ ++ ib_data0(w2) ++ ib_data1(w2) ++ ib_data2(w2) ++ ib_data3(w2) ++ ib_data4(w2) ++ ib_data5(w2) ++ ib_data6(w2) ++ ib_data7(w2) ++ ++ ib_data0(w3) ++ ib_data1(w3) ++ ib_data2(w3) ++ ib_data3(w3) ++ ib_data4(w3) ++ ib_data5(w3) ++ ib_data6(w3) ++ ib_data7(w3) ++ ++// The inverse mix column tables ++ ++ .align ALIGN32BYTES ++aes_im_tab: ++ im_data0(v0) ++ im_data1(v0) ++ im_data2(v0) ++ im_data3(v0) ++ im_data4(v0) ++ im_data5(v0) ++ im_data6(v0) ++ im_data7(v0) ++ ++ im_data0(v1) ++ im_data1(v1) ++ im_data2(v1) ++ im_data3(v1) ++ im_data4(v1) ++ im_data5(v1) ++ im_data6(v1) ++ im_data7(v1) ++ ++ im_data0(v2) ++ im_data1(v2) ++ im_data2(v2) ++ im_data3(v2) ++ im_data4(v2) ++ im_data5(v2) ++ im_data6(v2) ++ im_data7(v2) ++ ++ im_data0(v3) ++ im_data1(v3) ++ im_data2(v3) ++ im_data3(v3) ++ im_data4(v3) ++ im_data5(v3) ++ im_data6(v3) ++ im_data7(v3) +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/aes.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1415 @@ ++// I retain copyright in this code but I encourage its free use provided ++// that I don't carry any responsibility for the results. I am especially ++// happy to see it used in free and open source software. If you do use ++// it I would appreciate an acknowledgement of its origin in the code or ++// the product that results and I would also appreciate knowing a little ++// about the use to which it is being put. I am grateful to Frank Yellin ++// for some ideas that are used in this implementation. ++// ++// Dr B. R. Gladman 6th April 2001. ++// ++// This is an implementation of the AES encryption algorithm (Rijndael) ++// designed by Joan Daemen and Vincent Rijmen. This version is designed ++// to provide both fixed and dynamic block and key lengths and can also ++// run with either big or little endian internal byte order (see aes.h). ++// It inputs block and key lengths in bytes with the legal values being ++// 16, 24 and 32. ++ ++/* ++ * Modified by Jari Ruusu, May 1 2001 ++ * - Fixed some compile warnings, code was ok but gcc warned anyway. ++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t ++ * - Major name space cleanup: Names visible to outside now begin ++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c ++ * - Removed C++ and DLL support as part of name space cleanup. ++ * - Eliminated unnecessary recomputation of tables. (actual bug fix) ++ * - Merged precomputed constant tables to aes.c file. ++ * - Removed data alignment restrictions for portability reasons. ++ * - Made block and key lengths accept bit count (128/192/256) ++ * as well byte count (16/24/32). ++ * - Removed all error checks. This change also eliminated the need ++ * to preinitialize the context struct to zero. ++ * - Removed some totally unused constants. ++ */ ++ ++#include "crypto/aes.h" ++ ++// CONFIGURATION OPTIONS (see also aes.h) ++// ++// 1. Define UNROLL for full loop unrolling in encryption and decryption. ++// 2. Define PARTIAL_UNROLL to unroll two loops in encryption and decryption. ++// 3. Define FIXED_TABLES for compiled rather than dynamic tables. ++// 4. Define FF_TABLES to use tables for field multiplies and inverses. ++// Do not enable this without understanding stack space requirements. ++// 5. Define ARRAYS to use arrays to hold the local state block. If this ++// is not defined, individually declared 32-bit words are used. ++// 6. Define FAST_VARIABLE if a high speed variable block implementation ++// is needed (essentially three separate fixed block size code sequences) ++// 7. Define either ONE_TABLE or FOUR_TABLES for a fast table driven ++// version using 1 table (2 kbytes of table space) or 4 tables (8 ++// kbytes of table space) for higher speed. ++// 8. Define either ONE_LR_TABLE or FOUR_LR_TABLES for a further speed ++// increase by using tables for the last rounds but with more table ++// space (2 or 8 kbytes extra). ++// 9. If neither ONE_TABLE nor FOUR_TABLES is defined, a compact but ++// slower version is provided. ++// 10. If fast decryption key scheduling is needed define ONE_IM_TABLE ++// or FOUR_IM_TABLES for higher speed (2 or 8 kbytes extra). ++ ++#define UNROLL ++//#define PARTIAL_UNROLL ++ ++#define FIXED_TABLES ++//#define FF_TABLES ++//#define ARRAYS ++#define FAST_VARIABLE ++ ++//#define ONE_TABLE ++#define FOUR_TABLES ++ ++//#define ONE_LR_TABLE ++#define FOUR_LR_TABLES ++ ++//#define ONE_IM_TABLE ++#define FOUR_IM_TABLES ++ ++#if defined(UNROLL) && defined (PARTIAL_UNROLL) ++#error both UNROLL and PARTIAL_UNROLL are defined ++#endif ++ ++#if defined(ONE_TABLE) && defined (FOUR_TABLES) ++#error both ONE_TABLE and FOUR_TABLES are defined ++#endif ++ ++#if defined(ONE_LR_TABLE) && defined (FOUR_LR_TABLES) ++#error both ONE_LR_TABLE and FOUR_LR_TABLES are defined ++#endif ++ ++#if defined(ONE_IM_TABLE) && defined (FOUR_IM_TABLES) ++#error both ONE_IM_TABLE and FOUR_IM_TABLES are defined ++#endif ++ ++#if defined(AES_BLOCK_SIZE) && AES_BLOCK_SIZE != 16 && AES_BLOCK_SIZE != 24 && AES_BLOCK_SIZE != 32 ++#error an illegal block size has been specified ++#endif ++ ++// upr(x,n): rotates bytes within words by n positions, moving bytes ++// to higher index positions with wrap around into low positions ++// ups(x,n): moves bytes by n positions to higher index positions in ++// words but without wrap around ++// bval(x,n): extracts a byte from a word ++ ++#define upr(x,n) (((x) << 8 * (n)) | ((x) >> (32 - 8 * (n)))) ++#define ups(x,n) ((x) << 8 * (n)) ++#define bval(x,n) ((unsigned char)((x) >> 8 * (n))) ++#define bytes2word(b0, b1, b2, b3) \ ++ ((u_int32_t)(b3) << 24 | (u_int32_t)(b2) << 16 | (u_int32_t)(b1) << 8 | (b0)) ++ ++ ++/* little endian processor without data alignment restrictions: AES_LE_OK */ ++/* original code: i386 */ ++#if defined(i386) || defined(_I386) || defined(__i386__) || defined(__i386) ++#define AES_LE_OK 1 ++/* added (tested): alpha --jjo */ ++#elif defined(__alpha__)|| defined (__alpha) ++#define AES_LE_OK 1 ++/* added (tested): ia64 --jjo */ ++#elif defined(__ia64__)|| defined (__ia64) ++#define AES_LE_OK 1 ++#endif ++ ++#ifdef AES_LE_OK ++/* little endian processor without data alignment restrictions */ ++#define word_in(x) *(u_int32_t*)(x) ++#define const_word_in(x) *(const u_int32_t*)(x) ++#define word_out(x,v) *(u_int32_t*)(x) = (v) ++#define const_word_out(x,v) *(const u_int32_t*)(x) = (v) ++#else ++/* slower but generic big endian or with data alignment restrictions */ ++/* some additional "const" touches to stop "gcc -Wcast-qual" complains --jjo */ ++#define word_in(x) ((u_int32_t)(((unsigned char *)(x))[0])|((u_int32_t)(((unsigned char *)(x))[1])<<8)|((u_int32_t)(((unsigned char *)(x))[2])<<16)|((u_int32_t)(((unsigned char *)(x))[3])<<24)) ++#define const_word_in(x) ((const u_int32_t)(((const unsigned char *)(x))[0])|((const u_int32_t)(((const unsigned char *)(x))[1])<<8)|((const u_int32_t)(((const unsigned char *)(x))[2])<<16)|((const u_int32_t)(((const unsigned char *)(x))[3])<<24)) ++#define word_out(x,v) ((unsigned char *)(x))[0]=(v),((unsigned char *)(x))[1]=((v)>>8),((unsigned char *)(x))[2]=((v)>>16),((unsigned char *)(x))[3]=((v)>>24) ++#define const_word_out(x,v) ((const unsigned char *)(x))[0]=(v),((const unsigned char *)(x))[1]=((v)>>8),((const unsigned char *)(x))[2]=((v)>>16),((const unsigned char *)(x))[3]=((v)>>24) ++#endif ++ ++// Disable at least some poor combinations of options ++ ++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) ++#define FIXED_TABLES ++#undef UNROLL ++#undef ONE_LR_TABLE ++#undef FOUR_LR_TABLES ++#undef ONE_IM_TABLE ++#undef FOUR_IM_TABLES ++#elif !defined(FOUR_TABLES) ++#ifdef FOUR_LR_TABLES ++#undef FOUR_LR_TABLES ++#define ONE_LR_TABLE ++#endif ++#ifdef FOUR_IM_TABLES ++#undef FOUR_IM_TABLES ++#define ONE_IM_TABLE ++#endif ++#elif !defined(AES_BLOCK_SIZE) ++#if defined(UNROLL) ++#define PARTIAL_UNROLL ++#undef UNROLL ++#endif ++#endif ++ ++// the finite field modular polynomial and elements ++ ++#define ff_poly 0x011b ++#define ff_hi 0x80 ++ ++// multiply four bytes in GF(2^8) by 'x' {02} in parallel ++ ++#define m1 0x80808080 ++#define m2 0x7f7f7f7f ++#define m3 0x0000001b ++#define FFmulX(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * m3)) ++ ++// The following defines provide alternative definitions of FFmulX that might ++// give improved performance if a fast 32-bit multiply is not available. Note ++// that a temporary variable u needs to be defined where FFmulX is used. ++ ++// #define FFmulX(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6)) ++// #define m4 0x1b1b1b1b ++// #define FFmulX(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4) ++ ++// perform column mix operation on four bytes in parallel ++ ++#define fwd_mcol(x) (f2 = FFmulX(x), f2 ^ upr(x ^ f2,3) ^ upr(x,2) ^ upr(x,1)) ++ ++#if defined(FIXED_TABLES) ++ ++// the S-Box table ++ ++static const unsigned char s_box[256] = ++{ ++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, ++ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, ++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, ++ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, ++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, ++ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, ++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, ++ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, ++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, ++ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, ++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, ++ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, ++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, ++ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, ++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, ++ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, ++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, ++ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, ++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, ++ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, ++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, ++ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, ++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, ++ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, ++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, ++ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, ++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, ++ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, ++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, ++ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, ++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, ++ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ++}; ++ ++// the inverse S-Box table ++ ++static const unsigned char inv_s_box[256] = ++{ ++ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, ++ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, ++ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, ++ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, ++ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, ++ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, ++ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, ++ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, ++ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, ++ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, ++ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, ++ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, ++ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, ++ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, ++ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, ++ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, ++ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, ++ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, ++ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, ++ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, ++ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, ++ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, ++ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, ++ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, ++ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, ++ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, ++ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, ++ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, ++ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, ++ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, ++ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, ++ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ++}; ++ ++#define w0(p) 0x000000##p ++ ++// Number of elements required in this table for different ++// block and key lengths is: ++// ++// Nk = 4 6 8 ++// ---------- ++// Nb = 4 | 10 8 7 ++// 6 | 19 12 11 ++// 8 | 29 19 14 ++// ++// this table can be a table of bytes if the key schedule ++// code is adjusted accordingly ++ ++static const u_int32_t rcon_tab[29] = ++{ ++ w0(01), w0(02), w0(04), w0(08), ++ w0(10), w0(20), w0(40), w0(80), ++ w0(1b), w0(36), w0(6c), w0(d8), ++ w0(ab), w0(4d), w0(9a), w0(2f), ++ w0(5e), w0(bc), w0(63), w0(c6), ++ w0(97), w0(35), w0(6a), w0(d4), ++ w0(b3), w0(7d), w0(fa), w0(ef), ++ w0(c5) ++}; ++ ++#undef w0 ++ ++#define r0(p,q,r,s) 0x##p##q##r##s ++#define r1(p,q,r,s) 0x##q##r##s##p ++#define r2(p,q,r,s) 0x##r##s##p##q ++#define r3(p,q,r,s) 0x##s##p##q##r ++#define w0(p) 0x000000##p ++#define w1(p) 0x0000##p##00 ++#define w2(p) 0x00##p##0000 ++#define w3(p) 0x##p##000000 ++ ++#if defined(FIXED_TABLES) && (defined(ONE_TABLE) || defined(FOUR_TABLES)) ++ ++// data for forward tables (other than last round) ++ ++#define f_table \ ++ r(a5,63,63,c6), r(84,7c,7c,f8), r(99,77,77,ee), r(8d,7b,7b,f6),\ ++ r(0d,f2,f2,ff), r(bd,6b,6b,d6), r(b1,6f,6f,de), r(54,c5,c5,91),\ ++ r(50,30,30,60), r(03,01,01,02), r(a9,67,67,ce), r(7d,2b,2b,56),\ ++ r(19,fe,fe,e7), r(62,d7,d7,b5), r(e6,ab,ab,4d), r(9a,76,76,ec),\ ++ r(45,ca,ca,8f), r(9d,82,82,1f), r(40,c9,c9,89), r(87,7d,7d,fa),\ ++ r(15,fa,fa,ef), r(eb,59,59,b2), r(c9,47,47,8e), r(0b,f0,f0,fb),\ ++ r(ec,ad,ad,41), r(67,d4,d4,b3), r(fd,a2,a2,5f), r(ea,af,af,45),\ ++ r(bf,9c,9c,23), r(f7,a4,a4,53), r(96,72,72,e4), r(5b,c0,c0,9b),\ ++ r(c2,b7,b7,75), r(1c,fd,fd,e1), r(ae,93,93,3d), r(6a,26,26,4c),\ ++ r(5a,36,36,6c), r(41,3f,3f,7e), r(02,f7,f7,f5), r(4f,cc,cc,83),\ ++ r(5c,34,34,68), r(f4,a5,a5,51), r(34,e5,e5,d1), r(08,f1,f1,f9),\ ++ r(93,71,71,e2), r(73,d8,d8,ab), r(53,31,31,62), r(3f,15,15,2a),\ ++ r(0c,04,04,08), r(52,c7,c7,95), r(65,23,23,46), r(5e,c3,c3,9d),\ ++ r(28,18,18,30), r(a1,96,96,37), r(0f,05,05,0a), r(b5,9a,9a,2f),\ ++ r(09,07,07,0e), r(36,12,12,24), r(9b,80,80,1b), r(3d,e2,e2,df),\ ++ r(26,eb,eb,cd), r(69,27,27,4e), r(cd,b2,b2,7f), r(9f,75,75,ea),\ ++ r(1b,09,09,12), r(9e,83,83,1d), r(74,2c,2c,58), r(2e,1a,1a,34),\ ++ r(2d,1b,1b,36), r(b2,6e,6e,dc), r(ee,5a,5a,b4), r(fb,a0,a0,5b),\ ++ r(f6,52,52,a4), r(4d,3b,3b,76), r(61,d6,d6,b7), r(ce,b3,b3,7d),\ ++ r(7b,29,29,52), r(3e,e3,e3,dd), r(71,2f,2f,5e), r(97,84,84,13),\ ++ r(f5,53,53,a6), r(68,d1,d1,b9), r(00,00,00,00), r(2c,ed,ed,c1),\ ++ r(60,20,20,40), r(1f,fc,fc,e3), r(c8,b1,b1,79), r(ed,5b,5b,b6),\ ++ r(be,6a,6a,d4), r(46,cb,cb,8d), r(d9,be,be,67), r(4b,39,39,72),\ ++ r(de,4a,4a,94), r(d4,4c,4c,98), r(e8,58,58,b0), r(4a,cf,cf,85),\ ++ r(6b,d0,d0,bb), r(2a,ef,ef,c5), r(e5,aa,aa,4f), r(16,fb,fb,ed),\ ++ r(c5,43,43,86), r(d7,4d,4d,9a), r(55,33,33,66), r(94,85,85,11),\ ++ r(cf,45,45,8a), r(10,f9,f9,e9), r(06,02,02,04), r(81,7f,7f,fe),\ ++ r(f0,50,50,a0), r(44,3c,3c,78), r(ba,9f,9f,25), r(e3,a8,a8,4b),\ ++ r(f3,51,51,a2), r(fe,a3,a3,5d), r(c0,40,40,80), r(8a,8f,8f,05),\ ++ r(ad,92,92,3f), r(bc,9d,9d,21), r(48,38,38,70), r(04,f5,f5,f1),\ ++ r(df,bc,bc,63), r(c1,b6,b6,77), r(75,da,da,af), r(63,21,21,42),\ ++ r(30,10,10,20), r(1a,ff,ff,e5), r(0e,f3,f3,fd), r(6d,d2,d2,bf),\ ++ r(4c,cd,cd,81), r(14,0c,0c,18), r(35,13,13,26), r(2f,ec,ec,c3),\ ++ r(e1,5f,5f,be), r(a2,97,97,35), r(cc,44,44,88), r(39,17,17,2e),\ ++ r(57,c4,c4,93), r(f2,a7,a7,55), r(82,7e,7e,fc), r(47,3d,3d,7a),\ ++ r(ac,64,64,c8), r(e7,5d,5d,ba), r(2b,19,19,32), r(95,73,73,e6),\ ++ r(a0,60,60,c0), r(98,81,81,19), r(d1,4f,4f,9e), r(7f,dc,dc,a3),\ ++ r(66,22,22,44), r(7e,2a,2a,54), r(ab,90,90,3b), r(83,88,88,0b),\ ++ r(ca,46,46,8c), r(29,ee,ee,c7), r(d3,b8,b8,6b), r(3c,14,14,28),\ ++ r(79,de,de,a7), r(e2,5e,5e,bc), r(1d,0b,0b,16), r(76,db,db,ad),\ ++ r(3b,e0,e0,db), r(56,32,32,64), r(4e,3a,3a,74), r(1e,0a,0a,14),\ ++ r(db,49,49,92), r(0a,06,06,0c), r(6c,24,24,48), r(e4,5c,5c,b8),\ ++ r(5d,c2,c2,9f), r(6e,d3,d3,bd), r(ef,ac,ac,43), r(a6,62,62,c4),\ ++ r(a8,91,91,39), r(a4,95,95,31), r(37,e4,e4,d3), r(8b,79,79,f2),\ ++ r(32,e7,e7,d5), r(43,c8,c8,8b), r(59,37,37,6e), r(b7,6d,6d,da),\ ++ r(8c,8d,8d,01), r(64,d5,d5,b1), r(d2,4e,4e,9c), r(e0,a9,a9,49),\ ++ r(b4,6c,6c,d8), r(fa,56,56,ac), r(07,f4,f4,f3), r(25,ea,ea,cf),\ ++ r(af,65,65,ca), r(8e,7a,7a,f4), r(e9,ae,ae,47), r(18,08,08,10),\ ++ r(d5,ba,ba,6f), r(88,78,78,f0), r(6f,25,25,4a), r(72,2e,2e,5c),\ ++ r(24,1c,1c,38), r(f1,a6,a6,57), r(c7,b4,b4,73), r(51,c6,c6,97),\ ++ r(23,e8,e8,cb), r(7c,dd,dd,a1), r(9c,74,74,e8), r(21,1f,1f,3e),\ ++ r(dd,4b,4b,96), r(dc,bd,bd,61), r(86,8b,8b,0d), r(85,8a,8a,0f),\ ++ r(90,70,70,e0), r(42,3e,3e,7c), r(c4,b5,b5,71), r(aa,66,66,cc),\ ++ r(d8,48,48,90), r(05,03,03,06), r(01,f6,f6,f7), r(12,0e,0e,1c),\ ++ r(a3,61,61,c2), r(5f,35,35,6a), r(f9,57,57,ae), r(d0,b9,b9,69),\ ++ r(91,86,86,17), r(58,c1,c1,99), r(27,1d,1d,3a), r(b9,9e,9e,27),\ ++ r(38,e1,e1,d9), r(13,f8,f8,eb), r(b3,98,98,2b), r(33,11,11,22),\ ++ r(bb,69,69,d2), r(70,d9,d9,a9), r(89,8e,8e,07), r(a7,94,94,33),\ ++ r(b6,9b,9b,2d), r(22,1e,1e,3c), r(92,87,87,15), r(20,e9,e9,c9),\ ++ r(49,ce,ce,87), r(ff,55,55,aa), r(78,28,28,50), r(7a,df,df,a5),\ ++ r(8f,8c,8c,03), r(f8,a1,a1,59), r(80,89,89,09), r(17,0d,0d,1a),\ ++ r(da,bf,bf,65), r(31,e6,e6,d7), r(c6,42,42,84), r(b8,68,68,d0),\ ++ r(c3,41,41,82), r(b0,99,99,29), r(77,2d,2d,5a), r(11,0f,0f,1e),\ ++ r(cb,b0,b0,7b), r(fc,54,54,a8), r(d6,bb,bb,6d), r(3a,16,16,2c) ++ ++// data for inverse tables (other than last round) ++ ++#define i_table \ ++ r(50,a7,f4,51), r(53,65,41,7e), r(c3,a4,17,1a), r(96,5e,27,3a),\ ++ r(cb,6b,ab,3b), r(f1,45,9d,1f), r(ab,58,fa,ac), r(93,03,e3,4b),\ ++ r(55,fa,30,20), r(f6,6d,76,ad), r(91,76,cc,88), r(25,4c,02,f5),\ ++ r(fc,d7,e5,4f), r(d7,cb,2a,c5), r(80,44,35,26), r(8f,a3,62,b5),\ ++ r(49,5a,b1,de), r(67,1b,ba,25), r(98,0e,ea,45), r(e1,c0,fe,5d),\ ++ r(02,75,2f,c3), r(12,f0,4c,81), r(a3,97,46,8d), r(c6,f9,d3,6b),\ ++ r(e7,5f,8f,03), r(95,9c,92,15), r(eb,7a,6d,bf), r(da,59,52,95),\ ++ r(2d,83,be,d4), r(d3,21,74,58), r(29,69,e0,49), r(44,c8,c9,8e),\ ++ r(6a,89,c2,75), r(78,79,8e,f4), r(6b,3e,58,99), r(dd,71,b9,27),\ ++ r(b6,4f,e1,be), r(17,ad,88,f0), r(66,ac,20,c9), r(b4,3a,ce,7d),\ ++ r(18,4a,df,63), r(82,31,1a,e5), r(60,33,51,97), r(45,7f,53,62),\ ++ r(e0,77,64,b1), r(84,ae,6b,bb), r(1c,a0,81,fe), r(94,2b,08,f9),\ ++ r(58,68,48,70), r(19,fd,45,8f), r(87,6c,de,94), r(b7,f8,7b,52),\ ++ r(23,d3,73,ab), r(e2,02,4b,72), r(57,8f,1f,e3), r(2a,ab,55,66),\ ++ r(07,28,eb,b2), r(03,c2,b5,2f), r(9a,7b,c5,86), r(a5,08,37,d3),\ ++ r(f2,87,28,30), r(b2,a5,bf,23), r(ba,6a,03,02), r(5c,82,16,ed),\ ++ r(2b,1c,cf,8a), r(92,b4,79,a7), r(f0,f2,07,f3), r(a1,e2,69,4e),\ ++ r(cd,f4,da,65), r(d5,be,05,06), r(1f,62,34,d1), r(8a,fe,a6,c4),\ ++ r(9d,53,2e,34), r(a0,55,f3,a2), r(32,e1,8a,05), r(75,eb,f6,a4),\ ++ r(39,ec,83,0b), r(aa,ef,60,40), r(06,9f,71,5e), r(51,10,6e,bd),\ ++ r(f9,8a,21,3e), r(3d,06,dd,96), r(ae,05,3e,dd), r(46,bd,e6,4d),\ ++ r(b5,8d,54,91), r(05,5d,c4,71), r(6f,d4,06,04), r(ff,15,50,60),\ ++ r(24,fb,98,19), r(97,e9,bd,d6), r(cc,43,40,89), r(77,9e,d9,67),\ ++ r(bd,42,e8,b0), r(88,8b,89,07), r(38,5b,19,e7), r(db,ee,c8,79),\ ++ r(47,0a,7c,a1), r(e9,0f,42,7c), r(c9,1e,84,f8), r(00,00,00,00),\ ++ r(83,86,80,09), r(48,ed,2b,32), r(ac,70,11,1e), r(4e,72,5a,6c),\ ++ r(fb,ff,0e,fd), r(56,38,85,0f), r(1e,d5,ae,3d), r(27,39,2d,36),\ ++ r(64,d9,0f,0a), r(21,a6,5c,68), r(d1,54,5b,9b), r(3a,2e,36,24),\ ++ r(b1,67,0a,0c), r(0f,e7,57,93), r(d2,96,ee,b4), r(9e,91,9b,1b),\ ++ r(4f,c5,c0,80), r(a2,20,dc,61), r(69,4b,77,5a), r(16,1a,12,1c),\ ++ r(0a,ba,93,e2), r(e5,2a,a0,c0), r(43,e0,22,3c), r(1d,17,1b,12),\ ++ r(0b,0d,09,0e), r(ad,c7,8b,f2), r(b9,a8,b6,2d), r(c8,a9,1e,14),\ ++ r(85,19,f1,57), r(4c,07,75,af), r(bb,dd,99,ee), r(fd,60,7f,a3),\ ++ r(9f,26,01,f7), r(bc,f5,72,5c), r(c5,3b,66,44), r(34,7e,fb,5b),\ ++ r(76,29,43,8b), r(dc,c6,23,cb), r(68,fc,ed,b6), r(63,f1,e4,b8),\ ++ r(ca,dc,31,d7), r(10,85,63,42), r(40,22,97,13), r(20,11,c6,84),\ ++ r(7d,24,4a,85), r(f8,3d,bb,d2), r(11,32,f9,ae), r(6d,a1,29,c7),\ ++ r(4b,2f,9e,1d), r(f3,30,b2,dc), r(ec,52,86,0d), r(d0,e3,c1,77),\ ++ r(6c,16,b3,2b), r(99,b9,70,a9), r(fa,48,94,11), r(22,64,e9,47),\ ++ r(c4,8c,fc,a8), r(1a,3f,f0,a0), r(d8,2c,7d,56), r(ef,90,33,22),\ ++ r(c7,4e,49,87), r(c1,d1,38,d9), r(fe,a2,ca,8c), r(36,0b,d4,98),\ ++ r(cf,81,f5,a6), r(28,de,7a,a5), r(26,8e,b7,da), r(a4,bf,ad,3f),\ ++ r(e4,9d,3a,2c), r(0d,92,78,50), r(9b,cc,5f,6a), r(62,46,7e,54),\ ++ r(c2,13,8d,f6), r(e8,b8,d8,90), r(5e,f7,39,2e), r(f5,af,c3,82),\ ++ r(be,80,5d,9f), r(7c,93,d0,69), r(a9,2d,d5,6f), r(b3,12,25,cf),\ ++ r(3b,99,ac,c8), r(a7,7d,18,10), r(6e,63,9c,e8), r(7b,bb,3b,db),\ ++ r(09,78,26,cd), r(f4,18,59,6e), r(01,b7,9a,ec), r(a8,9a,4f,83),\ ++ r(65,6e,95,e6), r(7e,e6,ff,aa), r(08,cf,bc,21), r(e6,e8,15,ef),\ ++ r(d9,9b,e7,ba), r(ce,36,6f,4a), r(d4,09,9f,ea), r(d6,7c,b0,29),\ ++ r(af,b2,a4,31), r(31,23,3f,2a), r(30,94,a5,c6), r(c0,66,a2,35),\ ++ r(37,bc,4e,74), r(a6,ca,82,fc), r(b0,d0,90,e0), r(15,d8,a7,33),\ ++ r(4a,98,04,f1), r(f7,da,ec,41), r(0e,50,cd,7f), r(2f,f6,91,17),\ ++ r(8d,d6,4d,76), r(4d,b0,ef,43), r(54,4d,aa,cc), r(df,04,96,e4),\ ++ r(e3,b5,d1,9e), r(1b,88,6a,4c), r(b8,1f,2c,c1), r(7f,51,65,46),\ ++ r(04,ea,5e,9d), r(5d,35,8c,01), r(73,74,87,fa), r(2e,41,0b,fb),\ ++ r(5a,1d,67,b3), r(52,d2,db,92), r(33,56,10,e9), r(13,47,d6,6d),\ ++ r(8c,61,d7,9a), r(7a,0c,a1,37), r(8e,14,f8,59), r(89,3c,13,eb),\ ++ r(ee,27,a9,ce), r(35,c9,61,b7), r(ed,e5,1c,e1), r(3c,b1,47,7a),\ ++ r(59,df,d2,9c), r(3f,73,f2,55), r(79,ce,14,18), r(bf,37,c7,73),\ ++ r(ea,cd,f7,53), r(5b,aa,fd,5f), r(14,6f,3d,df), r(86,db,44,78),\ ++ r(81,f3,af,ca), r(3e,c4,68,b9), r(2c,34,24,38), r(5f,40,a3,c2),\ ++ r(72,c3,1d,16), r(0c,25,e2,bc), r(8b,49,3c,28), r(41,95,0d,ff),\ ++ r(71,01,a8,39), r(de,b3,0c,08), r(9c,e4,b4,d8), r(90,c1,56,64),\ ++ r(61,84,cb,7b), r(70,b6,32,d5), r(74,5c,6c,48), r(42,57,b8,d0) ++ ++// generate the required tables in the desired endian format ++ ++#undef r ++#define r r0 ++ ++#if defined(ONE_TABLE) ++static const u_int32_t ft_tab[256] = ++ { f_table }; ++#elif defined(FOUR_TABLES) ++static const u_int32_t ft_tab[4][256] = ++{ { f_table }, ++#undef r ++#define r r1 ++ { f_table }, ++#undef r ++#define r r2 ++ { f_table }, ++#undef r ++#define r r3 ++ { f_table } ++}; ++#endif ++ ++#undef r ++#define r r0 ++#if defined(ONE_TABLE) ++static const u_int32_t it_tab[256] = ++ { i_table }; ++#elif defined(FOUR_TABLES) ++static const u_int32_t it_tab[4][256] = ++{ { i_table }, ++#undef r ++#define r r1 ++ { i_table }, ++#undef r ++#define r r2 ++ { i_table }, ++#undef r ++#define r r3 ++ { i_table } ++}; ++#endif ++ ++#endif ++ ++#if defined(FIXED_TABLES) && (defined(ONE_LR_TABLE) || defined(FOUR_LR_TABLES)) ++ ++// data for inverse tables (last round) ++ ++#define li_table \ ++ w(52), w(09), w(6a), w(d5), w(30), w(36), w(a5), w(38),\ ++ w(bf), w(40), w(a3), w(9e), w(81), w(f3), w(d7), w(fb),\ ++ w(7c), w(e3), w(39), w(82), w(9b), w(2f), w(ff), w(87),\ ++ w(34), w(8e), w(43), w(44), w(c4), w(de), w(e9), w(cb),\ ++ w(54), w(7b), w(94), w(32), w(a6), w(c2), w(23), w(3d),\ ++ w(ee), w(4c), w(95), w(0b), w(42), w(fa), w(c3), w(4e),\ ++ w(08), w(2e), w(a1), w(66), w(28), w(d9), w(24), w(b2),\ ++ w(76), w(5b), w(a2), w(49), w(6d), w(8b), w(d1), w(25),\ ++ w(72), w(f8), w(f6), w(64), w(86), w(68), w(98), w(16),\ ++ w(d4), w(a4), w(5c), w(cc), w(5d), w(65), w(b6), w(92),\ ++ w(6c), w(70), w(48), w(50), w(fd), w(ed), w(b9), w(da),\ ++ w(5e), w(15), w(46), w(57), w(a7), w(8d), w(9d), w(84),\ ++ w(90), w(d8), w(ab), w(00), w(8c), w(bc), w(d3), w(0a),\ ++ w(f7), w(e4), w(58), w(05), w(b8), w(b3), w(45), w(06),\ ++ w(d0), w(2c), w(1e), w(8f), w(ca), w(3f), w(0f), w(02),\ ++ w(c1), w(af), w(bd), w(03), w(01), w(13), w(8a), w(6b),\ ++ w(3a), w(91), w(11), w(41), w(4f), w(67), w(dc), w(ea),\ ++ w(97), w(f2), w(cf), w(ce), w(f0), w(b4), w(e6), w(73),\ ++ w(96), w(ac), w(74), w(22), w(e7), w(ad), w(35), w(85),\ ++ w(e2), w(f9), w(37), w(e8), w(1c), w(75), w(df), w(6e),\ ++ w(47), w(f1), w(1a), w(71), w(1d), w(29), w(c5), w(89),\ ++ w(6f), w(b7), w(62), w(0e), w(aa), w(18), w(be), w(1b),\ ++ w(fc), w(56), w(3e), w(4b), w(c6), w(d2), w(79), w(20),\ ++ w(9a), w(db), w(c0), w(fe), w(78), w(cd), w(5a), w(f4),\ ++ w(1f), w(dd), w(a8), w(33), w(88), w(07), w(c7), w(31),\ ++ w(b1), w(12), w(10), w(59), w(27), w(80), w(ec), w(5f),\ ++ w(60), w(51), w(7f), w(a9), w(19), w(b5), w(4a), w(0d),\ ++ w(2d), w(e5), w(7a), w(9f), w(93), w(c9), w(9c), w(ef),\ ++ w(a0), w(e0), w(3b), w(4d), w(ae), w(2a), w(f5), w(b0),\ ++ w(c8), w(eb), w(bb), w(3c), w(83), w(53), w(99), w(61),\ ++ w(17), w(2b), w(04), w(7e), w(ba), w(77), w(d6), w(26),\ ++ w(e1), w(69), w(14), w(63), w(55), w(21), w(0c), w(7d), ++ ++// generate the required tables in the desired endian format ++ ++#undef r ++#define r(p,q,r,s) w0(q) ++#if defined(ONE_LR_TABLE) ++static const u_int32_t fl_tab[256] = ++ { f_table }; ++#elif defined(FOUR_LR_TABLES) ++static const u_int32_t fl_tab[4][256] = ++{ { f_table }, ++#undef r ++#define r(p,q,r,s) w1(q) ++ { f_table }, ++#undef r ++#define r(p,q,r,s) w2(q) ++ { f_table }, ++#undef r ++#define r(p,q,r,s) w3(q) ++ { f_table } ++}; ++#endif ++ ++#undef w ++#define w w0 ++#if defined(ONE_LR_TABLE) ++static const u_int32_t il_tab[256] = ++ { li_table }; ++#elif defined(FOUR_LR_TABLES) ++static const u_int32_t il_tab[4][256] = ++{ { li_table }, ++#undef w ++#define w w1 ++ { li_table }, ++#undef w ++#define w w2 ++ { li_table }, ++#undef w ++#define w w3 ++ { li_table } ++}; ++#endif ++ ++#endif ++ ++#if defined(FIXED_TABLES) && (defined(ONE_IM_TABLE) || defined(FOUR_IM_TABLES)) ++ ++#define m_table \ ++ r(00,00,00,00), r(0b,0d,09,0e), r(16,1a,12,1c), r(1d,17,1b,12),\ ++ r(2c,34,24,38), r(27,39,2d,36), r(3a,2e,36,24), r(31,23,3f,2a),\ ++ r(58,68,48,70), r(53,65,41,7e), r(4e,72,5a,6c), r(45,7f,53,62),\ ++ r(74,5c,6c,48), r(7f,51,65,46), r(62,46,7e,54), r(69,4b,77,5a),\ ++ r(b0,d0,90,e0), r(bb,dd,99,ee), r(a6,ca,82,fc), r(ad,c7,8b,f2),\ ++ r(9c,e4,b4,d8), r(97,e9,bd,d6), r(8a,fe,a6,c4), r(81,f3,af,ca),\ ++ r(e8,b8,d8,90), r(e3,b5,d1,9e), r(fe,a2,ca,8c), r(f5,af,c3,82),\ ++ r(c4,8c,fc,a8), r(cf,81,f5,a6), r(d2,96,ee,b4), r(d9,9b,e7,ba),\ ++ r(7b,bb,3b,db), r(70,b6,32,d5), r(6d,a1,29,c7), r(66,ac,20,c9),\ ++ r(57,8f,1f,e3), r(5c,82,16,ed), r(41,95,0d,ff), r(4a,98,04,f1),\ ++ r(23,d3,73,ab), r(28,de,7a,a5), r(35,c9,61,b7), r(3e,c4,68,b9),\ ++ r(0f,e7,57,93), r(04,ea,5e,9d), r(19,fd,45,8f), r(12,f0,4c,81),\ ++ r(cb,6b,ab,3b), r(c0,66,a2,35), r(dd,71,b9,27), r(d6,7c,b0,29),\ ++ r(e7,5f,8f,03), r(ec,52,86,0d), r(f1,45,9d,1f), r(fa,48,94,11),\ ++ r(93,03,e3,4b), r(98,0e,ea,45), r(85,19,f1,57), r(8e,14,f8,59),\ ++ r(bf,37,c7,73), r(b4,3a,ce,7d), r(a9,2d,d5,6f), r(a2,20,dc,61),\ ++ r(f6,6d,76,ad), r(fd,60,7f,a3), r(e0,77,64,b1), r(eb,7a,6d,bf),\ ++ r(da,59,52,95), r(d1,54,5b,9b), r(cc,43,40,89), r(c7,4e,49,87),\ ++ r(ae,05,3e,dd), r(a5,08,37,d3), r(b8,1f,2c,c1), r(b3,12,25,cf),\ ++ r(82,31,1a,e5), r(89,3c,13,eb), r(94,2b,08,f9), r(9f,26,01,f7),\ ++ r(46,bd,e6,4d), r(4d,b0,ef,43), r(50,a7,f4,51), r(5b,aa,fd,5f),\ ++ r(6a,89,c2,75), r(61,84,cb,7b), r(7c,93,d0,69), r(77,9e,d9,67),\ ++ r(1e,d5,ae,3d), r(15,d8,a7,33), r(08,cf,bc,21), r(03,c2,b5,2f),\ ++ r(32,e1,8a,05), r(39,ec,83,0b), r(24,fb,98,19), r(2f,f6,91,17),\ ++ r(8d,d6,4d,76), r(86,db,44,78), r(9b,cc,5f,6a), r(90,c1,56,64),\ ++ r(a1,e2,69,4e), r(aa,ef,60,40), r(b7,f8,7b,52), r(bc,f5,72,5c),\ ++ r(d5,be,05,06), r(de,b3,0c,08), r(c3,a4,17,1a), r(c8,a9,1e,14),\ ++ r(f9,8a,21,3e), r(f2,87,28,30), r(ef,90,33,22), r(e4,9d,3a,2c),\ ++ r(3d,06,dd,96), r(36,0b,d4,98), r(2b,1c,cf,8a), r(20,11,c6,84),\ ++ r(11,32,f9,ae), r(1a,3f,f0,a0), r(07,28,eb,b2), r(0c,25,e2,bc),\ ++ r(65,6e,95,e6), r(6e,63,9c,e8), r(73,74,87,fa), r(78,79,8e,f4),\ ++ r(49,5a,b1,de), r(42,57,b8,d0), r(5f,40,a3,c2), r(54,4d,aa,cc),\ ++ r(f7,da,ec,41), r(fc,d7,e5,4f), r(e1,c0,fe,5d), r(ea,cd,f7,53),\ ++ r(db,ee,c8,79), r(d0,e3,c1,77), r(cd,f4,da,65), r(c6,f9,d3,6b),\ ++ r(af,b2,a4,31), r(a4,bf,ad,3f), r(b9,a8,b6,2d), r(b2,a5,bf,23),\ ++ r(83,86,80,09), r(88,8b,89,07), r(95,9c,92,15), r(9e,91,9b,1b),\ ++ r(47,0a,7c,a1), r(4c,07,75,af), r(51,10,6e,bd), r(5a,1d,67,b3),\ ++ r(6b,3e,58,99), r(60,33,51,97), r(7d,24,4a,85), r(76,29,43,8b),\ ++ r(1f,62,34,d1), r(14,6f,3d,df), r(09,78,26,cd), r(02,75,2f,c3),\ ++ r(33,56,10,e9), r(38,5b,19,e7), r(25,4c,02,f5), r(2e,41,0b,fb),\ ++ r(8c,61,d7,9a), r(87,6c,de,94), r(9a,7b,c5,86), r(91,76,cc,88),\ ++ r(a0,55,f3,a2), r(ab,58,fa,ac), r(b6,4f,e1,be), r(bd,42,e8,b0),\ ++ r(d4,09,9f,ea), r(df,04,96,e4), r(c2,13,8d,f6), r(c9,1e,84,f8),\ ++ r(f8,3d,bb,d2), r(f3,30,b2,dc), r(ee,27,a9,ce), r(e5,2a,a0,c0),\ ++ r(3c,b1,47,7a), r(37,bc,4e,74), r(2a,ab,55,66), r(21,a6,5c,68),\ ++ r(10,85,63,42), r(1b,88,6a,4c), r(06,9f,71,5e), r(0d,92,78,50),\ ++ r(64,d9,0f,0a), r(6f,d4,06,04), r(72,c3,1d,16), r(79,ce,14,18),\ ++ r(48,ed,2b,32), r(43,e0,22,3c), r(5e,f7,39,2e), r(55,fa,30,20),\ ++ r(01,b7,9a,ec), r(0a,ba,93,e2), r(17,ad,88,f0), r(1c,a0,81,fe),\ ++ r(2d,83,be,d4), r(26,8e,b7,da), r(3b,99,ac,c8), r(30,94,a5,c6),\ ++ r(59,df,d2,9c), r(52,d2,db,92), r(4f,c5,c0,80), r(44,c8,c9,8e),\ ++ r(75,eb,f6,a4), r(7e,e6,ff,aa), r(63,f1,e4,b8), r(68,fc,ed,b6),\ ++ r(b1,67,0a,0c), r(ba,6a,03,02), r(a7,7d,18,10), r(ac,70,11,1e),\ ++ r(9d,53,2e,34), r(96,5e,27,3a), r(8b,49,3c,28), r(80,44,35,26),\ ++ r(e9,0f,42,7c), r(e2,02,4b,72), r(ff,15,50,60), r(f4,18,59,6e),\ ++ r(c5,3b,66,44), r(ce,36,6f,4a), r(d3,21,74,58), r(d8,2c,7d,56),\ ++ r(7a,0c,a1,37), r(71,01,a8,39), r(6c,16,b3,2b), r(67,1b,ba,25),\ ++ r(56,38,85,0f), r(5d,35,8c,01), r(40,22,97,13), r(4b,2f,9e,1d),\ ++ r(22,64,e9,47), r(29,69,e0,49), r(34,7e,fb,5b), r(3f,73,f2,55),\ ++ r(0e,50,cd,7f), r(05,5d,c4,71), r(18,4a,df,63), r(13,47,d6,6d),\ ++ r(ca,dc,31,d7), r(c1,d1,38,d9), r(dc,c6,23,cb), r(d7,cb,2a,c5),\ ++ r(e6,e8,15,ef), r(ed,e5,1c,e1), r(f0,f2,07,f3), r(fb,ff,0e,fd),\ ++ r(92,b4,79,a7), r(99,b9,70,a9), r(84,ae,6b,bb), r(8f,a3,62,b5),\ ++ r(be,80,5d,9f), r(b5,8d,54,91), r(a8,9a,4f,83), r(a3,97,46,8d) ++ ++#undef r ++#define r r0 ++ ++#if defined(ONE_IM_TABLE) ++static const u_int32_t im_tab[256] = ++ { m_table }; ++#elif defined(FOUR_IM_TABLES) ++static const u_int32_t im_tab[4][256] = ++{ { m_table }, ++#undef r ++#define r r1 ++ { m_table }, ++#undef r ++#define r r2 ++ { m_table }, ++#undef r ++#define r r3 ++ { m_table } ++}; ++#endif ++ ++#endif ++ ++#else ++ ++static int tab_gen = 0; ++ ++static unsigned char s_box[256]; // the S box ++static unsigned char inv_s_box[256]; // the inverse S box ++static u_int32_t rcon_tab[AES_RC_LENGTH]; // table of round constants ++ ++#if defined(ONE_TABLE) ++static u_int32_t ft_tab[256]; ++static u_int32_t it_tab[256]; ++#elif defined(FOUR_TABLES) ++static u_int32_t ft_tab[4][256]; ++static u_int32_t it_tab[4][256]; ++#endif ++ ++#if defined(ONE_LR_TABLE) ++static u_int32_t fl_tab[256]; ++static u_int32_t il_tab[256]; ++#elif defined(FOUR_LR_TABLES) ++static u_int32_t fl_tab[4][256]; ++static u_int32_t il_tab[4][256]; ++#endif ++ ++#if defined(ONE_IM_TABLE) ++static u_int32_t im_tab[256]; ++#elif defined(FOUR_IM_TABLES) ++static u_int32_t im_tab[4][256]; ++#endif ++ ++// Generate the tables for the dynamic table option ++ ++#if !defined(FF_TABLES) ++ ++// It will generally be sensible to use tables to compute finite ++// field multiplies and inverses but where memory is scarse this ++// code might sometimes be better. ++ ++// return 2 ^ (n - 1) where n is the bit number of the highest bit ++// set in x with x in the range 1 < x < 0x00000200. This form is ++// used so that locals within FFinv can be bytes rather than words ++ ++static unsigned char hibit(const u_int32_t x) ++{ unsigned char r = (unsigned char)((x >> 1) | (x >> 2)); ++ ++ r |= (r >> 2); ++ r |= (r >> 4); ++ return (r + 1) >> 1; ++} ++ ++// return the inverse of the finite field element x ++ ++static unsigned char FFinv(const unsigned char x) ++{ unsigned char p1 = x, p2 = 0x1b, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; ++ ++ if(x < 2) return x; ++ ++ for(;;) ++ { ++ if(!n1) return v1; ++ ++ while(n2 >= n1) ++ { ++ n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2); ++ } ++ ++ if(!n2) return v2; ++ ++ while(n1 >= n2) ++ { ++ n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1); ++ } ++ } ++} ++ ++// define the finite field multiplies required for Rijndael ++ ++#define FFmul02(x) ((((x) & 0x7f) << 1) ^ ((x) & 0x80 ? 0x1b : 0)) ++#define FFmul03(x) ((x) ^ FFmul02(x)) ++#define FFmul09(x) ((x) ^ FFmul02(FFmul02(FFmul02(x)))) ++#define FFmul0b(x) ((x) ^ FFmul02((x) ^ FFmul02(FFmul02(x)))) ++#define FFmul0d(x) ((x) ^ FFmul02(FFmul02((x) ^ FFmul02(x)))) ++#define FFmul0e(x) FFmul02((x) ^ FFmul02((x) ^ FFmul02(x))) ++ ++#else ++ ++#define FFinv(x) ((x) ? pow[255 - log[x]]: 0) ++ ++#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) ++#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) ++#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) ++#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) ++#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) ++#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) ++ ++#endif ++ ++// The forward and inverse affine transformations used in the S-box ++ ++#define fwd_affine(x) \ ++ (w = (u_int32_t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8))) ++ ++#define inv_affine(x) \ ++ (w = (u_int32_t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8))) ++ ++static void gen_tabs(void) ++{ u_int32_t i, w; ++ ++#if defined(FF_TABLES) ++ ++ unsigned char pow[512], log[256]; ++ ++ // log and power tables for GF(2^8) finite field with ++ // 0x011b as modular polynomial - the simplest primitive ++ // root is 0x03, used here to generate the tables ++ ++ i = 0; w = 1; ++ do ++ { ++ pow[i] = (unsigned char)w; ++ pow[i + 255] = (unsigned char)w; ++ log[w] = (unsigned char)i++; ++ w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); ++ } ++ while (w != 1); ++ ++#endif ++ ++ for(i = 0, w = 1; i < AES_RC_LENGTH; ++i) ++ { ++ rcon_tab[i] = bytes2word(w, 0, 0, 0); ++ w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); ++ } ++ ++ for(i = 0; i < 256; ++i) ++ { unsigned char b; ++ ++ s_box[i] = b = fwd_affine(FFinv((unsigned char)i)); ++ ++ w = bytes2word(b, 0, 0, 0); ++#if defined(ONE_LR_TABLE) ++ fl_tab[i] = w; ++#elif defined(FOUR_LR_TABLES) ++ fl_tab[0][i] = w; ++ fl_tab[1][i] = upr(w,1); ++ fl_tab[2][i] = upr(w,2); ++ fl_tab[3][i] = upr(w,3); ++#endif ++ w = bytes2word(FFmul02(b), b, b, FFmul03(b)); ++#if defined(ONE_TABLE) ++ ft_tab[i] = w; ++#elif defined(FOUR_TABLES) ++ ft_tab[0][i] = w; ++ ft_tab[1][i] = upr(w,1); ++ ft_tab[2][i] = upr(w,2); ++ ft_tab[3][i] = upr(w,3); ++#endif ++ inv_s_box[i] = b = FFinv(inv_affine((unsigned char)i)); ++ ++ w = bytes2word(b, 0, 0, 0); ++#if defined(ONE_LR_TABLE) ++ il_tab[i] = w; ++#elif defined(FOUR_LR_TABLES) ++ il_tab[0][i] = w; ++ il_tab[1][i] = upr(w,1); ++ il_tab[2][i] = upr(w,2); ++ il_tab[3][i] = upr(w,3); ++#endif ++ w = bytes2word(FFmul0e(b), FFmul09(b), FFmul0d(b), FFmul0b(b)); ++#if defined(ONE_TABLE) ++ it_tab[i] = w; ++#elif defined(FOUR_TABLES) ++ it_tab[0][i] = w; ++ it_tab[1][i] = upr(w,1); ++ it_tab[2][i] = upr(w,2); ++ it_tab[3][i] = upr(w,3); ++#endif ++#if defined(ONE_IM_TABLE) ++ im_tab[b] = w; ++#elif defined(FOUR_IM_TABLES) ++ im_tab[0][b] = w; ++ im_tab[1][b] = upr(w,1); ++ im_tab[2][b] = upr(w,2); ++ im_tab[3][b] = upr(w,3); ++#endif ++ ++ } ++} ++ ++#endif ++ ++#define no_table(x,box,vf,rf,c) bytes2word( \ ++ box[bval(vf(x,0,c),rf(0,c))], \ ++ box[bval(vf(x,1,c),rf(1,c))], \ ++ box[bval(vf(x,2,c),rf(2,c))], \ ++ box[bval(vf(x,3,c),rf(3,c))]) ++ ++#define one_table(x,op,tab,vf,rf,c) \ ++ ( tab[bval(vf(x,0,c),rf(0,c))] \ ++ ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \ ++ ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \ ++ ^ op(tab[bval(vf(x,3,c),rf(3,c))],3)) ++ ++#define four_tables(x,tab,vf,rf,c) \ ++ ( tab[0][bval(vf(x,0,c),rf(0,c))] \ ++ ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ ++ ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ ++ ^ tab[3][bval(vf(x,3,c),rf(3,c))]) ++ ++#define vf1(x,r,c) (x) ++#define rf1(r,c) (r) ++#define rf2(r,c) ((r-c)&3) ++ ++#if defined(FOUR_LR_TABLES) ++#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c) ++#elif defined(ONE_LR_TABLE) ++#define ls_box(x,c) one_table(x,upr,fl_tab,vf1,rf2,c) ++#else ++#define ls_box(x,c) no_table(x,s_box,vf1,rf2,c) ++#endif ++ ++#if defined(FOUR_IM_TABLES) ++#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0) ++#elif defined(ONE_IM_TABLE) ++#define inv_mcol(x) one_table(x,upr,im_tab,vf1,rf1,0) ++#else ++#define inv_mcol(x) \ ++ (f9 = (x),f2 = FFmulX(f9), f4 = FFmulX(f2), f8 = FFmulX(f4), f9 ^= f8, \ ++ f2 ^= f4 ^ f8 ^ upr(f2 ^ f9,3) ^ upr(f4 ^ f9,2) ^ upr(f9,1)) ++#endif ++ ++// Subroutine to set the block size (if variable) in bytes, legal ++// values being 16, 24 and 32. ++ ++#if defined(AES_BLOCK_SIZE) ++#define nc (AES_BLOCK_SIZE / 4) ++#else ++#define nc (cx->aes_Ncol) ++ ++void aes_set_blk(aes_context *cx, int n_bytes) ++{ ++#if !defined(FIXED_TABLES) ++ if(!tab_gen) { gen_tabs(); tab_gen = 1; } ++#endif ++ ++ switch(n_bytes) { ++ case 32: /* bytes */ ++ case 256: /* bits */ ++ nc = 8; ++ break; ++ case 24: /* bytes */ ++ case 192: /* bits */ ++ nc = 6; ++ break; ++ case 16: /* bytes */ ++ case 128: /* bits */ ++ default: ++ nc = 4; ++ break; ++ } ++} ++ ++#endif ++ ++// Initialise the key schedule from the user supplied key. The key ++// length is now specified in bytes - 16, 24 or 32 as appropriate. ++// This corresponds to bit lengths of 128, 192 and 256 bits, and ++// to Nk values of 4, 6 and 8 respectively. ++ ++#define mx(t,f) (*t++ = inv_mcol(*f),f++) ++#define cp(t,f) *t++ = *f++ ++ ++#if AES_BLOCK_SIZE == 16 ++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s) ++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s) ++#elif AES_BLOCK_SIZE == 24 ++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ ++ cp(d,s); cp(d,s) ++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ ++ mx(d,s); mx(d,s) ++#elif AES_BLOCK_SIZE == 32 ++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ ++ cp(d,s); cp(d,s); cp(d,s); cp(d,s) ++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ ++ mx(d,s); mx(d,s); mx(d,s); mx(d,s) ++#else ++ ++#define cpy(d,s) \ ++switch(nc) \ ++{ case 8: cp(d,s); cp(d,s); \ ++ case 6: cp(d,s); cp(d,s); \ ++ case 4: cp(d,s); cp(d,s); \ ++ cp(d,s); cp(d,s); \ ++} ++ ++#define mix(d,s) \ ++switch(nc) \ ++{ case 8: mx(d,s); mx(d,s); \ ++ case 6: mx(d,s); mx(d,s); \ ++ case 4: mx(d,s); mx(d,s); \ ++ mx(d,s); mx(d,s); \ ++} ++ ++#endif ++ ++void aes_set_key(aes_context *cx, const unsigned char in_key[], int n_bytes, const int f) ++{ u_int32_t *kf, *kt, rci; ++ ++#if !defined(FIXED_TABLES) ++ if(!tab_gen) { gen_tabs(); tab_gen = 1; } ++#endif ++ ++ switch(n_bytes) { ++ case 32: /* bytes */ ++ case 256: /* bits */ ++ cx->aes_Nkey = 8; ++ break; ++ case 24: /* bytes */ ++ case 192: /* bits */ ++ cx->aes_Nkey = 6; ++ break; ++ case 16: /* bytes */ ++ case 128: /* bits */ ++ default: ++ cx->aes_Nkey = 4; ++ break; ++ } ++ ++ cx->aes_Nrnd = (cx->aes_Nkey > nc ? cx->aes_Nkey : nc) + 6; ++ ++ cx->aes_e_key[0] = const_word_in(in_key ); ++ cx->aes_e_key[1] = const_word_in(in_key + 4); ++ cx->aes_e_key[2] = const_word_in(in_key + 8); ++ cx->aes_e_key[3] = const_word_in(in_key + 12); ++ ++ kf = cx->aes_e_key; ++ kt = kf + nc * (cx->aes_Nrnd + 1) - cx->aes_Nkey; ++ rci = 0; ++ ++ switch(cx->aes_Nkey) ++ { ++ case 4: do ++ { kf[4] = kf[0] ^ ls_box(kf[3],3) ^ rcon_tab[rci++]; ++ kf[5] = kf[1] ^ kf[4]; ++ kf[6] = kf[2] ^ kf[5]; ++ kf[7] = kf[3] ^ kf[6]; ++ kf += 4; ++ } ++ while(kf < kt); ++ break; ++ ++ case 6: cx->aes_e_key[4] = const_word_in(in_key + 16); ++ cx->aes_e_key[5] = const_word_in(in_key + 20); ++ do ++ { kf[ 6] = kf[0] ^ ls_box(kf[5],3) ^ rcon_tab[rci++]; ++ kf[ 7] = kf[1] ^ kf[ 6]; ++ kf[ 8] = kf[2] ^ kf[ 7]; ++ kf[ 9] = kf[3] ^ kf[ 8]; ++ kf[10] = kf[4] ^ kf[ 9]; ++ kf[11] = kf[5] ^ kf[10]; ++ kf += 6; ++ } ++ while(kf < kt); ++ break; ++ ++ case 8: cx->aes_e_key[4] = const_word_in(in_key + 16); ++ cx->aes_e_key[5] = const_word_in(in_key + 20); ++ cx->aes_e_key[6] = const_word_in(in_key + 24); ++ cx->aes_e_key[7] = const_word_in(in_key + 28); ++ do ++ { kf[ 8] = kf[0] ^ ls_box(kf[7],3) ^ rcon_tab[rci++]; ++ kf[ 9] = kf[1] ^ kf[ 8]; ++ kf[10] = kf[2] ^ kf[ 9]; ++ kf[11] = kf[3] ^ kf[10]; ++ kf[12] = kf[4] ^ ls_box(kf[11],0); ++ kf[13] = kf[5] ^ kf[12]; ++ kf[14] = kf[6] ^ kf[13]; ++ kf[15] = kf[7] ^ kf[14]; ++ kf += 8; ++ } ++ while (kf < kt); ++ break; ++ } ++ ++ if(!f) ++ { u_int32_t i; ++ ++ kt = cx->aes_d_key + nc * cx->aes_Nrnd; ++ kf = cx->aes_e_key; ++ ++ cpy(kt, kf); kt -= 2 * nc; ++ ++ for(i = 1; i < cx->aes_Nrnd; ++i) ++ { ++#if defined(ONE_TABLE) || defined(FOUR_TABLES) ++#if !defined(ONE_IM_TABLE) && !defined(FOUR_IM_TABLES) ++ u_int32_t f2, f4, f8, f9; ++#endif ++ mix(kt, kf); ++#else ++ cpy(kt, kf); ++#endif ++ kt -= 2 * nc; ++ } ++ ++ cpy(kt, kf); ++ } ++} ++ ++// y = output word, x = input word, r = row, c = column ++// for r = 0, 1, 2 and 3 = column accessed for row r ++ ++#if defined(ARRAYS) ++#define s(x,c) x[c] ++#else ++#define s(x,c) x##c ++#endif ++ ++// I am grateful to Frank Yellin for the following constructions ++// which, given the column (c) of the output state variable that ++// is being computed, return the input state variables which are ++// needed for each row (r) of the state ++ ++// For the fixed block size options, compilers reduce these two ++// expressions to fixed variable references. For variable block ++// size code conditional clauses will sometimes be returned ++ ++#define unused 77 // Sunset Strip ++ ++#define fwd_var(x,r,c) \ ++ ( r==0 ? \ ++ ( c==0 ? s(x,0) \ ++ : c==1 ? s(x,1) \ ++ : c==2 ? s(x,2) \ ++ : c==3 ? s(x,3) \ ++ : c==4 ? s(x,4) \ ++ : c==5 ? s(x,5) \ ++ : c==6 ? s(x,6) \ ++ : s(x,7)) \ ++ : r==1 ? \ ++ ( c==0 ? s(x,1) \ ++ : c==1 ? s(x,2) \ ++ : c==2 ? s(x,3) \ ++ : c==3 ? nc==4 ? s(x,0) : s(x,4) \ ++ : c==4 ? s(x,5) \ ++ : c==5 ? nc==8 ? s(x,6) : s(x,0) \ ++ : c==6 ? s(x,7) \ ++ : s(x,0)) \ ++ : r==2 ? \ ++ ( c==0 ? nc==8 ? s(x,3) : s(x,2) \ ++ : c==1 ? nc==8 ? s(x,4) : s(x,3) \ ++ : c==2 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ ++ : c==3 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ ++ : c==4 ? nc==8 ? s(x,7) : s(x,0) \ ++ : c==5 ? nc==8 ? s(x,0) : s(x,1) \ ++ : c==6 ? s(x,1) \ ++ : s(x,2)) \ ++ : \ ++ ( c==0 ? nc==8 ? s(x,4) : s(x,3) \ ++ : c==1 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ ++ : c==2 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ ++ : c==3 ? nc==4 ? s(x,2) : nc==8 ? s(x,7) : s(x,0) \ ++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \ ++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \ ++ : c==6 ? s(x,2) \ ++ : s(x,3))) ++ ++#define inv_var(x,r,c) \ ++ ( r==0 ? \ ++ ( c==0 ? s(x,0) \ ++ : c==1 ? s(x,1) \ ++ : c==2 ? s(x,2) \ ++ : c==3 ? s(x,3) \ ++ : c==4 ? s(x,4) \ ++ : c==5 ? s(x,5) \ ++ : c==6 ? s(x,6) \ ++ : s(x,7)) \ ++ : r==1 ? \ ++ ( c==0 ? nc==4 ? s(x,3) : nc==8 ? s(x,7) : s(x,5) \ ++ : c==1 ? s(x,0) \ ++ : c==2 ? s(x,1) \ ++ : c==3 ? s(x,2) \ ++ : c==4 ? s(x,3) \ ++ : c==5 ? s(x,4) \ ++ : c==6 ? s(x,5) \ ++ : s(x,6)) \ ++ : r==2 ? \ ++ ( c==0 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ ++ : c==1 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ ++ : c==2 ? nc==8 ? s(x,7) : s(x,0) \ ++ : c==3 ? nc==8 ? s(x,0) : s(x,1) \ ++ : c==4 ? nc==8 ? s(x,1) : s(x,2) \ ++ : c==5 ? nc==8 ? s(x,2) : s(x,3) \ ++ : c==6 ? s(x,3) \ ++ : s(x,4)) \ ++ : \ ++ ( c==0 ? nc==4 ? s(x,1) : nc==8 ? s(x,4) : s(x,3) \ ++ : c==1 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ ++ : c==2 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ ++ : c==3 ? nc==8 ? s(x,7) : s(x,0) \ ++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \ ++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \ ++ : c==6 ? s(x,2) \ ++ : s(x,3))) ++ ++#define si(y,x,k,c) s(y,c) = const_word_in(x + 4 * c) ^ k[c] ++#define so(y,x,c) word_out(y + 4 * c, s(x,c)) ++ ++#if defined(FOUR_TABLES) ++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,ft_tab,fwd_var,rf1,c) ++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,it_tab,inv_var,rf1,c) ++#elif defined(ONE_TABLE) ++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,ft_tab,fwd_var,rf1,c) ++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,it_tab,inv_var,rf1,c) ++#else ++#define fwd_rnd(y,x,k,c) s(y,c) = fwd_mcol(no_table(x,s_box,fwd_var,rf1,c)) ^ (k)[c] ++#define inv_rnd(y,x,k,c) s(y,c) = inv_mcol(no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c]) ++#endif ++ ++#if defined(FOUR_LR_TABLES) ++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,fl_tab,fwd_var,rf1,c) ++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,il_tab,inv_var,rf1,c) ++#elif defined(ONE_LR_TABLE) ++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,fl_tab,fwd_var,rf1,c) ++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,il_tab,inv_var,rf1,c) ++#else ++#define fwd_lrnd(y,x,k,c) s(y,c) = no_table(x,s_box,fwd_var,rf1,c) ^ (k)[c] ++#define inv_lrnd(y,x,k,c) s(y,c) = no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c] ++#endif ++ ++#if AES_BLOCK_SIZE == 16 ++ ++#if defined(ARRAYS) ++#define locals(y,x) x[4],y[4] ++#else ++#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3 ++// the following defines prevent the compiler requiring the declaration ++// of generated but unused variables in the fwd_var and inv_var macros ++#define b04 unused ++#define b05 unused ++#define b06 unused ++#define b07 unused ++#define b14 unused ++#define b15 unused ++#define b16 unused ++#define b17 unused ++#endif ++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ ++ s(y,2) = s(x,2); s(y,3) = s(x,3); ++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3) ++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) ++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) ++ ++#elif AES_BLOCK_SIZE == 24 ++ ++#if defined(ARRAYS) ++#define locals(y,x) x[6],y[6] ++#else ++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5, \ ++ y##0,y##1,y##2,y##3,y##4,y##5 ++#define b06 unused ++#define b07 unused ++#define b16 unused ++#define b17 unused ++#endif ++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ ++ s(y,2) = s(x,2); s(y,3) = s(x,3); \ ++ s(y,4) = s(x,4); s(y,5) = s(x,5); ++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); \ ++ si(y,x,k,3); si(y,x,k,4); si(y,x,k,5) ++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); \ ++ so(y,x,3); so(y,x,4); so(y,x,5) ++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); \ ++ rm(y,x,k,3); rm(y,x,k,4); rm(y,x,k,5) ++#else ++ ++#if defined(ARRAYS) ++#define locals(y,x) x[8],y[8] ++#else ++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5,x##6,x##7, \ ++ y##0,y##1,y##2,y##3,y##4,y##5,y##6,y##7 ++#endif ++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ ++ s(y,2) = s(x,2); s(y,3) = s(x,3); \ ++ s(y,4) = s(x,4); s(y,5) = s(x,5); \ ++ s(y,6) = s(x,6); s(y,7) = s(x,7); ++ ++#if AES_BLOCK_SIZE == 32 ++ ++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3); \ ++ si(y,x,k,4); si(y,x,k,5); si(y,x,k,6); si(y,x,k,7) ++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3); \ ++ so(y,x,4); so(y,x,5); so(y,x,6); so(y,x,7) ++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3); \ ++ rm(y,x,k,4); rm(y,x,k,5); rm(y,x,k,6); rm(y,x,k,7) ++#else ++ ++#define state_in(y,x,k) \ ++switch(nc) \ ++{ case 8: si(y,x,k,7); si(y,x,k,6); \ ++ case 6: si(y,x,k,5); si(y,x,k,4); \ ++ case 4: si(y,x,k,3); si(y,x,k,2); \ ++ si(y,x,k,1); si(y,x,k,0); \ ++} ++ ++#define state_out(y,x) \ ++switch(nc) \ ++{ case 8: so(y,x,7); so(y,x,6); \ ++ case 6: so(y,x,5); so(y,x,4); \ ++ case 4: so(y,x,3); so(y,x,2); \ ++ so(y,x,1); so(y,x,0); \ ++} ++ ++#if defined(FAST_VARIABLE) ++ ++#define round(rm,y,x,k) \ ++switch(nc) \ ++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ ++ rm(y,x,k,5); rm(y,x,k,4); \ ++ rm(y,x,k,3); rm(y,x,k,2); \ ++ rm(y,x,k,1); rm(y,x,k,0); \ ++ break; \ ++ case 6: rm(y,x,k,5); rm(y,x,k,4); \ ++ rm(y,x,k,3); rm(y,x,k,2); \ ++ rm(y,x,k,1); rm(y,x,k,0); \ ++ break; \ ++ case 4: rm(y,x,k,3); rm(y,x,k,2); \ ++ rm(y,x,k,1); rm(y,x,k,0); \ ++ break; \ ++} ++#else ++ ++#define round(rm,y,x,k) \ ++switch(nc) \ ++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ ++ case 6: rm(y,x,k,5); rm(y,x,k,4); \ ++ case 4: rm(y,x,k,3); rm(y,x,k,2); \ ++ rm(y,x,k,1); rm(y,x,k,0); \ ++} ++ ++#endif ++ ++#endif ++#endif ++ ++void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) ++{ u_int32_t locals(b0, b1); ++ const u_int32_t *kp = cx->aes_e_key; ++ ++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) ++ u_int32_t f2; ++#endif ++ ++ state_in(b0, in_blk, kp); kp += nc; ++ ++#if defined(UNROLL) ++ ++ switch(cx->aes_Nrnd) ++ { ++ case 14: round(fwd_rnd, b1, b0, kp ); ++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; ++ case 12: round(fwd_rnd, b1, b0, kp ); ++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; ++ case 10: round(fwd_rnd, b1, b0, kp ); ++ round(fwd_rnd, b0, b1, kp + nc); ++ round(fwd_rnd, b1, b0, kp + 2 * nc); ++ round(fwd_rnd, b0, b1, kp + 3 * nc); ++ round(fwd_rnd, b1, b0, kp + 4 * nc); ++ round(fwd_rnd, b0, b1, kp + 5 * nc); ++ round(fwd_rnd, b1, b0, kp + 6 * nc); ++ round(fwd_rnd, b0, b1, kp + 7 * nc); ++ round(fwd_rnd, b1, b0, kp + 8 * nc); ++ round(fwd_lrnd, b0, b1, kp + 9 * nc); ++ } ++ ++#elif defined(PARTIAL_UNROLL) ++ { u_int32_t rnd; ++ ++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd) ++ { ++ round(fwd_rnd, b1, b0, kp); ++ round(fwd_rnd, b0, b1, kp + nc); kp += 2 * nc; ++ } ++ ++ round(fwd_rnd, b1, b0, kp); ++ round(fwd_lrnd, b0, b1, kp + nc); ++ } ++#else ++ { u_int32_t rnd; ++ ++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd) ++ { ++ round(fwd_rnd, b1, b0, kp); ++ l_copy(b0, b1); kp += nc; ++ } ++ ++ round(fwd_lrnd, b0, b1, kp); ++ } ++#endif ++ ++ state_out(out_blk, b0); ++} ++ ++void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) ++{ u_int32_t locals(b0, b1); ++ const u_int32_t *kp = cx->aes_d_key; ++ ++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) ++ u_int32_t f2, f4, f8, f9; ++#endif ++ ++ state_in(b0, in_blk, kp); kp += nc; ++ ++#if defined(UNROLL) ++ ++ switch(cx->aes_Nrnd) ++ { ++ case 14: round(inv_rnd, b1, b0, kp ); ++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; ++ case 12: round(inv_rnd, b1, b0, kp ); ++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; ++ case 10: round(inv_rnd, b1, b0, kp ); ++ round(inv_rnd, b0, b1, kp + nc); ++ round(inv_rnd, b1, b0, kp + 2 * nc); ++ round(inv_rnd, b0, b1, kp + 3 * nc); ++ round(inv_rnd, b1, b0, kp + 4 * nc); ++ round(inv_rnd, b0, b1, kp + 5 * nc); ++ round(inv_rnd, b1, b0, kp + 6 * nc); ++ round(inv_rnd, b0, b1, kp + 7 * nc); ++ round(inv_rnd, b1, b0, kp + 8 * nc); ++ round(inv_lrnd, b0, b1, kp + 9 * nc); ++ } ++ ++#elif defined(PARTIAL_UNROLL) ++ { u_int32_t rnd; ++ ++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd) ++ { ++ round(inv_rnd, b1, b0, kp); ++ round(inv_rnd, b0, b1, kp + nc); kp += 2 * nc; ++ } ++ ++ round(inv_rnd, b1, b0, kp); ++ round(inv_lrnd, b0, b1, kp + nc); ++ } ++#else ++ { u_int32_t rnd; ++ ++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd) ++ { ++ round(inv_rnd, b1, b0, kp); ++ l_copy(b0, b1); kp += nc; ++ } ++ ++ round(inv_lrnd, b0, b1, kp); ++ } ++#endif ++ ++ state_out(out_blk, b0); ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/aes_cbc.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,43 @@ ++/* ++// I retain copyright in this code but I encourage its free use provided ++// that I don't carry any responsibility for the results. I am especially ++// happy to see it used in free and open source software. If you do use ++// it I would appreciate an acknowledgement of its origin in the code or ++// the product that results and I would also appreciate knowing a little ++// about the use to which it is being put. I am grateful to Frank Yellin ++// for some ideas that are used in this implementation. ++// ++// Dr B. R. Gladman 6th April 2001. ++// ++// This is an implementation of the AES encryption algorithm (Rijndael) ++// designed by Joan Daemen and Vincent Rijmen. This version is designed ++// to provide both fixed and dynamic block and key lengths and can also ++// run with either big or little endian internal byte order (see aes.h). ++// It inputs block and key lengths in bytes with the legal values being ++// 16, 24 and 32. ++* ++*/ ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif ++#include "crypto/aes_cbc.h" ++#include "crypto/cbc_generic.h" ++ ++/* returns bool success */ ++int AES_set_key(aes_context *aes_ctx, const u_int8_t *key, int keysize) { ++ aes_set_key(aes_ctx, key, keysize, 0); ++ return 1; ++} ++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt); ++ ++ ++/* ++ * $Log: aes_cbc.c,v $ ++ * Revision 1.1 2004/04/06 02:48:12 mcr ++ * pullup of AES cipher from alg-branch. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/aes_xcbc_mac.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,67 @@ ++#ifdef __KERNEL__ ++#include ++#include ++#define DEBUG(x) ++#else ++#include ++#include ++#define DEBUG(x) x ++#endif ++ ++#include "crypto/aes.h" ++#include "crypto/aes_xcbc_mac.h" ++ ++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen) ++{ ++ int ret=1; ++ aes_block kn[3] = { ++ { 0x01010101, 0x01010101, 0x01010101, 0x01010101 }, ++ { 0x02020202, 0x02020202, 0x02020202, 0x02020202 }, ++ { 0x03030303, 0x03030303, 0x03030303, 0x03030303 }, ++ }; ++ aes_set_key(&ctxm->ctx_k1, key, keylen, 0); ++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[0], (u_int8_t *) kn[0]); ++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[1], (u_int8_t *) ctxm->k2); ++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[2], (u_int8_t *) ctxm->k3); ++ aes_set_key(&ctxm->ctx_k1, (u_int8_t *) kn[0], 16, 0); ++ return ret; ++} ++static void do_pad_xor(u_int8_t *out, const u_int8_t *in, int len) { ++ int pos=0; ++ for (pos=1; pos <= 16; pos++, in++, out++) { ++ if (pos <= len) ++ *out ^= *in; ++ if (pos > len) { ++ DEBUG(printf("put 0x80 at pos=%d\n", pos)); ++ *out ^= 0x80; ++ break; ++ } ++ } ++} ++static void xor_block(aes_block res, const aes_block op) { ++ res[0] ^= op[0]; ++ res[1] ^= op[1]; ++ res[2] ^= op[2]; ++ res[3] ^= op[3]; ++} ++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]) { ++ int ret=ilen; ++ u_int32_t out[4] = { 0, 0, 0, 0 }; ++ for (; ilen > 16 ; ilen-=16) { ++ xor_block(out, (const u_int32_t*) &in[0]); ++ aes_encrypt(&ctxm->ctx_k1, in, (u_int8_t *)&out[0]); ++ in+=16; ++ } ++ do_pad_xor((u_int8_t *)&out, in, ilen); ++ if (ilen==16) { ++ DEBUG(printf("using k3\n")); ++ xor_block(out, ctxm->k3); ++ } ++ else ++ { ++ DEBUG(printf("using k2\n")); ++ xor_block(out, ctxm->k2); ++ } ++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *)out, hash); ++ return ret; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/aes/test_main.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,41 @@ ++#include ++#include ++#include ++#include "aes_cbc.h" ++#define AES_BLOCK_SIZE 16 ++#define KEY_SIZE 128 /* bits */ ++#define KEY "1234567890123456" ++#define STR "hola guaso como estaisss ... 012" ++#define STRSZ (sizeof(STR)-1) ++ ++#define EMT_AESCBC_BLKLEN AES_BLOCK_SIZE ++#define AES_CONTEXT_T aes_context ++#define EMT_ESPAES_KEY_SZ 16 ++int pretty_print(const unsigned char *buf, int count) { ++ int i=0; ++ for (;i ++#include ++#include ++#include "aes.h" ++#include "aes_xcbc_mac.h" ++#define STR "Hola guasssso c|mo estais ...012" ++void print_hash(const __u8 *hash) { ++ printf("%08x %08x %08x %08x\n", ++ *(__u32*)(&hash[0]), ++ *(__u32*)(&hash[4]), ++ *(__u32*)(&hash[8]), ++ *(__u32*)(&hash[12])); ++} ++int main(int argc, char *argv[]) { ++ aes_block key= { 0xdeadbeef, 0xceedcaca, 0xcafebabe, 0xff010204 }; ++ __u8 hash[16]; ++ char *str = argv[1]; ++ aes_context_mac ctx; ++ if (str==NULL) { ++ fprintf(stderr, "pasame el str\n"); ++ return 255; ++ } ++ AES_xcbc_mac_set_key(&ctx, (__u8 *)&key, sizeof(key)); ++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash); ++ print_hash(hash); ++ str[2]='x'; ++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash); ++ print_hash(hash); ++ return 0; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/COPYRIGHT Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,50 @@ ++Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++All rights reserved. ++ ++This package is an DES implementation written by Eric Young (eay@cryptsoft.com). ++The implementation was written so as to conform with MIT's libdes. ++ ++This library is free for commercial and non-commercial use as long as ++the following conditions are aheared to. The following conditions ++apply to all code found in this distribution. ++ ++Copyright remains Eric Young's, and as such any Copyright notices in ++the code are not to be removed. ++If this package is used in a product, Eric Young should be given attribution ++as the author of that the SSL library. This can be in the form of a textual ++message at program startup or in documentation (online or textual) provided ++with the package. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++1. Redistributions of source code must retain the copyright ++ notice, this list of conditions and the following disclaimer. ++2. 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. ++3. All advertising materials mentioning features or use of this software ++ must display the following acknowledgement: ++ This product includes software developed by Eric Young (eay@cryptsoft.com) ++ ++THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++SUCH DAMAGE. ++ ++The license and distribution terms for any publically available version or ++derivative of this code cannot be changed. i.e. this code cannot simply be ++copied and put under another distrubution license ++[including the GNU Public License.] ++ ++The reason behind this being stated in this direct manner is past ++experience in code simply being copied and the attribution removed ++from it and then being distributed as part of other packages. This ++implementation was a non-trivial and unpaid effort. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/INSTALL Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,69 @@ ++Check the CC and CFLAGS lines in the makefile ++ ++If your C library does not support the times(3) function, change the ++#define TIMES to ++#undef TIMES in speed.c ++If it does, check the HZ value for the times(3) function. ++If your system does not define CLK_TCK it will be assumed to ++be 100.0. ++ ++If possible use gcc v 2.7.? ++Turn on the maximum optimising (normally '-O3 -fomit-frame-pointer' for gcc) ++In recent times, some system compilers give better performace. ++ ++type 'make' ++ ++run './destest' to check things are ok. ++run './rpw' to check the tty code for reading passwords works. ++run './speed' to see how fast those optimisations make the library run :-) ++run './des_opts' to determin the best compile time options. ++ ++The output from des_opts should be put in the makefile options and des_enc.c ++should be rebuilt. For 64 bit computers, do not use the DES_PTR option. ++For the DEC Alpha, edit des.h and change DES_LONG to 'unsigned int' ++and then you can use the 'DES_PTR' option. ++ ++The file options.txt has the options listed for best speed on quite a ++few systems. Look and the options (UNROLL, PTR, RISC2 etc) and then ++turn on the relevent option in the Makefile ++ ++There are some special Makefile targets that make life easier. ++make cc - standard cc build ++make gcc - standard gcc build ++make x86-elf - x86 assembler (elf), linux-elf. ++make x86-out - x86 assembler (a.out), FreeBSD ++make x86-solaris- x86 assembler ++make x86-bsdi - x86 assembler (a.out with primative assembler). ++ ++If at all possible use the assembler (for Windows NT/95, use ++asm/win32.obj to link with). The x86 assembler is very very fast. ++ ++A make install will by default install ++libdes.a in /usr/local/lib/libdes.a ++des in /usr/local/bin/des ++des_crypt.man in /usr/local/man/man3/des_crypt.3 ++des.man in /usr/local/man/man1/des.1 ++des.h in /usr/include/des.h ++ ++des(1) should be compatible with sunOS's but I have been unable to ++test it. ++ ++These routines should compile on MSDOS, most 32bit and 64bit version ++of Unix (BSD and SYSV) and VMS, without modification. ++The only problems should be #include files that are in the wrong places. ++ ++These routines can be compiled under MSDOS. ++I have successfully encrypted files using des(1) under MSDOS and then ++decrypted the files on a SparcStation. ++I have been able to compile and test the routines with ++Microsoft C v 5.1 and Turbo C v 2.0. ++The code in this library is in no way optimised for the 16bit ++operation of MSDOS. ++ ++When building for glibc, ignore all of the above and just unpack into ++glibc-1.??/des and then gmake as per normal. ++ ++As a final note on performace. Certain CPUs like sparcs and Alpha often give ++a %10 speed difference depending on the link order. It is rather anoying ++when one program reports 'x' DES encrypts a second and another reports ++'x*0.9' the speed. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/Makefile.objs Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,20 @@ ++obj-$(CONFIG_IPSEC_ENC_3DES) += cbc_enc.o ++#obj-$(CONFIG_IPSEC_ENC_3DES) += des_opts.o ++obj-$(CONFIG_IPSEC_ENC_3DES) += ecb_enc.o ++#obj-$(CONFIG_IPSEC_ENC_3DES) += fcrypt.o ++obj-$(CONFIG_IPSEC_ENC_3DES) += set_key.o ++ ++ifeq ($(strip ${SUBARCH}),) ++SUBARCH:=${ARCH} ++endif ++ ++ifeq (${SUBARCH},i386) ++obj-$(CONFIG_IPSEC_ENC_3DES) += dx86unix.o ++else ++obj-$(CONFIG_IPSEC_ENC_3DES) += des_enc.o ++endif ++ ++ ++ ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/README Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,54 @@ ++ ++ libdes, Version 4.01 10-Jan-97 ++ ++ Copyright (c) 1997, Eric Young ++ All rights reserved. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms specified in COPYRIGHT. ++ ++-- ++The primary ftp site for this library is ++ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz ++libdes is now also shipped with SSLeay. Primary ftp site of ++ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/SSLeay-x.x.x.tar.gz ++ ++The best way to build this library is to build it as part of SSLeay. ++ ++This kit builds a DES encryption library and a DES encryption program. ++It supports ecb, cbc, ofb, cfb, triple ecb, triple cbc, triple ofb, ++triple cfb, desx, and MIT's pcbc encryption modes and also has a fast ++implementation of crypt(3). ++It contains support routines to read keys from a terminal, ++generate a random key, generate a key from an arbitrary length string, ++read/write encrypted data from/to a file descriptor. ++ ++The implementation was written so as to conform with the manual entry ++for the des_crypt(3) library routines from MIT's project Athena. ++ ++destest should be run after compilation to test the des routines. ++rpw should be run after compilation to test the read password routines. ++The des program is a replacement for the sun des command. I believe it ++conforms to the sun version. ++ ++The Imakefile is setup for use in the kerberos distribution. ++ ++These routines are best compiled with gcc or any other good ++optimising compiler. ++Just turn you optimiser up to the highest settings and run destest ++after the build to make sure everything works. ++ ++I believe these routines are close to the fastest and most portable DES ++routines that use small lookup tables (4.5k) that are publicly available. ++The fcrypt routine is faster than ufc's fcrypt (when compiling with ++gcc2 -O2) on the sparc 2 (1410 vs 1270) but is not so good on other machines ++(on a sun3/260 168 vs 336). It is a function of CPU on chip cache size. ++[ 10-Jan-97 and a function of an incorrect speed testing program in ++ ufc which gave much better test figures that reality ]. ++ ++It is worth noting that on sparc and Alpha CPUs, performance of the DES ++library can vary by upto %10 due to the positioning of files after application ++linkage. ++ ++Eric Young (eay@cryptsoft.com) ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/README.freeswan Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,33 @@ ++The only changes the FreeS/WAN project has made to libdes-lite 4.04b are: ++ ++We #ifdef-ed the declaration of DES_LONG in des.h, so it's more efficient ++on the Alpha, instead of just noting the issue in a comment. ++ ++We #ifdef-ed out the des_options() function in ecb_enc.c, because we don't ++use it, and its call to sprintf() can cause subtle difficulties when KLIPS ++is built as a module (depending on details of Linux configuration options). ++ ++We changed some instances of CC=$(CC) in the Makefile to CC='$(CC)' to make ++it cope better with Linux kernel Makefile stupidities, and took out an ++explicit CC=gcc (unwise on systems with strange compilers). ++ ++We deleted some references to and , and a declaration ++of one function found only in the full libdes (not in libdes-lite), to ++avoid dragging in bits of stdio/stdlib unnecessarily. (Our thanks to Hans ++Schultz for spotting this and pointing out the fixes.) ++ ++We deleted a couple of .obj files in the asm subdirectory, which appear to ++have been included in the original library by accident. ++ ++We have added an include of our Makefile.inc file, to permit overriding ++things like choice of compiler (although the libdes Makefile would ++probably need some work to make this effective). ++ ++ ++ ++Note that Eric Young is no longer at the email address listed in these ++files, and is (alas) no longer working on free crypto software. ++ ++ ++ ++This file is RCSID $Id: README.freeswan,v 1.11 2002/04/24 07:36:37 mcr Exp $ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/VERSION Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,406 @@ ++Version 4.04 ++ Fixed a few tests in destest. Also added x86 assember for ++ des_ncbc_encrypt() which is the standard cbc mode function. ++ This makes a very very large performace difference. ++ Ariel Glenn ariel@columbia.edu reports that the terminal ++ 'turn echo off' can return (errno == EINVAL) under solaris ++ when redirection is used. So I now catch that as well as ENOTTY. ++ ++ ++Version 4.03 ++ Left a static out of enc_write.c, which caused to buffer to be ++ continiously malloc()ed. Does anyone use these functions? I keep ++ on feeling like removing them since I only had these in there ++ for a version of kerberised login. Anyway, this was pointed out ++ by Theo de Raadt ++ The 'n' bit ofb code was wrong, it was not shifting the shift ++ register. It worked correctly for n == 64. Thanks to ++ Gigi Ankeny for pointing this one out. ++ ++Version 4.02 ++ I was doing 'if (memcmp(weak_keys[i],key,sizeof(key)) == 0)' ++ when checking for weak keys which is wrong :-(, pointed out by ++ Markus F.X.J. Oberhumer . ++ ++Version 4.01 ++ Even faster inner loop in the DES assembler for x86 and a modification ++ for IP/FP which is faster on x86. Both of these changes are ++ from Svend Olaf Mikkelsen . His ++ changes make the assembler run %40 faster on a pentium. This is just ++ a case of getting the instruction sequence 'just right'. ++ All credit to 'Svend' :-) ++ Quite a few special x86 'make' targets. ++ A libdes-l (lite) distribution. ++ ++Version 4.00 ++ After a bit of a pause, I'll up the major version number since this ++ is mostly a performace release. I've added x86 assembler and ++ added more options for performance. A %28 speedup for gcc ++ on a pentium and the assembler is a %50 speedup. ++ MIPS CPU's, sparc and Alpha are the main CPU's with speedups. ++ Run des_opts to work out which options should be used. ++ DES_RISC1/DES_RISC2 use alternative inner loops which use ++ more registers but should give speedups on any CPU that does ++ dual issue (pentium). DES_UNROLL unrolls the inner loop, ++ which costs in code size. ++ ++Version 3.26 ++ I've finally removed one of the shifts in D_ENCRYPT. This ++ meant I've changed the des_SPtrans table (spr.h), the set_key() ++ function and some things in des_enc.c. This has definitly ++ made things faster :-). I've known about this one for some ++ time but I've been too lazy to follow it up :-). ++ Noticed that in the D_ENCRYPT() macro, we can just do L^=(..)^(..)^.. ++ instead of L^=((..)|(..)|(..).. This should save a register at ++ least. ++ Assember for x86. The file to replace is des_enc.c, which is replaced ++ by one of the assembler files found in asm. Look at des/asm/readme ++ for more info. ++ ++ /* Modification to fcrypt so it can be compiled to support ++ HPUX 10.x's long password format, define -DLONGCRYPT to use this. ++ Thanks to Jens Kupferschmidt . */ ++ ++ SIGWINCH case put in des_read_passwd() so the function does not ++ 'exit' if this function is recieved. ++ ++Version 3.25 17/07/96 ++ Modified read_pwd.c so that stdin can be read if not a tty. ++ Thanks to Jeff Barber for the patches. ++ des_init_random_number_generator() shortened due to VMS linker ++ limits. ++ Added RSA's DESX cbc mode. It is a form of cbc encryption, with 2 ++ 8 byte quantites xored before and after encryption. ++ des_xcbc_encryption() - the name is funny to preserve the des_ ++ prefix on all functions. ++ ++Version 3.24 20/04/96 ++ The DES_PTR macro option checked and used by SSLeay configuration ++ ++Version 3.23 11/04/96 ++ Added DES_LONG. If defined to 'unsigned int' on the DEC Alpha, ++ it gives a %20 speedup :-) ++ Fixed the problem with des.pl under perl5. The patches were ++ sent by Ed Kubaitis (ejk@uiuc.edu). ++ if fcrypt.c, changed values to handle illegal salt values the way ++ normal crypt() implementations do. Some programs apparently use ++ them :-(. The patch was sent by Bjorn Gronvall ++ ++Version 3.22 29/11/95 ++ Bug in des(1), an error with the uuencoding stuff when the ++ 'data' is small, thanks to Geoff Keating ++ for the patch. ++ ++Version 3.21 22/11/95 ++ After some emailing back and forth with ++ Colin Plumb , I've tweaked a few things ++ and in a future version I will probably put in some of the ++ optimisation he suggested for use with the DES_USE_PTR option. ++ Extra routines from Mark Murray for use in ++ freeBSD. They mostly involve random number generation for use ++ with kerberos. They involve evil machine specific system calls ++ etc so I would normally suggest pushing this stuff into the ++ application and/or using RAND_seed()/RAND_bytes() if you are ++ using this DES library as part of SSLeay. ++ Redone the read_pw() function so that it is cleaner and ++ supports termios, thanks to Sameer Parekh ++ for the initial patches for this. ++ Renamed 3ecb_encrypt() to ecb3_encrypt(). This has been ++ done just to make things more consistent. ++ I have also now added triple DES versions of cfb and ofb. ++ ++Version 3.20 ++ Damn, Damn, Damn, as pointed out by Mike_Spreitzer.PARC@xerox.com, ++ my des_random_seed() function was only copying 4 bytes of the ++ passed seed into the init structure. It is now fixed to copy 8. ++ My own suggestion is to used something like MD5 :-) ++ ++Version 3.19 ++ While looking at my code one day, I though, why do I keep on ++ calling des_encrypt(in,out,ks,enc) when every function that ++ calls it has in and out the same. So I dropped the 'out' ++ parameter, people should not be using this function. ++ ++Version 3.18 30/08/95 ++ Fixed a few bit with the distribution and the filenames. ++ 3.17 had been munged via a move to DOS and back again. ++ NO CODE CHANGES ++ ++Version 3.17 14/07/95 ++ Fixed ede3 cbc which I had broken in 3.16. I have also ++ removed some unneeded variables in 7-8 of the routines. ++ ++Version 3.16 26/06/95 ++ Added des_encrypt2() which does not use IP/FP, used by triple ++ des routines. Tweaked things a bit elsewhere. %13 speedup on ++ sparc and %6 on a R4400 for ede3 cbc mode. ++ ++Version 3.15 06/06/95 ++ Added des_ncbc_encrypt(), it is des_cbc mode except that it is ++ 'normal' and copies the new iv value back over the top of the ++ passed parameter. ++ CHANGED des_ede3_cbc_encrypt() so that it too now overwrites ++ the iv. THIS WILL BREAK EXISTING CODE, but since this function ++ only new, I feel I can change it, not so with des_cbc_encrypt :-(. ++ I need to update the documentation. ++ ++Version 3.14 31/05/95 ++ New release upon the world, as part of my SSL implementation. ++ New copyright and usage stuff. Basically free for all to use ++ as long as you say it came from me :-) ++ ++Version 3.13 31/05/95 ++ A fix in speed.c, if HZ is not defined, I set it to 100.0 ++ which is reasonable for most unixes except SunOS 4.x. ++ I now have a #ifdef sun but timing for SunOS 4.x looked very ++ good :-(. At my last job where I used SunOS 4.x, it was ++ defined to be 60.0 (look at the old INSTALL documentation), at ++ the last release had it changed to 100.0 since I now work with ++ Solaris2 and SVR4 boxes. ++ Thanks to Rory Chisholm for pointing this ++ one out. ++ ++Version 3.12 08/05/95 ++ As pointed out by The Crypt Keeper , ++ my D_ENCRYPT macro in crypt() had an un-necessary variable. ++ It has been removed. ++ ++Version 3.11 03/05/95 ++ Added des_ede3_cbc_encrypt() which is cbc mode des with 3 keys ++ and one iv. It is a standard and I needed it for my SSL code. ++ It makes more sense to use this for triple DES than ++ 3cbc_encrypt(). I have also added (or should I say tested :-) ++ cfb64_encrypt() which is cfb64 but it will encrypt a partial ++ number of bytes - 3 bytes in 3 bytes out. Again this is for ++ my SSL library, as a form of encryption to use with SSL ++ telnet. ++ ++Version 3.10 22/03/95 ++ Fixed a bug in 3cbc_encrypt() :-(. When making repeated calls ++ to cbc3_encrypt, the 2 iv values that were being returned to ++ be used in the next call were reversed :-(. ++ Many thanks to Bill Wade for pointing out ++ this error. ++ ++Version 3.09 01/02/95 ++ Fixed des_random_key to far more random, it was rather feeble ++ with regards to picking the initial seed. The problem was ++ pointed out by Olaf Kirch . ++ ++Version 3.08 14/12/94 ++ Added Makefile.PL so libdes can be built into perl5. ++ Changed des_locl.h so RAND is always defined. ++ ++Version 3.07 05/12/94 ++ Added GNUmake and stuff so the library can be build with ++ glibc. ++ ++Version 3.06 30/08/94 ++ Added rpc_enc.c which contains _des_crypt. This is for use in ++ secure_rpc v 4.0 ++ Finally fixed the cfb_enc problems. ++ Fixed a few parameter parsing bugs in des (-3 and -b), thanks ++ to Rob McMillan ++ ++Version 3.05 21/04/94 ++ for unsigned long l; gcc does not produce ((l>>34) == 0) ++ This causes bugs in cfb_enc. ++ Thanks to Hadmut Danisch ++ ++Version 3.04 20/04/94 ++ Added a version number to des.c and libdes.a ++ ++Version 3.03 12/01/94 ++ Fixed a bug in non zero iv in 3cbc_enc. ++ ++Version 3.02 29/10/93 ++ I now work in a place where there are 6+ architectures and 14+ ++ OS versions :-). ++ Fixed TERMIO definition so the most sys V boxes will work :-) ++ ++Release upon comp.sources.misc ++Version 3.01 08/10/93 ++ Added des_3cbc_encrypt() ++ ++Version 3.00 07/10/93 ++ Fixed up documentation. ++ quad_cksum definitely compatible with MIT's now. ++ ++Version 2.30 24/08/93 ++ Triple DES now defaults to triple cbc but can do triple ecb ++ with the -b flag. ++ Fixed some MSDOS uuen/uudecoding problems, thanks to ++ Added prototypes. ++ ++Version 2.22 29/06/93 ++ Fixed a bug in des_is_weak_key() which stopped it working :-( ++ thanks to engineering@MorningStar.Com. ++ ++Version 2.21 03/06/93 ++ des(1) with no arguments gives quite a bit of help. ++ Added -c (generate ckecksum) flag to des(1). ++ Added -3 (triple DES) flag to des(1). ++ Added cfb and ofb routines to the library. ++ ++Version 2.20 11/03/93 ++ Added -u (uuencode) flag to des(1). ++ I have been playing with byte order in quad_cksum to make it ++ compatible with MIT's version. All I can say is avid this ++ function if possible since MIT's output is endian dependent. ++ ++Version 2.12 14/10/92 ++ Added MSDOS specific macro in ecb_encrypt which gives a %70 ++ speed up when the code is compiled with turbo C. ++ ++Version 2.11 12/10/92 ++ Speedup in set_key (recoding of PC-1) ++ I now do it in 47 simple operations, down from 60. ++ Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) ++ for motivating me to look for a faster system :-) ++ The speedup is probably less that 1% but it is still 13 ++ instructions less :-). ++ ++Version 2.10 06/10/92 ++ The code now works on the 64bit ETA10 and CRAY without modifications or ++ #defines. I believe the code should work on any machine that ++ defines long, int or short to be 8 bytes long. ++ Thanks to Shabbir J. Safdar (shabby@mentor.cc.purdue.edu) ++ for helping me fix the code to run on 64bit machines (he had ++ access to an ETA10). ++ Thanks also to John Fletcher ++ for testing the routines on a CRAY. ++ read_password.c has been renamed to read_passwd.c ++ string_to_key.c has been renamed to string2key.c ++ ++Version 2.00 14/09/92 ++ Made mods so that the library should work on 64bit CPU's. ++ Removed all my uchar and ulong defs. To many different ++ versions of unix define them in their header files in too many ++ different combinations :-) ++ IRIX - Sillicon Graphics mods (mostly in read_password.c). ++ Thanks to Andrew Daviel (advax@erich.triumf.ca) ++ ++Version 1.99 26/08/92 ++ Fixed a bug or 2 in enc_read.c ++ Fixed a bug in enc_write.c ++ Fixed a pseudo bug in fcrypt.c (very obscure). ++ ++Version 1.98 31/07/92 ++ Support for the ETA10. This is a strange machine that defines ++ longs and ints as 8 bytes and shorts as 4 bytes. ++ Since I do evil things with long * that assume that they are 4 ++ bytes. Look in the Makefile for the option to compile for ++ this machine. quad_cksum appears to have problems but I ++ will don't have the time to fix it right now, and this is not ++ a function that uses DES and so will not effect the main uses ++ of the library. ++ ++Version 1.97 20/05/92 eay ++ Fixed the Imakefile and made some changes to des.h to fix some ++ problems when building this package with Kerberos v 4. ++ ++Version 1.96 18/05/92 eay ++ Fixed a small bug in string_to_key() where problems could ++ occur if des_check_key was set to true and the string ++ generated a weak key. ++ ++Patch2 posted to comp.sources.misc ++Version 1.95 13/05/92 eay ++ Added an alternative version of the D_ENCRYPT macro in ++ ecb_encrypt and fcrypt. Depending on the compiler, one version or the ++ other will be faster. This was inspired by ++ Dana How , and her pointers about doing the ++ *(ulong *)((uchar *)ptr+(value&0xfc)) ++ vs ++ ptr[value&0x3f] ++ to stop the C compiler doing a <<2 to convert the long array index. ++ ++Version 1.94 05/05/92 eay ++ Fixed an incompatibility between my string_to_key and the MIT ++ version. When the key is longer than 8 chars, I was wrapping ++ with a different method. To use the old version, define ++ OLD_STR_TO_KEY in the makefile. Thanks to ++ viktor@newsu.shearson.com (Viktor Dukhovni). ++ ++Version 1.93 28/04/92 eay ++ Fixed the VMS mods so that echo is now turned off in ++ read_password. Thanks again to brennan@coco.cchs.su.oz.AU. ++ MSDOS support added. The routines can be compiled with ++ Turbo C (v2.0) and MSC (v5.1). Make sure MSDOS is defined. ++ ++Patch1 posted to comp.sources.misc ++Version 1.92 13/04/92 eay ++ Changed D_ENCRYPT so that the rotation of R occurs outside of ++ the loop. This required rotating all the longs in sp.h (now ++ called spr.h). Thanks to Richard Outerbridge <71755.204@CompuServe.COM> ++ speed.c has been changed so it will work without SIGALRM. If ++ times(3) is not present it will try to use ftime() instead. ++ ++Version 1.91 08/04/92 eay ++ Added -E/-D options to des(1) so it can use string_to_key. ++ Added SVR4 mods suggested by witr@rwwa.COM ++ Added VMS mods suggested by brennan@coco.cchs.su.oz.AU. If ++ anyone knows how to turn of tty echo in VMS please tell me or ++ implement it yourself :-). ++ Changed FILE *IN/*OUT to *DES_IN/*DES_OUT since it appears VMS ++ does not like IN/OUT being used. ++ ++Libdes posted to comp.sources.misc ++Version 1.9 24/03/92 eay ++ Now contains a fast small crypt replacement. ++ Added des(1) command. ++ Added des_rw_mode so people can use cbc encryption with ++ enc_read and enc_write. ++ ++Version 1.8 15/10/91 eay ++ Bug in cbc_cksum. ++ Many thanks to Keith Reynolds (keithr@sco.COM) for pointing this ++ one out. ++ ++Version 1.7 24/09/91 eay ++ Fixed set_key :-) ++ set_key is 4 times faster and takes less space. ++ There are a few minor changes that could be made. ++ ++Version 1.6 19/09/1991 eay ++ Finally go IP and FP finished. ++ Now I need to fix set_key. ++ This version is quite a bit faster that 1.51 ++ ++Version 1.52 15/06/1991 eay ++ 20% speedup in ecb_encrypt by changing the E bit selection ++ to use 2 32bit words. This also required modification of the ++ sp table. There is still a way to speedup the IP and IP-1 ++ (hints from outer@sq.com) still working on this one :-(. ++ ++Version 1.51 07/06/1991 eay ++ Faster des_encrypt by loop unrolling ++ Fixed bug in quad_cksum.c (thanks to hughes@logos.ucs.indiana.edu) ++ ++Version 1.50 28/05/1991 eay ++ Optimised the code a bit more for the sparc. I have improved the ++ speed of the inner des_encrypt by speeding up the initial and ++ final permutations. ++ ++Version 1.40 23/10/1990 eay ++ Fixed des_random_key, it did not produce a random key :-( ++ ++Version 1.30 2/10/1990 eay ++ Have made des_quad_cksum the same as MIT's, the full package ++ should be compatible with MIT's ++ Have tested on a DECstation 3100 ++ Still need to fix des_set_key (make it faster). ++ Does des_cbc_encrypts at 70.5k/sec on a 3100. ++ ++Version 1.20 18/09/1990 eay ++ Fixed byte order dependencies. ++ Fixed (I hope) all the word alignment problems. ++ Speedup in des_ecb_encrypt. ++ ++Version 1.10 11/09/1990 eay ++ Added des_enc_read and des_enc_write. ++ Still need to fix des_quad_cksum. ++ Still need to document des_enc_read and des_enc_write. ++ ++Version 1.00 27/08/1990 eay ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/crypt586.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,204 @@ ++#!/usr/bin/perl ++# ++# The inner loop instruction sequence and the IP/FP modifications are from ++# Svend Olaf Mikkelsen ++# I've added the stuff needed for crypt() but I've not worried about making ++# things perfect. ++# ++ ++push(@INC,"perlasm","../../perlasm"); ++require "x86asm.pl"; ++ ++&asm_init($ARGV[0],"crypt586.pl"); ++ ++$L="edi"; ++$R="esi"; ++ ++&external_label("des_SPtrans"); ++&fcrypt_body("fcrypt_body"); ++&asm_finish(); ++ ++sub fcrypt_body ++ { ++ local($name,$do_ip)=@_; ++ ++ &function_begin($name,"EXTRN _des_SPtrans:DWORD"); ++ ++ &comment(""); ++ &comment("Load the 2 words"); ++ $ks="ebp"; ++ ++ &xor( $L, $L); ++ &xor( $R, $R); ++ &mov($ks,&wparam(1)); ++ ++ &push(25); # add a variable ++ ++ &set_label("start"); ++ for ($i=0; $i<16; $i+=2) ++ { ++ &comment(""); ++ &comment("Round $i"); ++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ ++ &comment(""); ++ &comment("Round ".sprintf("%d",$i+1)); ++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ } ++ &mov("ebx", &swtmp(0)); ++ &mov("eax", $L); ++ &dec("ebx"); ++ &mov($L, $R); ++ &mov($R, "eax"); ++ &mov(&swtmp(0), "ebx"); ++ &jnz(&label("start")); ++ ++ &comment(""); ++ &comment("FP"); ++ &mov("edx",&wparam(0)); ++ ++ &FP_new($R,$L,"eax",3); ++ &mov(&DWP(0,"edx","",0),"eax"); ++ &mov(&DWP(4,"edx","",0),$L); ++ ++ &pop("ecx"); # remove variable ++ ++ &function_end($name); ++ } ++ ++sub D_ENCRYPT ++ { ++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_; ++ ++ &mov( $u, &wparam(2)); # 2 ++ &mov( $t, $R); ++ &shr( $t, 16); # 1 ++ &mov( $tmp2, &wparam(3)); # 2 ++ &xor( $t, $R); # 1 ++ ++ &and( $u, $t); # 2 ++ &and( $t, $tmp2); # 2 ++ ++ &mov( $tmp1, $u); ++ &shl( $tmp1, 16); # 1 ++ &mov( $tmp2, $t); ++ &shl( $tmp2, 16); # 1 ++ &xor( $u, $tmp1); # 2 ++ &xor( $t, $tmp2); # 2 ++ &mov( $tmp1, &DWP(&n2a($S*4),$ks,"",0)); # 2 ++ &xor( $u, $tmp1); ++ &mov( $tmp2, &DWP(&n2a(($S+1)*4),$ks,"",0)); # 2 ++ &xor( $u, $R); ++ &xor( $t, $R); ++ &xor( $t, $tmp2); ++ ++ &and( $u, "0xfcfcfcfc" ); # 2 ++ &xor( $tmp1, $tmp1); # 1 ++ &and( $t, "0xcfcfcfcf" ); # 2 ++ &xor( $tmp2, $tmp2); ++ &movb( &LB($tmp1), &LB($u) ); ++ &movb( &LB($tmp2), &HB($u) ); ++ &rotr( $t, 4 ); ++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0)); ++ &movb( &LB($tmp1), &LB($t) ); ++ &xor( $L, $ks); ++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0)); ++ &xor( $L, $ks); ++ &movb( &LB($tmp2), &HB($t) ); ++ &shr( $u, 16); ++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0)); ++ &xor( $L, $ks); ++ &movb( &LB($tmp1), &HB($u) ); ++ &shr( $t, 16); ++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0)); ++ &xor( $L, $ks); ++ &mov( $ks, &wparam(1)); ++ &movb( &LB($tmp2), &HB($t) ); ++ &and( $u, "0xff" ); ++ &and( $t, "0xff" ); ++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0)); ++ &xor( $L, $tmp1); ++ } ++ ++sub n2a ++ { ++ sprintf("%d",$_[0]); ++ } ++ ++# now has a side affect of rotating $a by $shift ++sub R_PERM_OP ++ { ++ local($a,$b,$tt,$shift,$mask,$last)=@_; ++ ++ &rotl( $a, $shift ) if ($shift != 0); ++ &mov( $tt, $a ); ++ &xor( $a, $b ); ++ &and( $a, $mask ); ++ if ($notlast eq $b) ++ { ++ &xor( $b, $a ); ++ &xor( $tt, $a ); ++ } ++ else ++ { ++ &xor( $tt, $a ); ++ &xor( $b, $a ); ++ } ++ &comment(""); ++ } ++ ++sub IP_new ++ { ++ local($l,$r,$tt,$lr)=@_; ++ ++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l); ++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l); ++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r); ++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r); ++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r); ++ ++ if ($lr != 3) ++ { ++ if (($lr-3) < 0) ++ { &rotr($tt, 3-$lr); } ++ else { &rotl($tt, $lr-3); } ++ } ++ if ($lr != 2) ++ { ++ if (($lr-2) < 0) ++ { &rotr($r, 2-$lr); } ++ else { &rotl($r, $lr-2); } ++ } ++ } ++ ++sub FP_new ++ { ++ local($l,$r,$tt,$lr)=@_; ++ ++ if ($lr != 2) ++ { ++ if (($lr-2) < 0) ++ { &rotl($r, 2-$lr); } ++ else { &rotr($r, $lr-2); } ++ } ++ if ($lr != 3) ++ { ++ if (($lr-3) < 0) ++ { &rotl($l, 3-$lr); } ++ else { &rotr($l, $lr-3); } ++ } ++ ++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r); ++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r); ++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l); ++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l); ++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r); ++ &rotr($tt , 4); ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/des-586.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,251 @@ ++#!/usr/bin/perl ++# ++# The inner loop instruction sequence and the IP/FP modifications are from ++# Svend Olaf Mikkelsen ++# ++ ++push(@INC,"perlasm","../../perlasm"); ++require "x86asm.pl"; ++require "cbc.pl"; ++require "desboth.pl"; ++ ++# base code is in microsft ++# op dest, source ++# format. ++# ++ ++&asm_init($ARGV[0],"des-586.pl"); ++ ++$L="edi"; ++$R="esi"; ++ ++&external_label("des_SPtrans"); ++&des_encrypt("des_encrypt",1); ++&des_encrypt("des_encrypt2",0); ++&des_encrypt3("des_encrypt3",1); ++&des_encrypt3("des_decrypt3",0); ++&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1); ++&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5); ++ ++&asm_finish(); ++ ++sub des_encrypt ++ { ++ local($name,$do_ip)=@_; ++ ++ &function_begin_B($name,"EXTRN _des_SPtrans:DWORD"); ++ ++ &push("esi"); ++ &push("edi"); ++ ++ &comment(""); ++ &comment("Load the 2 words"); ++ $ks="ebp"; ++ ++ if ($do_ip) ++ { ++ &mov($R,&wparam(0)); ++ &xor( "ecx", "ecx" ); ++ ++ &push("ebx"); ++ &push("ebp"); ++ ++ &mov("eax",&DWP(0,$R,"",0)); ++ &mov("ebx",&wparam(2)); # get encrypt flag ++ &mov($L,&DWP(4,$R,"",0)); ++ &comment(""); ++ &comment("IP"); ++ &IP_new("eax",$L,$R,3); ++ } ++ else ++ { ++ &mov("eax",&wparam(0)); ++ &xor( "ecx", "ecx" ); ++ ++ &push("ebx"); ++ &push("ebp"); ++ ++ &mov($R,&DWP(0,"eax","",0)); ++ &mov("ebx",&wparam(2)); # get encrypt flag ++ &rotl($R,3); ++ &mov($L,&DWP(4,"eax","",0)); ++ &rotl($L,3); ++ } ++ ++ &mov( $ks, &wparam(1) ); ++ &cmp("ebx","0"); ++ &je(&label("start_decrypt")); ++ ++ for ($i=0; $i<16; $i+=2) ++ { ++ &comment(""); ++ &comment("Round $i"); ++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ ++ &comment(""); ++ &comment("Round ".sprintf("%d",$i+1)); ++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ } ++ &jmp(&label("end")); ++ ++ &set_label("start_decrypt"); ++ ++ for ($i=15; $i>0; $i-=2) ++ { ++ &comment(""); ++ &comment("Round $i"); ++ &D_ENCRYPT(15-$i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ &comment(""); ++ &comment("Round ".sprintf("%d",$i-1)); ++ &D_ENCRYPT(15-$i+1,$R,$L,($i-1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx"); ++ } ++ ++ &set_label("end"); ++ ++ if ($do_ip) ++ { ++ &comment(""); ++ &comment("FP"); ++ &mov("edx",&wparam(0)); ++ &FP_new($L,$R,"eax",3); ++ ++ &mov(&DWP(0,"edx","",0),"eax"); ++ &mov(&DWP(4,"edx","",0),$R); ++ } ++ else ++ { ++ &comment(""); ++ &comment("Fixup"); ++ &rotr($L,3); # r ++ &mov("eax",&wparam(0)); ++ &rotr($R,3); # l ++ &mov(&DWP(0,"eax","",0),$L); ++ &mov(&DWP(4,"eax","",0),$R); ++ } ++ ++ &pop("ebp"); ++ &pop("ebx"); ++ &pop("edi"); ++ &pop("esi"); ++ &ret(); ++ ++ &function_end_B($name); ++ } ++ ++sub D_ENCRYPT ++ { ++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_; ++ ++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0)); ++ &xor( $tmp1, $tmp1); ++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0)); ++ &xor( $u, $R); ++ &xor( $t, $R); ++ &and( $u, "0xfcfcfcfc" ); ++ &and( $t, "0xcfcfcfcf" ); ++ &movb( &LB($tmp1), &LB($u) ); ++ &movb( &LB($tmp2), &HB($u) ); ++ &rotr( $t, 4 ); ++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0)); ++ &movb( &LB($tmp1), &LB($t) ); ++ &xor( $L, $ks); ++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0)); ++ &xor( $L, $ks); ###### ++ &movb( &LB($tmp2), &HB($t) ); ++ &shr( $u, 16); ++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0)); ++ &xor( $L, $ks); ###### ++ &movb( &LB($tmp1), &HB($u) ); ++ &shr( $t, 16); ++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0)); ++ &xor( $L, $ks); ++ &mov( $ks, &wparam(1) ); ++ &movb( &LB($tmp2), &HB($t) ); ++ &and( $u, "0xff" ); ++ &and( $t, "0xff" ); ++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0)); ++ &xor( $L, $tmp1); ++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0)); ++ &xor( $L, $tmp1); ++ } ++ ++sub n2a ++ { ++ sprintf("%d",$_[0]); ++ } ++ ++# now has a side affect of rotating $a by $shift ++sub R_PERM_OP ++ { ++ local($a,$b,$tt,$shift,$mask,$last)=@_; ++ ++ &rotl( $a, $shift ) if ($shift != 0); ++ &mov( $tt, $a ); ++ &xor( $a, $b ); ++ &and( $a, $mask ); ++ if (!$last eq $b) ++ { ++ &xor( $b, $a ); ++ &xor( $tt, $a ); ++ } ++ else ++ { ++ &xor( $tt, $a ); ++ &xor( $b, $a ); ++ } ++ &comment(""); ++ } ++ ++sub IP_new ++ { ++ local($l,$r,$tt,$lr)=@_; ++ ++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l); ++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l); ++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r); ++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r); ++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r); ++ ++ if ($lr != 3) ++ { ++ if (($lr-3) < 0) ++ { &rotr($tt, 3-$lr); } ++ else { &rotl($tt, $lr-3); } ++ } ++ if ($lr != 2) ++ { ++ if (($lr-2) < 0) ++ { &rotr($r, 2-$lr); } ++ else { &rotl($r, $lr-2); } ++ } ++ } ++ ++sub FP_new ++ { ++ local($l,$r,$tt,$lr)=@_; ++ ++ if ($lr != 2) ++ { ++ if (($lr-2) < 0) ++ { &rotl($r, 2-$lr); } ++ else { &rotr($r, $lr-2); } ++ } ++ if ($lr != 3) ++ { ++ if (($lr-3) < 0) ++ { &rotl($l, 3-$lr); } ++ else { &rotr($l, $lr-3); } ++ } ++ ++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r); ++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r); ++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l); ++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l); ++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r); ++ &rotr($tt , 4); ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/des686.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,230 @@ ++#!/usr/bin/perl ++ ++$prog="des686.pl"; ++ ++# base code is in microsft ++# op dest, source ++# format. ++# ++ ++# WILL NOT WORK ANYMORE WITH desboth.pl ++require "desboth.pl"; ++ ++if ( ($ARGV[0] eq "elf")) ++ { require "x86unix.pl"; } ++elsif ( ($ARGV[0] eq "a.out")) ++ { $aout=1; require "x86unix.pl"; } ++elsif ( ($ARGV[0] eq "sol")) ++ { $sol=1; require "x86unix.pl"; } ++elsif ( ($ARGV[0] eq "cpp")) ++ { $cpp=1; require "x86unix.pl"; } ++elsif ( ($ARGV[0] eq "win32")) ++ { require "x86ms.pl"; } ++else ++ { ++ print STDERR <<"EOF"; ++Pick one target type from ++ elf - linux, FreeBSD etc ++ a.out - old linux ++ sol - x86 solaris ++ cpp - format so x86unix.cpp can be used ++ win32 - Windows 95/Windows NT ++EOF ++ exit(1); ++ } ++ ++&comment("Don't even think of reading this code"); ++&comment("It was automatically generated by $prog"); ++&comment("Which is a perl program used to generate the x86 assember for"); ++&comment("any of elf, a.out, Win32, or Solaris"); ++&comment("It can be found in SSLeay 0.6.5+ or in libdes 3.26+"); ++&comment("eric "); ++&comment(""); ++ ++&file("dx86xxxx"); ++ ++$L="edi"; ++$R="esi"; ++ ++&des_encrypt("des_encrypt",1); ++&des_encrypt("des_encrypt2",0); ++ ++&des_encrypt3("des_encrypt3",1); ++&des_encrypt3("des_decrypt3",0); ++ ++&file_end(); ++ ++sub des_encrypt ++ { ++ local($name,$do_ip)=@_; ++ ++ &function_begin($name,"EXTRN _des_SPtrans:DWORD"); ++ ++ &comment(""); ++ &comment("Load the 2 words"); ++ &mov("eax",&wparam(0)); ++ &mov($L,&DWP(0,"eax","",0)); ++ &mov($R,&DWP(4,"eax","",0)); ++ ++ $ksp=&wparam(1); ++ ++ if ($do_ip) ++ { ++ &comment(""); ++ &comment("IP"); ++ &IP_new($L,$R,"eax"); ++ } ++ ++ &comment(""); ++ &comment("fixup rotate"); ++ &rotl($R,3); ++ &rotl($L,3); ++ &exch($L,$R); ++ ++ &comment(""); ++ &comment("load counter, key_schedule and enc flag"); ++ &mov("eax",&wparam(2)); # get encrypt flag ++ &mov("ebp",&wparam(1)); # get ks ++ &cmp("eax","0"); ++ &je(&label("start_decrypt")); ++ ++ # encrypting part ++ ++ for ($i=0; $i<16; $i+=2) ++ { ++ &comment(""); ++ &comment("Round $i"); ++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx"); ++ ++ &comment(""); ++ &comment("Round ".sprintf("%d",$i+1)); ++ &D_ENCRYPT($R,$L,($i+1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx"); ++ } ++ &jmp(&label("end")); ++ ++ &set_label("start_decrypt"); ++ ++ for ($i=15; $i>0; $i-=2) ++ { ++ &comment(""); ++ &comment("Round $i"); ++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx"); ++ &comment(""); ++ &comment("Round ".sprintf("%d",$i-1)); ++ &D_ENCRYPT($R,$L,($i-1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx"); ++ } ++ ++ &set_label("end"); ++ ++ &comment(""); ++ &comment("Fixup"); ++ &rotr($L,3); # r ++ &rotr($R,3); # l ++ ++ if ($do_ip) ++ { ++ &comment(""); ++ &comment("FP"); ++ &FP_new($R,$L,"eax"); ++ } ++ ++ &mov("eax",&wparam(0)); ++ &mov(&DWP(0,"eax","",0),$L); ++ &mov(&DWP(4,"eax","",0),$R); ++ ++ &function_end($name); ++ } ++ ++ ++# The logic is to load R into 2 registers and operate on both at the same time. ++# We also load the 2 R's into 2 more registers so we can do the 'move word down a byte' ++# while also masking the other copy and doing a lookup. We then also accumulate the ++# L value in 2 registers then combine them at the end. ++sub D_ENCRYPT ++ { ++ local($L,$R,$S,$ks,$desSP,$u,$t,$tmp1,$tmp2,$tmp3)=@_; ++ ++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0)); ++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0)); ++ &xor( $u, $R ); ++ &xor( $t, $R ); ++ &rotr( $t, 4 ); ++ ++ # the numbers at the end of the line are origional instruction order ++ &mov( $tmp2, $u ); # 1 2 ++ &mov( $tmp1, $t ); # 1 1 ++ &and( $tmp2, "0xfc" ); # 1 4 ++ &and( $tmp1, "0xfc" ); # 1 3 ++ &shr( $t, 8 ); # 1 5 ++ &xor( $L, &DWP("0x100+$desSP",$tmp1,"",0)); # 1 7 ++ &shr( $u, 8 ); # 1 6 ++ &mov( $tmp1, &DWP(" $desSP",$tmp2,"",0)); # 1 8 ++ ++ &mov( $tmp2, $u ); # 2 2 ++ &xor( $L, $tmp1 ); # 1 9 ++ &and( $tmp2, "0xfc" ); # 2 4 ++ &mov( $tmp1, $t ); # 2 1 ++ &and( $tmp1, "0xfc" ); # 2 3 ++ &shr( $t, 8 ); # 2 5 ++ &xor( $L, &DWP("0x300+$desSP",$tmp1,"",0)); # 2 7 ++ &shr( $u, 8 ); # 2 6 ++ &mov( $tmp1, &DWP("0x200+$desSP",$tmp2,"",0)); # 2 8 ++ &mov( $tmp2, $u ); # 3 2 ++ ++ &xor( $L, $tmp1 ); # 2 9 ++ &and( $tmp2, "0xfc" ); # 3 4 ++ ++ &mov( $tmp1, $t ); # 3 1 ++ &shr( $u, 8 ); # 3 6 ++ &and( $tmp1, "0xfc" ); # 3 3 ++ &shr( $t, 8 ); # 3 5 ++ &xor( $L, &DWP("0x500+$desSP",$tmp1,"",0)); # 3 7 ++ &mov( $tmp1, &DWP("0x400+$desSP",$tmp2,"",0)); # 3 8 ++ ++ &and( $t, "0xfc" ); # 4 1 ++ &xor( $L, $tmp1 ); # 3 9 ++ ++ &and( $u, "0xfc" ); # 4 2 ++ &xor( $L, &DWP("0x700+$desSP",$t,"",0)); # 4 3 ++ &xor( $L, &DWP("0x600+$desSP",$u,"",0)); # 4 4 ++ } ++ ++sub PERM_OP ++ { ++ local($a,$b,$tt,$shift,$mask)=@_; ++ ++ &mov( $tt, $a ); ++ &shr( $tt, $shift ); ++ &xor( $tt, $b ); ++ &and( $tt, $mask ); ++ &xor( $b, $tt ); ++ &shl( $tt, $shift ); ++ &xor( $a, $tt ); ++ } ++ ++sub IP_new ++ { ++ local($l,$r,$tt)=@_; ++ ++ &PERM_OP($r,$l,$tt, 4,"0x0f0f0f0f"); ++ &PERM_OP($l,$r,$tt,16,"0x0000ffff"); ++ &PERM_OP($r,$l,$tt, 2,"0x33333333"); ++ &PERM_OP($l,$r,$tt, 8,"0x00ff00ff"); ++ &PERM_OP($r,$l,$tt, 1,"0x55555555"); ++ } ++ ++sub FP_new ++ { ++ local($l,$r,$tt)=@_; ++ ++ &PERM_OP($l,$r,$tt, 1,"0x55555555"); ++ &PERM_OP($r,$l,$tt, 8,"0x00ff00ff"); ++ &PERM_OP($l,$r,$tt, 2,"0x33333333"); ++ &PERM_OP($r,$l,$tt,16,"0x0000ffff"); ++ &PERM_OP($l,$r,$tt, 4,"0x0f0f0f0f"); ++ } ++ ++sub n2a ++ { ++ sprintf("%d",$_[0]); ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/desboth.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,79 @@ ++#!/usr/bin/perl ++ ++$L="edi"; ++$R="esi"; ++ ++sub des_encrypt3 ++ { ++ local($name,$enc)=@_; ++ ++ &function_begin_B($name,""); ++ &push("ebx"); ++ &mov("ebx",&wparam(0)); ++ ++ &push("ebp"); ++ &push("esi"); ++ ++ &push("edi"); ++ ++ &comment(""); ++ &comment("Load the data words"); ++ &mov($L,&DWP(0,"ebx","",0)); ++ &mov($R,&DWP(4,"ebx","",0)); ++ &stack_push(3); ++ ++ &comment(""); ++ &comment("IP"); ++ &IP_new($L,$R,"edx",0); ++ ++ # put them back ++ ++ if ($enc) ++ { ++ &mov(&DWP(4,"ebx","",0),$R); ++ &mov("eax",&wparam(1)); ++ &mov(&DWP(0,"ebx","",0),"edx"); ++ &mov("edi",&wparam(2)); ++ &mov("esi",&wparam(3)); ++ } ++ else ++ { ++ &mov(&DWP(4,"ebx","",0),$R); ++ &mov("esi",&wparam(1)); ++ &mov(&DWP(0,"ebx","",0),"edx"); ++ &mov("edi",&wparam(2)); ++ &mov("eax",&wparam(3)); ++ } ++ &mov(&swtmp(2), (($enc)?"1":"0")); ++ &mov(&swtmp(1), "eax"); ++ &mov(&swtmp(0), "ebx"); ++ &call("des_encrypt2"); ++ &mov(&swtmp(2), (($enc)?"0":"1")); ++ &mov(&swtmp(1), "edi"); ++ &mov(&swtmp(0), "ebx"); ++ &call("des_encrypt2"); ++ &mov(&swtmp(2), (($enc)?"1":"0")); ++ &mov(&swtmp(1), "esi"); ++ &mov(&swtmp(0), "ebx"); ++ &call("des_encrypt2"); ++ ++ &stack_pop(3); ++ &mov($L,&DWP(0,"ebx","",0)); ++ &mov($R,&DWP(4,"ebx","",0)); ++ ++ &comment(""); ++ &comment("FP"); ++ &FP_new($L,$R,"eax",0); ++ ++ &mov(&DWP(0,"ebx","",0),"eax"); ++ &mov(&DWP(4,"ebx","",0),$R); ++ ++ &pop("edi"); ++ &pop("esi"); ++ &pop("ebp"); ++ &pop("ebx"); ++ &ret(); ++ &function_end_B($name); ++ } ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/perlasm/cbc.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,342 @@ ++#!/usr/bin/perl ++ ++# void des_ncbc_encrypt(input, output, length, schedule, ivec, enc) ++# des_cblock (*input); ++# des_cblock (*output); ++# long length; ++# des_key_schedule schedule; ++# des_cblock (*ivec); ++# int enc; ++# ++# calls ++# des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); ++# ++ ++#&cbc("des_ncbc_encrypt","des_encrypt",0); ++#&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt", ++# 1,4,5,3,5,-1); ++#&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt", ++# 0,4,5,3,5,-1); ++#&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3", ++# 0,6,7,3,4,5); ++# ++# When doing a cipher that needs bigendian order, ++# for encrypt, the iv is kept in bigendian form, ++# while for decrypt, it is kept in little endian. ++sub cbc ++ { ++ local($name,$enc_func,$dec_func,$swap,$iv_off,$enc_off,$p1,$p2,$p3)=@_; ++ # name is the function name ++ # enc_func and dec_func and the functions to call for encrypt/decrypt ++ # swap is true if byte order needs to be reversed ++ # iv_off is parameter number for the iv ++ # enc_off is parameter number for the encrypt/decrypt flag ++ # p1,p2,p3 are the offsets for parameters to be passed to the ++ # underlying calls. ++ ++ &function_begin_B($name,""); ++ &comment(""); ++ ++ $in="esi"; ++ $out="edi"; ++ $count="ebp"; ++ ++ &push("ebp"); ++ &push("ebx"); ++ &push("esi"); ++ &push("edi"); ++ ++ $data_off=4; ++ $data_off+=4 if ($p1 > 0); ++ $data_off+=4 if ($p2 > 0); ++ $data_off+=4 if ($p3 > 0); ++ ++ &mov($count, &wparam(2)); # length ++ ++ &comment("getting iv ptr from parameter $iv_off"); ++ &mov("ebx", &wparam($iv_off)); # Get iv ptr ++ ++ &mov($in, &DWP(0,"ebx","",0));# iv[0] ++ &mov($out, &DWP(4,"ebx","",0));# iv[1] ++ ++ &push($out); ++ &push($in); ++ &push($out); # used in decrypt for iv[1] ++ &push($in); # used in decrypt for iv[0] ++ ++ &mov("ebx", "esp"); # This is the address of tin[2] ++ ++ &mov($in, &wparam(0)); # in ++ &mov($out, &wparam(1)); # out ++ ++ # We have loaded them all, how lets push things ++ &comment("getting encrypt flag from parameter $enc_off"); ++ &mov("ecx", &wparam($enc_off)); # Get enc flag ++ if ($p3 > 0) ++ { ++ &comment("get and push parameter $p3"); ++ if ($enc_off != $p3) ++ { &mov("eax", &wparam($p3)); &push("eax"); } ++ else { &push("ecx"); } ++ } ++ if ($p2 > 0) ++ { ++ &comment("get and push parameter $p2"); ++ if ($enc_off != $p2) ++ { &mov("eax", &wparam($p2)); &push("eax"); } ++ else { &push("ecx"); } ++ } ++ if ($p1 > 0) ++ { ++ &comment("get and push parameter $p1"); ++ if ($enc_off != $p1) ++ { &mov("eax", &wparam($p1)); &push("eax"); } ++ else { &push("ecx"); } ++ } ++ &push("ebx"); # push data/iv ++ ++ &cmp("ecx",0); ++ &jz(&label("decrypt")); ++ ++ &and($count,0xfffffff8); ++ &mov("eax", &DWP($data_off,"esp","",0)); # load iv[0] ++ &mov("ebx", &DWP($data_off+4,"esp","",0)); # load iv[1] ++ ++ &jz(&label("encrypt_finish")); ++ ++ ############################################################# ++ ++ &set_label("encrypt_loop"); ++ # encrypt start ++ # "eax" and "ebx" hold iv (or the last cipher text) ++ ++ &mov("ecx", &DWP(0,$in,"",0)); # load first 4 bytes ++ &mov("edx", &DWP(4,$in,"",0)); # second 4 bytes ++ ++ &xor("eax", "ecx"); ++ &xor("ebx", "edx"); ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call ++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); # ++ ++ &call($enc_func); ++ ++ &mov("eax", &DWP($data_off,"esp","",0)); ++ &mov("ebx", &DWP($data_off+4,"esp","",0)); ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP(0,$out,"",0),"eax"); ++ &mov(&DWP(4,$out,"",0),"ebx"); ++ ++ # eax and ebx are the next iv. ++ ++ &add($in, 8); ++ &add($out, 8); ++ ++ &sub($count, 8); ++ &jnz(&label("encrypt_loop")); ++ ++###################################################################3 ++ &set_label("encrypt_finish"); ++ &mov($count, &wparam(2)); # length ++ &and($count, 7); ++ &jz(&label("finish")); ++ &xor("ecx","ecx"); ++ &xor("edx","edx"); ++ &mov($count,&DWP(&label("cbc_enc_jmp_table"),"",$count,4)); ++ &jmp_ptr($count); ++ ++&set_label("ej7"); ++ &xor("edx", "edx") if $ppro; # ppro friendly ++ &movb(&HB("edx"), &BP(6,$in,"",0)); ++ &shl("edx",8); ++&set_label("ej6"); ++ &movb(&HB("edx"), &BP(5,$in,"",0)); ++&set_label("ej5"); ++ &movb(&LB("edx"), &BP(4,$in,"",0)); ++&set_label("ej4"); ++ &mov("ecx", &DWP(0,$in,"",0)); ++ &jmp(&label("ejend")); ++&set_label("ej3"); ++ &movb(&HB("ecx"), &BP(2,$in,"",0)); ++ &xor("ecx", "ecx") if $ppro; # ppro friendly ++ &shl("ecx",8); ++&set_label("ej2"); ++ &movb(&HB("ecx"), &BP(1,$in,"",0)); ++&set_label("ej1"); ++ &movb(&LB("ecx"), &BP(0,$in,"",0)); ++&set_label("ejend"); ++ ++ &xor("eax", "ecx"); ++ &xor("ebx", "edx"); ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call ++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); # ++ ++ &call($enc_func); ++ ++ &mov("eax", &DWP($data_off,"esp","",0)); ++ &mov("ebx", &DWP($data_off+4,"esp","",0)); ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP(0,$out,"",0),"eax"); ++ &mov(&DWP(4,$out,"",0),"ebx"); ++ ++ &jmp(&label("finish")); ++ ++ ############################################################# ++ ############################################################# ++ &set_label("decrypt",1); ++ # decrypt start ++ &and($count,0xfffffff8); ++ # The next 2 instructions are only for if the jz is taken ++ &mov("eax", &DWP($data_off+8,"esp","",0)); # get iv[0] ++ &mov("ebx", &DWP($data_off+12,"esp","",0)); # get iv[1] ++ &jz(&label("decrypt_finish")); ++ ++ &set_label("decrypt_loop"); ++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes ++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back ++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); # ++ ++ &call($dec_func); ++ ++ &mov("eax", &DWP($data_off,"esp","",0)); # get return ++ &mov("ebx", &DWP($data_off+4,"esp","",0)); # ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0] ++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1] ++ ++ &xor("ecx", "eax"); ++ &xor("edx", "ebx"); ++ ++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text, ++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually ++ ++ &mov(&DWP(0,$out,"",0),"ecx"); ++ &mov(&DWP(4,$out,"",0),"edx"); ++ ++ &mov(&DWP($data_off+8,"esp","",0), "eax"); # save iv ++ &mov(&DWP($data_off+12,"esp","",0), "ebx"); # ++ ++ &add($in, 8); ++ &add($out, 8); ++ ++ &sub($count, 8); ++ &jnz(&label("decrypt_loop")); ++############################ ENDIT #######################3 ++ &set_label("decrypt_finish"); ++ &mov($count, &wparam(2)); # length ++ &and($count, 7); ++ &jz(&label("finish")); ++ ++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes ++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back ++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); # ++ ++ &call($dec_func); ++ ++ &mov("eax", &DWP($data_off,"esp","",0)); # get return ++ &mov("ebx", &DWP($data_off+4,"esp","",0)); # ++ ++ &bswap("eax") if $swap; ++ &bswap("ebx") if $swap; ++ ++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0] ++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1] ++ ++ &xor("ecx", "eax"); ++ &xor("edx", "ebx"); ++ ++ # this is for when we exit ++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text, ++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually ++ ++&set_label("dj7"); ++ &rotr("edx", 16); ++ &movb(&BP(6,$out,"",0), &LB("edx")); ++ &shr("edx",16); ++&set_label("dj6"); ++ &movb(&BP(5,$out,"",0), &HB("edx")); ++&set_label("dj5"); ++ &movb(&BP(4,$out,"",0), &LB("edx")); ++&set_label("dj4"); ++ &mov(&DWP(0,$out,"",0), "ecx"); ++ &jmp(&label("djend")); ++&set_label("dj3"); ++ &rotr("ecx", 16); ++ &movb(&BP(2,$out,"",0), &LB("ecx")); ++ &shl("ecx",16); ++&set_label("dj2"); ++ &movb(&BP(1,$in,"",0), &HB("ecx")); ++&set_label("dj1"); ++ &movb(&BP(0,$in,"",0), &LB("ecx")); ++&set_label("djend"); ++ ++ # final iv is still in eax:ebx ++ &jmp(&label("finish")); ++ ++ ++############################ FINISH #######################3 ++ &set_label("finish",1); ++ &mov("ecx", &wparam($iv_off)); # Get iv ptr ++ ++ ################################################# ++ $total=16+4; ++ $total+=4 if ($p1 > 0); ++ $total+=4 if ($p2 > 0); ++ $total+=4 if ($p3 > 0); ++ &add("esp",$total); ++ ++ &mov(&DWP(0,"ecx","",0), "eax"); # save iv ++ &mov(&DWP(4,"ecx","",0), "ebx"); # save iv ++ ++ &function_end_A($name); ++ ++ &set_label("cbc_enc_jmp_table",1); ++ &data_word("0"); ++ &data_word(&label("ej1")); ++ &data_word(&label("ej2")); ++ &data_word(&label("ej3")); ++ &data_word(&label("ej4")); ++ &data_word(&label("ej5")); ++ &data_word(&label("ej6")); ++ &data_word(&label("ej7")); ++ &set_label("cbc_dec_jmp_table",1); ++ &data_word("0"); ++ &data_word(&label("dj1")); ++ &data_word(&label("dj2")); ++ &data_word(&label("dj3")); ++ &data_word(&label("dj4")); ++ &data_word(&label("dj5")); ++ &data_word(&label("dj6")); ++ &data_word(&label("dj7")); ++ ++ &function_end_B($name); ++ ++ } ++ ++1; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/perlasm/readme Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,124 @@ ++The perl scripts in this directory are my 'hack' to generate ++multiple different assembler formats via the one origional script. ++ ++The way to use this library is to start with adding the path to this directory ++and then include it. ++ ++push(@INC,"perlasm","../../perlasm"); ++require "x86asm.pl"; ++ ++The first thing we do is setup the file and type of assember ++ ++&asm_init($ARGV[0],$0); ++ ++The first argument is the 'type'. Currently ++'cpp', 'sol', 'a.out', 'elf' or 'win32'. ++Argument 2 is the file name. ++ ++The reciprocal function is ++&asm_finish() which should be called at the end. ++ ++There are 2 main 'packages'. x86ms.pl, which is the microsoft assembler, ++and x86unix.pl which is the unix (gas) version. ++ ++Functions of interest are: ++&external_label("des_SPtrans"); declare and external variable ++&LB(reg); Low byte for a register ++&HB(reg); High byte for a register ++&BP(off,base,index,scale) Byte pointer addressing ++&DWP(off,base,index,scale) Word pointer addressing ++&stack_push(num) Basically a 'sub esp, num*4' with extra ++&stack_pop(num) inverse of stack_push ++&function_begin(name,extra) Start a function with pushing of ++ edi, esi, ebx and ebp. extra is extra win32 ++ external info that may be required. ++&function_begin_B(name,extra) Same as norma function_begin but no pushing. ++&function_end(name) Call at end of function. ++&function_end_A(name) Standard pop and ret, for use inside functions ++&function_end_B(name) Call at end but with poping or 'ret'. ++&swtmp(num) Address on stack temp word. ++&wparam(num) Parameter number num, that was push ++ in C convention. This all works over pushes ++ and pops. ++&comment("hello there") Put in a comment. ++&label("loop") Refer to a label, normally a jmp target. ++&set_label("loop") Set a label at this point. ++&data_word(word) Put in a word of data. ++ ++So how does this all hold together? Given ++ ++int calc(int len, int *data) ++ { ++ int i,j=0; ++ ++ for (i=0; i"); ++&comment(""); ++ ++ $filename =~ s/\.pl$//; ++ &file($filename); ++ } ++ ++sub asm_finish_cpp ++ { ++ return unless $cpp; ++ ++ local($tmp,$i); ++ foreach $i (&get_labels()) ++ { ++ $tmp.="#define $i _$i\n"; ++ } ++ print <<"EOF"; ++/* Run the C pre-processor over this file with one of the following defined ++ * ELF - elf object files, ++ * OUT - a.out object files, ++ * BSDI - BSDI style a.out object files ++ * SOL - Solaris style elf ++ */ ++ ++#define TYPE(a,b) .type a,b ++#define SIZE(a,b) .size a,b ++ ++#if defined(OUT) || defined(BSDI) ++$tmp ++#endif ++ ++#ifdef OUT ++#define OK 1 ++#define ALIGN 4 ++#endif ++ ++#ifdef BSDI ++#define OK 1 ++#define ALIGN 4 ++#undef SIZE ++#undef TYPE ++#endif ++ ++#if defined(ELF) || defined(SOL) ++#define OK 1 ++#define ALIGN 16 ++#endif ++ ++#ifndef OK ++You need to define one of ++ELF - elf systems - linux-elf, NetBSD and DG-UX ++OUT - a.out systems - linux-a.out and FreeBSD ++SOL - solaris systems, which are elf with strange comment lines ++BSDI - a.out with a very primative version of as. ++#endif ++ ++/* Let the Assembler begin :-) */ ++EOF ++ } ++ ++1; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/perlasm/x86ms.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,345 @@ ++#!/usr/bin/perl ++ ++package x86ms; ++ ++$label="L000"; ++ ++%lb=( 'eax', 'al', ++ 'ebx', 'bl', ++ 'ecx', 'cl', ++ 'edx', 'dl', ++ 'ax', 'al', ++ 'bx', 'bl', ++ 'cx', 'cl', ++ 'dx', 'dl', ++ ); ++ ++%hb=( 'eax', 'ah', ++ 'ebx', 'bh', ++ 'ecx', 'ch', ++ 'edx', 'dh', ++ 'ax', 'ah', ++ 'bx', 'bh', ++ 'cx', 'ch', ++ 'dx', 'dh', ++ ); ++ ++sub main'asm_init_output { @out=(); } ++sub main'asm_get_output { return(@out); } ++sub main'get_labels { return(@labels); } ++sub main'external_label { push(@labels,@_); } ++ ++sub main'LB ++ { ++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n"; ++ return($lb{$_[0]}); ++ } ++ ++sub main'HB ++ { ++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n"; ++ return($hb{$_[0]}); ++ } ++ ++sub main'BP ++ { ++ &get_mem("BYTE",@_); ++ } ++ ++sub main'DWP ++ { ++ &get_mem("DWORD",@_); ++ } ++ ++sub main'stack_push ++ { ++ local($num)=@_; ++ $stack+=$num*4; ++ &main'sub("esp",$num*4); ++ } ++ ++sub main'stack_pop ++ { ++ local($num)=@_; ++ $stack-=$num*4; ++ &main'add("esp",$num*4); ++ } ++ ++sub get_mem ++ { ++ local($size,$addr,$reg1,$reg2,$idx)=@_; ++ local($t,$post); ++ local($ret)="$size PTR "; ++ ++ $addr =~ s/^\s+//; ++ if ($addr =~ /^(.+)\+(.+)$/) ++ { ++ $reg2=&conv($1); ++ $addr="_$2"; ++ } ++ elsif ($addr =~ /^[_a-zA-Z]/) ++ { ++ $addr="_$addr"; ++ } ++ ++ $reg1="$regs{$reg1}" if defined($regs{$reg1}); ++ $reg2="$regs{$reg2}" if defined($regs{$reg2}); ++ if (($addr ne "") && ($addr ne 0)) ++ { ++ if ($addr !~ /^-/) ++ { $ret.=$addr; } ++ else { $post=$addr; } ++ } ++ if ($reg2 ne "") ++ { ++ $t=""; ++ $t="*$idx" if ($idx != 0); ++ $reg1="+".$reg1 if ("$reg1$post" ne ""); ++ $ret.="[$reg2$t$reg1$post]"; ++ } ++ else ++ { ++ $ret.="[$reg1$post]" ++ } ++ return($ret); ++ } ++ ++sub main'mov { &out2("mov",@_); } ++sub main'movb { &out2("mov",@_); } ++sub main'and { &out2("and",@_); } ++sub main'or { &out2("or",@_); } ++sub main'shl { &out2("shl",@_); } ++sub main'shr { &out2("shr",@_); } ++sub main'xor { &out2("xor",@_); } ++sub main'xorb { &out2("xor",@_); } ++sub main'add { &out2("add",@_); } ++sub main'adc { &out2("adc",@_); } ++sub main'sub { &out2("sub",@_); } ++sub main'rotl { &out2("rol",@_); } ++sub main'rotr { &out2("ror",@_); } ++sub main'exch { &out2("xchg",@_); } ++sub main'cmp { &out2("cmp",@_); } ++sub main'lea { &out2("lea",@_); } ++sub main'mul { &out1("mul",@_); } ++sub main'div { &out1("div",@_); } ++sub main'dec { &out1("dec",@_); } ++sub main'inc { &out1("inc",@_); } ++sub main'jmp { &out1("jmp",@_); } ++sub main'jmp_ptr { &out1p("jmp",@_); } ++sub main'je { &out1("je",@_); } ++sub main'jle { &out1("jle",@_); } ++sub main'jz { &out1("jz",@_); } ++sub main'jge { &out1("jge",@_); } ++sub main'jl { &out1("jl",@_); } ++sub main'jb { &out1("jb",@_); } ++sub main'jnz { &out1("jnz",@_); } ++sub main'jne { &out1("jne",@_); } ++sub main'push { &out1("push",@_); $stack+=4; } ++sub main'pop { &out1("pop",@_); $stack-=4; } ++sub main'bswap { &out1("bswap",@_); &using486(); } ++sub main'not { &out1("not",@_); } ++sub main'call { &out1("call",'_'.$_[0]); } ++sub main'ret { &out0("ret"); } ++sub main'nop { &out0("nop"); } ++ ++sub out2 ++ { ++ local($name,$p1,$p2)=@_; ++ local($l,$t); ++ ++ push(@out,"\t$name\t"); ++ $t=&conv($p1).","; ++ $l=length($t); ++ push(@out,$t); ++ $l=4-($l+9)/8; ++ push(@out,"\t" x $l); ++ push(@out,&conv($p2)); ++ push(@out,"\n"); ++ } ++ ++sub out0 ++ { ++ local($name)=@_; ++ ++ push(@out,"\t$name\n"); ++ } ++ ++sub out1 ++ { ++ local($name,$p1)=@_; ++ local($l,$t); ++ ++ push(@out,"\t$name\t".&conv($p1)."\n"); ++ } ++ ++sub conv ++ { ++ local($p)=@_; ++ ++ $p =~ s/0x([0-9A-Fa-f]+)/0$1h/; ++ return $p; ++ } ++ ++sub using486 ++ { ++ return if $using486; ++ $using486++; ++ grep(s/\.386/\.486/,@out); ++ } ++ ++sub main'file ++ { ++ local($file)=@_; ++ ++ local($tmp)=<<"EOF"; ++ TITLE $file.asm ++ .386 ++.model FLAT ++EOF ++ push(@out,$tmp); ++ } ++ ++sub main'function_begin ++ { ++ local($func,$extra)=@_; ++ ++ push(@labels,$func); ++ ++ local($tmp)=<<"EOF"; ++_TEXT SEGMENT ++PUBLIC _$func ++$extra ++_$func PROC NEAR ++ push ebp ++ push ebx ++ push esi ++ push edi ++EOF ++ push(@out,$tmp); ++ $stack=20; ++ } ++ ++sub main'function_begin_B ++ { ++ local($func,$extra)=@_; ++ ++ local($tmp)=<<"EOF"; ++_TEXT SEGMENT ++PUBLIC _$func ++$extra ++_$func PROC NEAR ++EOF ++ push(@out,$tmp); ++ $stack=4; ++ } ++ ++sub main'function_end ++ { ++ local($func)=@_; ++ ++ local($tmp)=<<"EOF"; ++ pop edi ++ pop esi ++ pop ebx ++ pop ebp ++ ret ++_$func ENDP ++_TEXT ENDS ++EOF ++ push(@out,$tmp); ++ $stack=0; ++ %label=(); ++ } ++ ++sub main'function_end_B ++ { ++ local($func)=@_; ++ ++ local($tmp)=<<"EOF"; ++_$func ENDP ++_TEXT ENDS ++EOF ++ push(@out,$tmp); ++ $stack=0; ++ %label=(); ++ } ++ ++sub main'function_end_A ++ { ++ local($func)=@_; ++ ++ local($tmp)=<<"EOF"; ++ pop edi ++ pop esi ++ pop ebx ++ pop ebp ++ ret ++EOF ++ push(@out,$tmp); ++ } ++ ++sub main'file_end ++ { ++ push(@out,"END\n"); ++ } ++ ++sub main'wparam ++ { ++ local($num)=@_; ++ ++ return(&main'DWP($stack+$num*4,"esp","",0)); ++ } ++ ++sub main'swtmp ++ { ++ return(&main'DWP($_[0]*4,"esp","",0)); ++ } ++ ++# Should use swtmp, which is above esp. Linix can trash the stack above esp ++#sub main'wtmp ++# { ++# local($num)=@_; ++# ++# return(&main'DWP(-(($num+1)*4),"esp","",0)); ++# } ++ ++sub main'comment ++ { ++ foreach (@_) ++ { ++ push(@out,"\t; $_\n"); ++ } ++ } ++ ++sub main'label ++ { ++ if (!defined($label{$_[0]})) ++ { ++ $label{$_[0]}="\$${label}${_[0]}"; ++ $label++; ++ } ++ return($label{$_[0]}); ++ } ++ ++sub main'set_label ++ { ++ if (!defined($label{$_[0]})) ++ { ++ $label{$_[0]}="${label}${_[0]}"; ++ $label++; ++ } ++ push(@out,"$label{$_[0]}:\n"); ++ } ++ ++sub main'data_word ++ { ++ push(@out,"\tDD\t$_[0]\n"); ++ } ++ ++sub out1p ++ { ++ local($name,$p1)=@_; ++ local($l,$t); ++ ++ push(@out,"\t$name\t ".&conv($p1)."\n"); ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/perlasm/x86unix.pl Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,403 @@ ++#!/usr/bin/perl ++ ++package x86unix; ++ ++$label="L000"; ++ ++$align=($main'aout)?"4":"16"; ++$under=($main'aout)?"_":""; ++$com_start=($main'sol)?"/":"#"; ++ ++sub main'asm_init_output { @out=(); } ++sub main'asm_get_output { return(@out); } ++sub main'get_labels { return(@labels); } ++sub main'external_label { push(@labels,@_); } ++ ++if ($main'cpp) ++ { ++ $align="ALIGN"; ++ $under=""; ++ $com_start='/*'; ++ $com_end='*/'; ++ } ++ ++%lb=( 'eax', '%al', ++ 'ebx', '%bl', ++ 'ecx', '%cl', ++ 'edx', '%dl', ++ 'ax', '%al', ++ 'bx', '%bl', ++ 'cx', '%cl', ++ 'dx', '%dl', ++ ); ++ ++%hb=( 'eax', '%ah', ++ 'ebx', '%bh', ++ 'ecx', '%ch', ++ 'edx', '%dh', ++ 'ax', '%ah', ++ 'bx', '%bh', ++ 'cx', '%ch', ++ 'dx', '%dh', ++ ); ++ ++%regs=( 'eax', '%eax', ++ 'ebx', '%ebx', ++ 'ecx', '%ecx', ++ 'edx', '%edx', ++ 'esi', '%esi', ++ 'edi', '%edi', ++ 'ebp', '%ebp', ++ 'esp', '%esp', ++ ); ++ ++%reg_val=( ++ 'eax', 0x00, ++ 'ebx', 0x03, ++ 'ecx', 0x01, ++ 'edx', 0x02, ++ 'esi', 0x06, ++ 'edi', 0x07, ++ 'ebp', 0x05, ++ 'esp', 0x04, ++ ); ++ ++sub main'LB ++ { ++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n"; ++ return($lb{$_[0]}); ++ } ++ ++sub main'HB ++ { ++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n"; ++ return($hb{$_[0]}); ++ } ++ ++sub main'DWP ++ { ++ local($addr,$reg1,$reg2,$idx)=@_; ++ ++ $ret=""; ++ $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/; ++ $reg1="$regs{$reg1}" if defined($regs{$reg1}); ++ $reg2="$regs{$reg2}" if defined($regs{$reg2}); ++ $ret.=$addr if ($addr ne "") && ($addr ne 0); ++ if ($reg2 ne "") ++ { $ret.="($reg1,$reg2,$idx)"; } ++ else ++ { $ret.="($reg1)" } ++ return($ret); ++ } ++ ++sub main'BP ++ { ++ return(&main'DWP(@_)); ++ } ++ ++#sub main'BP ++# { ++# local($addr,$reg1,$reg2,$idx)=@_; ++# ++# $ret=""; ++# ++# $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/; ++# $reg1="$regs{$reg1}" if defined($regs{$reg1}); ++# $reg2="$regs{$reg2}" if defined($regs{$reg2}); ++# $ret.=$addr if ($addr ne "") && ($addr ne 0); ++# if ($reg2 ne "") ++# { $ret.="($reg1,$reg2,$idx)"; } ++# else ++# { $ret.="($reg1)" } ++# return($ret); ++# } ++ ++sub main'mov { &out2("movl",@_); } ++sub main'movb { &out2("movb",@_); } ++sub main'and { &out2("andl",@_); } ++sub main'or { &out2("orl",@_); } ++sub main'shl { &out2("sall",@_); } ++sub main'shr { &out2("shrl",@_); } ++sub main'xor { &out2("xorl",@_); } ++sub main'xorb { &out2("xorb",@_); } ++sub main'add { &out2("addl",@_); } ++sub main'adc { &out2("adcl",@_); } ++sub main'sub { &out2("subl",@_); } ++sub main'rotl { &out2("roll",@_); } ++sub main'rotr { &out2("rorl",@_); } ++sub main'exch { &out2("xchg",@_); } ++sub main'cmp { &out2("cmpl",@_); } ++sub main'lea { &out2("leal",@_); } ++sub main'mul { &out1("mull",@_); } ++sub main'div { &out1("divl",@_); } ++sub main'jmp { &out1("jmp",@_); } ++sub main'jmp_ptr { &out1p("jmp",@_); } ++sub main'je { &out1("je",@_); } ++sub main'jle { &out1("jle",@_); } ++sub main'jne { &out1("jne",@_); } ++sub main'jnz { &out1("jnz",@_); } ++sub main'jz { &out1("jz",@_); } ++sub main'jge { &out1("jge",@_); } ++sub main'jl { &out1("jl",@_); } ++sub main'jb { &out1("jb",@_); } ++sub main'dec { &out1("decl",@_); } ++sub main'inc { &out1("incl",@_); } ++sub main'push { &out1("pushl",@_); $stack+=4; } ++sub main'pop { &out1("popl",@_); $stack-=4; } ++sub main'bswap { &out1("bswapl",@_); } ++sub main'not { &out1("notl",@_); } ++sub main'call { &out1("call",$under.$_[0]); } ++sub main'ret { &out0("ret"); } ++sub main'nop { &out0("nop"); } ++ ++sub out2 ++ { ++ local($name,$p1,$p2)=@_; ++ local($l,$ll,$t); ++ local(%special)=( "roll",0xD1C0,"rorl",0xD1C8, ++ "rcll",0xD1D0,"rcrl",0xD1D8, ++ "shll",0xD1E0,"shrl",0xD1E8, ++ "sarl",0xD1F8); ++ ++ if ((defined($special{$name})) && defined($regs{$p1}) && ($p2 == 1)) ++ { ++ $op=$special{$name}|$reg_val{$p1}; ++ $tmp1=sprintf ".byte %d\n",($op>>8)&0xff; ++ $tmp2=sprintf ".byte %d\t",$op &0xff; ++ push(@out,$tmp1); ++ push(@out,$tmp2); ++ ++ $p2=&conv($p2); ++ $p1=&conv($p1); ++ &main'comment("$name $p2 $p1"); ++ return; ++ } ++ ++ push(@out,"\t$name\t"); ++ $t=&conv($p2).","; ++ $l=length($t); ++ push(@out,$t); ++ $ll=4-($l+9)/8; ++ $tmp1=sprintf "\t" x $ll; ++ push(@out,$tmp1); ++ push(@out,&conv($p1)."\n"); ++ } ++ ++sub out1 ++ { ++ local($name,$p1)=@_; ++ local($l,$t); ++ ++ push(@out,"\t$name\t".&conv($p1)."\n"); ++ } ++ ++sub out1p ++ { ++ local($name,$p1)=@_; ++ local($l,$t); ++ ++ push(@out,"\t$name\t*".&conv($p1)."\n"); ++ } ++ ++sub out0 ++ { ++ push(@out,"\t$_[0]\n"); ++ } ++ ++sub conv ++ { ++ local($p)=@_; ++ ++# $p =~ s/0x([0-9A-Fa-f]+)/0$1h/; ++ ++ $p=$regs{$p} if (defined($regs{$p})); ++ ++ $p =~ s/^(-{0,1}[0-9A-Fa-f]+)$/\$$1/; ++ $p =~ s/^(0x[0-9A-Fa-f]+)$/\$$1/; ++ return $p; ++ } ++ ++sub main'file ++ { ++ local($file)=@_; ++ ++ local($tmp)=<<"EOF"; ++ .file "$file.s" ++ .version "01.01" ++gcc2_compiled.: ++EOF ++ push(@out,$tmp); ++ } ++ ++sub main'function_begin ++ { ++ local($func)=@_; ++ ++ $func=$under.$func; ++ ++ local($tmp)=<<"EOF"; ++.text ++ .align $align ++.globl $func ++EOF ++ push(@out,$tmp); ++ if ($main'cpp) ++ { $tmp=push(@out,"\tTYPE($func,\@function)\n"); } ++ else { $tmp=push(@out,"\t.type\t$func,\@function\n"); } ++ push(@out,"$func:\n"); ++ $tmp=<<"EOF"; ++ pushl %ebp ++ pushl %ebx ++ pushl %esi ++ pushl %edi ++ ++EOF ++ push(@out,$tmp); ++ $stack=20; ++ } ++ ++sub main'function_begin_B ++ { ++ local($func,$extra)=@_; ++ ++ $func=$under.$func; ++ ++ local($tmp)=<<"EOF"; ++.text ++ .align $align ++.globl $func ++EOF ++ push(@out,$tmp); ++ if ($main'cpp) ++ { push(@out,"\tTYPE($func,\@function)\n"); } ++ else { push(@out,"\t.type $func,\@function\n"); } ++ push(@out,"$func:\n"); ++ $stack=4; ++ } ++ ++sub main'function_end ++ { ++ local($func)=@_; ++ ++ $func=$under.$func; ++ ++ local($tmp)=<<"EOF"; ++ popl %edi ++ popl %esi ++ popl %ebx ++ popl %ebp ++ ret ++.${func}_end: ++EOF ++ push(@out,$tmp); ++ if ($main'cpp) ++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); } ++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); } ++ push(@out,".ident \"$func\"\n"); ++ $stack=0; ++ %label=(); ++ } ++ ++sub main'function_end_A ++ { ++ local($func)=@_; ++ ++ local($tmp)=<<"EOF"; ++ popl %edi ++ popl %esi ++ popl %ebx ++ popl %ebp ++ ret ++EOF ++ push(@out,$tmp); ++ } ++ ++sub main'function_end_B ++ { ++ local($func)=@_; ++ ++ $func=$under.$func; ++ ++ push(@out,".${func}_end:\n"); ++ if ($main'cpp) ++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); } ++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); } ++ push(@out,".ident \"desasm.pl\"\n"); ++ $stack=0; ++ %label=(); ++ } ++ ++sub main'wparam ++ { ++ local($num)=@_; ++ ++ return(&main'DWP($stack+$num*4,"esp","",0)); ++ } ++ ++sub main'stack_push ++ { ++ local($num)=@_; ++ $stack+=$num*4; ++ &main'sub("esp",$num*4); ++ } ++ ++sub main'stack_pop ++ { ++ local($num)=@_; ++ $stack-=$num*4; ++ &main'add("esp",$num*4); ++ } ++ ++sub main'swtmp ++ { ++ return(&main'DWP($_[0]*4,"esp","",0)); ++ } ++ ++# Should use swtmp, which is above esp. Linix can trash the stack above esp ++#sub main'wtmp ++# { ++# local($num)=@_; ++# ++# return(&main'DWP(-($num+1)*4,"esp","",0)); ++# } ++ ++sub main'comment ++ { ++ foreach (@_) ++ { ++ if (/^\s*$/) ++ { push(@out,"\n"); } ++ else ++ { push(@out,"\t$com_start $_ $com_end\n"); } ++ } ++ } ++ ++sub main'label ++ { ++ if (!defined($label{$_[0]})) ++ { ++ $label{$_[0]}=".${label}${_[0]}"; ++ $label++; ++ } ++ return($label{$_[0]}); ++ } ++ ++sub main'set_label ++ { ++ if (!defined($label{$_[0]})) ++ { ++ $label{$_[0]}=".${label}${_[0]}"; ++ $label++; ++ } ++ push(@out,".align $align\n") if ($_[1] != 0); ++ push(@out,"$label{$_[0]}:\n"); ++ } ++ ++sub main'file_end ++ { ++ } ++ ++sub main'data_word ++ { ++ push(@out,"\t.long $_[0]\n"); ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/asm/readme Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,131 @@ ++First up, let me say I don't like writing in assembler. It is not portable, ++dependant on the particular CPU architecture release and is generally a pig ++to debug and get right. Having said that, the x86 architecture is probably ++the most important for speed due to number of boxes and since ++it appears to be the worst architecture to to get ++good C compilers for. So due to this, I have lowered myself to do ++assembler for the inner DES routines in libdes :-). ++ ++The file to implement in assembler is des_enc.c. Replace the following ++4 functions ++des_encrypt(DES_LONG data[2],des_key_schedule ks, int encrypt); ++des_encrypt2(DES_LONG data[2],des_key_schedule ks, int encrypt); ++des_encrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3); ++des_decrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3); ++ ++They encrypt/decrypt the 64 bits held in 'data' using ++the 'ks' key schedules. The only difference between the 4 functions is that ++des_encrypt2() does not perform IP() or FP() on the data (this is an ++optimization for when doing triple DES and des_encrypt3() and des_decrypt3() ++perform triple des. The triple DES routines are in here because it does ++make a big difference to have them located near the des_encrypt2 function ++at link time.. ++ ++Now as we all know, there are lots of different operating systems running on ++x86 boxes, and unfortunately they normally try to make sure their assembler ++formating is not the same as the other peoples. ++The 4 main formats I know of are ++Microsoft Windows 95/Windows NT ++Elf Includes Linux and FreeBSD(?). ++a.out The older Linux. ++Solaris Same as Elf but different comments :-(. ++ ++Now I was not overly keen to write 4 different copies of the same code, ++so I wrote a few perl routines to output the correct assembler, given ++a target assembler type. This code is ugly and is just a hack. ++The libraries are x86unix.pl and x86ms.pl. ++des586.pl, des686.pl and des-som[23].pl are the programs to actually ++generate the assembler. ++ ++So to generate elf assembler ++perl des-som3.pl elf >dx86-elf.s ++For Windows 95/NT ++perl des-som2.pl win32 >win32.asm ++ ++[ update 4 Jan 1996 ] ++I have added another way to do things. ++perl des-som3.pl cpp >dx86-cpp.s ++generates a file that will be included by dx86unix.cpp when it is compiled. ++To build for elf, a.out, solaris, bsdi etc, ++cc -E -DELF asm/dx86unix.cpp | as -o asm/dx86-elf.o ++cc -E -DSOL asm/dx86unix.cpp | as -o asm/dx86-sol.o ++cc -E -DOUT asm/dx86unix.cpp | as -o asm/dx86-out.o ++cc -E -DBSDI asm/dx86unix.cpp | as -o asm/dx86bsdi.o ++This was done to cut down the number of files in the distribution. ++ ++Now the ugly part. I acquired my copy of Intels ++"Optimization's For Intel's 32-Bit Processors" and found a few interesting ++things. First, the aim of the exersize is to 'extract' one byte at a time ++from a word and do an array lookup. This involves getting the byte from ++the 4 locations in the word and moving it to a new word and doing the lookup. ++The most obvious way to do this is ++xor eax, eax # clear word ++movb al, cl # get low byte ++xor edi DWORD PTR 0x100+des_SP[eax] # xor in word ++movb al, ch # get next byte ++xor edi DWORD PTR 0x300+des_SP[eax] # xor in word ++shr ecx 16 ++which seems ok. For the pentium, this system appears to be the best. ++One has to do instruction interleaving to keep both functional units ++operating, but it is basically very efficient. ++ ++Now the crunch. When a full register is used after a partial write, eg. ++mov al, cl ++xor edi, DWORD PTR 0x100+des_SP[eax] ++386 - 1 cycle stall ++486 - 1 cycle stall ++586 - 0 cycle stall ++686 - at least 7 cycle stall (page 22 of the above mentioned document). ++ ++So the technique that produces the best results on a pentium, according to ++the documentation, will produce hideous results on a pentium pro. ++ ++To get around this, des686.pl will generate code that is not as fast on ++a pentium, should be very good on a pentium pro. ++mov eax, ecx # copy word ++shr ecx, 8 # line up next byte ++and eax, 0fch # mask byte ++xor edi DWORD PTR 0x100+des_SP[eax] # xor in array lookup ++mov eax, ecx # get word ++shr ecx 8 # line up next byte ++and eax, 0fch # mask byte ++xor edi DWORD PTR 0x300+des_SP[eax] # xor in array lookup ++ ++Due to the execution units in the pentium, this actually works quite well. ++For a pentium pro it should be very good. This is the type of output ++Visual C++ generates. ++ ++There is a third option. instead of using ++mov al, ch ++which is bad on the pentium pro, one may be able to use ++movzx eax, ch ++which may not incur the partial write penalty. On the pentium, ++this instruction takes 4 cycles so is not worth using but on the ++pentium pro it appears it may be worth while. I need access to one to ++experiment :-). ++ ++eric (20 Oct 1996) ++ ++22 Nov 1996 - I have asked people to run the 2 different version on pentium ++pros and it appears that the intel documentation is wrong. The ++mov al,bh is still faster on a pentium pro, so just use the des586.pl ++install des686.pl ++ ++3 Dec 1996 - I added des_encrypt3/des_decrypt3 because I have moved these ++functions into des_enc.c because it does make a massive performance ++difference on some boxes to have the functions code located close to ++the des_encrypt2() function. ++ ++9 Jan 1997 - des-som2.pl is now the correct perl script to use for ++pentiums. It contains an inner loop from ++Svend Olaf Mikkelsen which does raw ecb DES calls at ++273,000 per second. He had a previous version at 250,000 and the best ++I was able to get was 203,000. The content has not changed, this is all ++due to instruction sequencing (and actual instructions choice) which is able ++to keep both functional units of the pentium going. ++We may have lost the ugly register usage restrictions when x86 went 32 bit ++but for the pentium it has been replaced by evil instruction ordering tricks. ++ ++13 Jan 1997 - des-som3.pl, more optimizations from Svend Olaf. ++raw DES at 281,000 per second on a pentium 100. ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/cbc_enc.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,135 @@ ++/* crypto/des/cbc_enc.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++#include "des_locl.h" ++ ++void des_cbc_encrypt(input, output, length, schedule, ivec, enc) ++des_cblock (*input); ++des_cblock (*output); ++long length; ++des_key_schedule schedule; ++des_cblock (*ivec); ++int enc; ++ { ++ register DES_LONG tin0,tin1; ++ register DES_LONG tout0,tout1,xor0,xor1; ++ register unsigned char *in,*out; ++ register long l=length; ++ DES_LONG tin[2]; ++ unsigned char *iv; ++ ++ in=(unsigned char *)input; ++ out=(unsigned char *)output; ++ iv=(unsigned char *)ivec; ++ ++ if (enc) ++ { ++ c2l(iv,tout0); ++ c2l(iv,tout1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); ++ c2l(in,tin1); ++ tin0^=tout0; tin[0]=tin0; ++ tin1^=tout1; tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); ++ tout0=tin[0]; l2c(tout0,out); ++ tout1=tin[1]; l2c(tout1,out); ++ } ++ if (l != -8) ++ { ++ c2ln(in,tin0,tin1,l+8); ++ tin0^=tout0; tin[0]=tin0; ++ tin1^=tout1; tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); ++ tout0=tin[0]; l2c(tout0,out); ++ tout1=tin[1]; l2c(tout1,out); ++ } ++ } ++ else ++ { ++ c2l(iv,xor0); ++ c2l(iv,xor1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); tin[0]=tin0; ++ c2l(in,tin1); tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); ++ tout0=tin[0]^xor0; ++ tout1=tin[1]^xor1; ++ l2c(tout0,out); ++ l2c(tout1,out); ++ xor0=tin0; ++ xor1=tin1; ++ } ++ if (l != -8) ++ { ++ c2l(in,tin0); tin[0]=tin0; ++ c2l(in,tin1); tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); ++ tout0=tin[0]^xor0; ++ tout1=tin[1]^xor1; ++ l2cn(tout0,tout1,out,l+8); ++ /* xor0=tin0; ++ xor1=tin1; */ ++ } ++ } ++ tin0=tin1=tout0=tout1=xor0=xor1=0; ++ tin[0]=tin[1]=0; ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des.doc Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,505 @@ ++The DES library. ++ ++Please note that this library was originally written to operate with ++eBones, a version of Kerberos that had had encryption removed when it left ++the USA and then put back in. As such there are some routines that I will ++advise not using but they are still in the library for historical reasons. ++For all calls that have an 'input' and 'output' variables, they can be the ++same. ++ ++This library requires the inclusion of 'des.h'. ++ ++All of the encryption functions take what is called a des_key_schedule as an ++argument. A des_key_schedule is an expanded form of the des key. ++A des_key is 8 bytes of odd parity, the type used to hold the key is a ++des_cblock. A des_cblock is an array of 8 bytes, often in this library ++description I will refer to input bytes when the function specifies ++des_cblock's as input or output, this just means that the variable should ++be a multiple of 8 bytes. ++ ++The define DES_ENCRYPT is passed to specify encryption, DES_DECRYPT to ++specify decryption. The functions and global variable are as follows: ++ ++int des_check_key; ++ DES keys are supposed to be odd parity. If this variable is set to ++ a non-zero value, des_set_key() will check that the key has odd ++ parity and is not one of the known weak DES keys. By default this ++ variable is turned off; ++ ++void des_set_odd_parity( ++des_cblock *key ); ++ This function takes a DES key (8 bytes) and sets the parity to odd. ++ ++int des_is_weak_key( ++des_cblock *key ); ++ This function returns a non-zero value if the DES key passed is a ++ weak, DES key. If it is a weak key, don't use it, try a different ++ one. If you are using 'random' keys, the chances of hitting a weak ++ key are 1/2^52 so it is probably not worth checking for them. ++ ++int des_set_key( ++des_cblock *key, ++des_key_schedule schedule); ++ Des_set_key converts an 8 byte DES key into a des_key_schedule. ++ A des_key_schedule is an expanded form of the key which is used to ++ perform actual encryption. It can be regenerated from the DES key ++ so it only needs to be kept when encryption or decryption is about ++ to occur. Don't save or pass around des_key_schedule's since they ++ are CPU architecture dependent, DES keys are not. If des_check_key ++ is non zero, zero is returned if the key has the wrong parity or ++ the key is a weak key, else 1 is returned. ++ ++int des_key_sched( ++des_cblock *key, ++des_key_schedule schedule); ++ An alternative name for des_set_key(). ++ ++int des_rw_mode; /* defaults to DES_PCBC_MODE */ ++ This flag holds either DES_CBC_MODE or DES_PCBC_MODE (default). ++ This specifies the function to use in the enc_read() and enc_write() ++ functions. ++ ++void des_encrypt( ++unsigned long *data, ++des_key_schedule ks, ++int enc); ++ This is the DES encryption function that gets called by just about ++ every other DES routine in the library. You should not use this ++ function except to implement 'modes' of DES. I say this because the ++ functions that call this routine do the conversion from 'char *' to ++ long, and this needs to be done to make sure 'non-aligned' memory ++ access do not occur. The characters are loaded 'little endian', ++ have a look at my source code for more details on how I use this ++ function. ++ Data is a pointer to 2 unsigned long's and ks is the ++ des_key_schedule to use. enc, is non zero specifies encryption, ++ zero if decryption. ++ ++void des_encrypt2( ++unsigned long *data, ++des_key_schedule ks, ++int enc); ++ This functions is the same as des_encrypt() except that the DES ++ initial permutation (IP) and final permutation (FP) have been left ++ out. As for des_encrypt(), you should not use this function. ++ It is used by the routines in my library that implement triple DES. ++ IP() des_encrypt2() des_encrypt2() des_encrypt2() FP() is the same ++ as des_encrypt() des_encrypt() des_encrypt() except faster :-). ++ ++void des_ecb_encrypt( ++des_cblock *input, ++des_cblock *output, ++des_key_schedule ks, ++int enc); ++ This is the basic Electronic Code Book form of DES, the most basic ++ form. Input is encrypted into output using the key represented by ++ ks. If enc is non zero (DES_ENCRYPT), encryption occurs, otherwise ++ decryption occurs. Input is 8 bytes long and output is 8 bytes. ++ (the des_cblock structure is 8 chars). ++ ++void des_ecb3_encrypt( ++des_cblock *input, ++des_cblock *output, ++des_key_schedule ks1, ++des_key_schedule ks2, ++des_key_schedule ks3, ++int enc); ++ This is the 3 key EDE mode of ECB DES. What this means is that ++ the 8 bytes of input is encrypted with ks1, decrypted with ks2 and ++ then encrypted again with ks3, before being put into output; ++ C=E(ks3,D(ks2,E(ks1,M))). There is a macro, des_ecb2_encrypt() ++ that only takes 2 des_key_schedules that implements, ++ C=E(ks1,D(ks2,E(ks1,M))) in that the final encrypt is done with ks1. ++ ++void des_cbc_encrypt( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule ks, ++des_cblock *ivec, ++int enc); ++ This routine implements DES in Cipher Block Chaining mode. ++ Input, which should be a multiple of 8 bytes is encrypted ++ (or decrypted) to output which will also be a multiple of 8 bytes. ++ The number of bytes is in length (and from what I've said above, ++ should be a multiple of 8). If length is not a multiple of 8, I'm ++ not being held responsible :-). ivec is the initialisation vector. ++ This function does not modify this variable. To correctly implement ++ cbc mode, you need to do one of 2 things; copy the last 8 bytes of ++ cipher text for use as the next ivec in your application, ++ or use des_ncbc_encrypt(). ++ Only this routine has this problem with updating the ivec, all ++ other routines that are implementing cbc mode update ivec. ++ ++void des_ncbc_encrypt( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule sk, ++des_cblock *ivec, ++int enc); ++ For historical reasons, des_cbc_encrypt() did not update the ++ ivec with the value requires so that subsequent calls to ++ des_cbc_encrypt() would 'chain'. This was needed so that the same ++ 'length' values would not need to be used when decrypting. ++ des_ncbc_encrypt() does the right thing. It is the same as ++ des_cbc_encrypt accept that ivec is updates with the correct value ++ to pass in subsequent calls to des_ncbc_encrypt(). I advise using ++ des_ncbc_encrypt() instead of des_cbc_encrypt(); ++ ++void des_xcbc_encrypt( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule sk, ++des_cblock *ivec, ++des_cblock *inw, ++des_cblock *outw, ++int enc); ++ This is RSA's DESX mode of DES. It uses inw and outw to ++ 'whiten' the encryption. inw and outw are secret (unlike the iv) ++ and are as such, part of the key. So the key is sort of 24 bytes. ++ This is much better than cbc des. ++ ++void des_3cbc_encrypt( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule sk1, ++des_key_schedule sk2, ++des_cblock *ivec1, ++des_cblock *ivec2, ++int enc); ++ This function is flawed, do not use it. I have left it in the ++ library because it is used in my des(1) program and will function ++ correctly when used by des(1). If I removed the function, people ++ could end up unable to decrypt files. ++ This routine implements outer triple cbc encryption using 2 ks and ++ 2 ivec's. Use des_ede2_cbc_encrypt() instead. ++ ++void 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); ++ This function implements inner triple CBC DES encryption with 3 ++ keys. What this means is that each 'DES' operation ++ inside the cbc mode is really an C=E(ks3,D(ks2,E(ks1,M))). ++ Again, this is cbc mode so an ivec is requires. ++ This mode is used by SSL. ++ There is also a des_ede2_cbc_encrypt() that only uses 2 ++ des_key_schedule's, the first being reused for the final ++ encryption. C=E(ks1,D(ks2,E(ks1,M))). This form of triple DES ++ is used by the RSAref library. ++ ++void des_pcbc_encrypt( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule ks, ++des_cblock *ivec, ++int enc); ++ This is Propagating Cipher Block Chaining mode of DES. It is used ++ by Kerberos v4. It's parameters are the same as des_ncbc_encrypt(). ++ ++void des_cfb_encrypt( ++unsigned char *in, ++unsigned char *out, ++int numbits, ++long length, ++des_key_schedule ks, ++des_cblock *ivec, ++int enc); ++ Cipher Feedback Back mode of DES. This implementation 'feeds back' ++ in numbit blocks. The input (and output) is in multiples of numbits ++ bits. numbits should to be a multiple of 8 bits. Length is the ++ number of bytes input. If numbits is not a multiple of 8 bits, ++ the extra bits in the bytes will be considered padding. So if ++ numbits is 12, for each 2 input bytes, the 4 high bits of the ++ second byte will be ignored. So to encode 72 bits when using ++ a numbits of 12 take 12 bytes. To encode 72 bits when using ++ numbits of 9 will take 16 bytes. To encode 80 bits when using ++ numbits of 16 will take 10 bytes. etc, etc. This padding will ++ apply to both input and output. ++ ++ ++void des_cfb64_encrypt( ++unsigned char *in, ++unsigned char *out, ++long length, ++des_key_schedule ks, ++des_cblock *ivec, ++int *num, ++int enc); ++ This is one of the more useful functions in this DES library, it ++ implements CFB mode of DES with 64bit feedback. Why is this ++ useful you ask? Because this routine will allow you to encrypt an ++ arbitrary number of bytes, no 8 byte padding. Each call to this ++ routine will encrypt the input bytes to output and then update ivec ++ and num. num contains 'how far' we are though ivec. If this does ++ not make much sense, read more about cfb mode of DES :-). ++ ++void des_ede3_cfb64_encrypt( ++unsigned char *in, ++unsigned char *out, ++long length, ++des_key_schedule ks1, ++des_key_schedule ks2, ++des_key_schedule ks3, ++des_cblock *ivec, ++int *num, ++int enc); ++ Same as des_cfb64_encrypt() accept that the DES operation is ++ triple DES. As usual, there is a macro for ++ des_ede2_cfb64_encrypt() which reuses ks1. ++ ++void des_ofb_encrypt( ++unsigned char *in, ++unsigned char *out, ++int numbits, ++long length, ++des_key_schedule ks, ++des_cblock *ivec); ++ This is a implementation of Output Feed Back mode of DES. It is ++ the same as des_cfb_encrypt() in that numbits is the size of the ++ units dealt with during input and output (in bits). ++ ++void des_ofb64_encrypt( ++unsigned char *in, ++unsigned char *out, ++long length, ++des_key_schedule ks, ++des_cblock *ivec, ++int *num); ++ The same as des_cfb64_encrypt() except that it is Output Feed Back ++ mode. ++ ++void des_ede3_ofb64_encrypt( ++unsigned char *in, ++unsigned char *out, ++long length, ++des_key_schedule ks1, ++des_key_schedule ks2, ++des_key_schedule ks3, ++des_cblock *ivec, ++int *num); ++ Same as des_ofb64_encrypt() accept that the DES operation is ++ triple DES. As usual, there is a macro for ++ des_ede2_ofb64_encrypt() which reuses ks1. ++ ++int des_read_pw_string( ++char *buf, ++int length, ++char *prompt, ++int verify); ++ This routine is used to get a password from the terminal with echo ++ turned off. Buf is where the string will end up and length is the ++ size of buf. Prompt is a string presented to the 'user' and if ++ verify is set, the key is asked for twice and unless the 2 copies ++ match, an error is returned. A return code of -1 indicates a ++ system error, 1 failure due to use interaction, and 0 is success. ++ ++unsigned long des_cbc_cksum( ++des_cblock *input, ++des_cblock *output, ++long length, ++des_key_schedule ks, ++des_cblock *ivec); ++ This function produces an 8 byte checksum from input that it puts in ++ output and returns the last 4 bytes as a long. The checksum is ++ generated via cbc mode of DES in which only the last 8 byes are ++ kept. I would recommend not using this function but instead using ++ the EVP_Digest routines, or at least using MD5 or SHA. This ++ function is used by Kerberos v4 so that is why it stays in the ++ library. ++ ++char *des_fcrypt( ++const char *buf, ++const char *salt ++char *ret); ++ This is my fast version of the unix crypt(3) function. This version ++ takes only a small amount of space relative to other fast ++ crypt() implementations. This is different to the normal crypt ++ in that the third parameter is the buffer that the return value ++ is written into. It needs to be at least 14 bytes long. This ++ function is thread safe, unlike the normal crypt. ++ ++char *crypt( ++const char *buf, ++const char *salt); ++ This function calls des_fcrypt() with a static array passed as the ++ third parameter. This emulates the normal non-thread safe semantics ++ of crypt(3). ++ ++void des_string_to_key( ++char *str, ++des_cblock *key); ++ This function takes str and converts it into a DES key. I would ++ recommend using MD5 instead and use the first 8 bytes of output. ++ When I wrote the first version of these routines back in 1990, MD5 ++ did not exist but I feel these routines are still sound. This ++ routines is compatible with the one in MIT's libdes. ++ ++void des_string_to_2keys( ++char *str, ++des_cblock *key1, ++des_cblock *key2); ++ This function takes str and converts it into 2 DES keys. ++ I would recommend using MD5 and using the 16 bytes as the 2 keys. ++ I have nothing against these 2 'string_to_key' routines, it's just ++ that if you say that your encryption key is generated by using the ++ 16 bytes of an MD5 hash, every-one knows how you generated your ++ keys. ++ ++int des_read_password( ++des_cblock *key, ++char *prompt, ++int verify); ++ This routine combines des_read_pw_string() with des_string_to_key(). ++ ++int des_read_2passwords( ++des_cblock *key1, ++des_cblock *key2, ++char *prompt, ++int verify); ++ This routine combines des_read_pw_string() with des_string_to_2key(). ++ ++void des_random_seed( ++des_cblock key); ++ This routine sets a starting point for des_random_key(). ++ ++void des_random_key( ++des_cblock ret); ++ This function return a random key. Make sure to 'seed' the random ++ number generator (with des_random_seed()) before using this function. ++ I personally now use a MD5 based random number system. ++ ++int des_enc_read( ++int fd, ++char *buf, ++int len, ++des_key_schedule ks, ++des_cblock *iv); ++ This function will write to a file descriptor the encrypted data ++ from buf. This data will be preceded by a 4 byte 'byte count' and ++ will be padded out to 8 bytes. The encryption is either CBC of ++ PCBC depending on the value of des_rw_mode. If it is DES_PCBC_MODE, ++ pcbc is used, if DES_CBC_MODE, cbc is used. The default is to use ++ DES_PCBC_MODE. ++ ++int des_enc_write( ++int fd, ++char *buf, ++int len, ++des_key_schedule ks, ++des_cblock *iv); ++ This routines read stuff written by des_enc_read() and decrypts it. ++ I have used these routines quite a lot but I don't believe they are ++ suitable for non-blocking io. If you are after a full ++ authentication/encryption over networks, have a look at SSL instead. ++ ++unsigned long des_quad_cksum( ++des_cblock *input, ++des_cblock *output, ++long length, ++int out_count, ++des_cblock *seed); ++ This is a function from Kerberos v4 that is not anything to do with ++ DES but was needed. It is a cksum that is quicker to generate than ++ des_cbc_cksum(); I personally would use MD5 routines now. ++===== ++Modes of DES ++Quite a bit of the following information has been taken from ++ AS 2805.5.2 ++ Australian Standard ++ Electronic funds transfer - Requirements for interfaces, ++ Part 5.2: Modes of operation for an n-bit block cipher algorithm ++ Appendix A ++ ++There are several different modes in which DES can be used, they are ++as follows. ++ ++Electronic Codebook Mode (ECB) (des_ecb_encrypt()) ++- 64 bits are enciphered at a time. ++- The order of the blocks can be rearranged without detection. ++- The same plaintext block always produces the same ciphertext block ++ (for the same key) making it vulnerable to a 'dictionary attack'. ++- An error will only affect one ciphertext block. ++ ++Cipher Block Chaining Mode (CBC) (des_cbc_encrypt()) ++- a multiple of 64 bits are enciphered at a time. ++- The CBC mode produces the same ciphertext whenever the same ++ plaintext is encrypted using the same key and starting variable. ++- The chaining operation makes the ciphertext blocks dependent on the ++ current and all preceding plaintext blocks and therefore blocks can not ++ be rearranged. ++- The use of different starting variables prevents the same plaintext ++ enciphering to the same ciphertext. ++- An error will affect the current and the following ciphertext blocks. ++ ++Cipher Feedback Mode (CFB) (des_cfb_encrypt()) ++- a number of bits (j) <= 64 are enciphered at a time. ++- The CFB mode produces the same ciphertext whenever the same ++ plaintext is encrypted using the same key and starting variable. ++- The chaining operation makes the ciphertext variables dependent on the ++ current and all preceding variables and therefore j-bit variables are ++ chained together and can not be rearranged. ++- The use of different starting variables prevents the same plaintext ++ enciphering to the same ciphertext. ++- The strength of the CFB mode depends on the size of k (maximal if ++ j == k). In my implementation this is always the case. ++- Selection of a small value for j will require more cycles through ++ the encipherment algorithm per unit of plaintext and thus cause ++ greater processing overheads. ++- Only multiples of j bits can be enciphered. ++- An error will affect the current and the following ciphertext variables. ++ ++Output Feedback Mode (OFB) (des_ofb_encrypt()) ++- a number of bits (j) <= 64 are enciphered at a time. ++- The OFB mode produces the same ciphertext whenever the same ++ plaintext enciphered using the same key and starting variable. More ++ over, in the OFB mode the same key stream is produced when the same ++ key and start variable are used. Consequently, for security reasons ++ a specific start variable should be used only once for a given key. ++- The absence of chaining makes the OFB more vulnerable to specific attacks. ++- The use of different start variables values prevents the same ++ plaintext enciphering to the same ciphertext, by producing different ++ key streams. ++- Selection of a small value for j will require more cycles through ++ the encipherment algorithm per unit of plaintext and thus cause ++ greater processing overheads. ++- Only multiples of j bits can be enciphered. ++- OFB mode of operation does not extend ciphertext errors in the ++ resultant plaintext output. Every bit error in the ciphertext causes ++ only one bit to be in error in the deciphered plaintext. ++- OFB mode is not self-synchronising. If the two operation of ++ encipherment and decipherment get out of synchronism, the system needs ++ to be re-initialised. ++- Each re-initialisation should use a value of the start variable ++ different from the start variable values used before with the same ++ key. The reason for this is that an identical bit stream would be ++ produced each time from the same parameters. This would be ++ susceptible to a ' known plaintext' attack. ++ ++Triple ECB Mode (des_ecb3_encrypt()) ++- Encrypt with key1, decrypt with key2 and encrypt with key3 again. ++- As for ECB encryption but increases the key length to 168 bits. ++ There are theoretic attacks that can be used that make the effective ++ key length 112 bits, but this attack also requires 2^56 blocks of ++ memory, not very likely, even for the NSA. ++- If both keys are the same it is equivalent to encrypting once with ++ just one key. ++- If the first and last key are the same, the key length is 112 bits. ++ There are attacks that could reduce the key space to 55 bit's but it ++ requires 2^56 blocks of memory. ++- If all 3 keys are the same, this is effectively the same as normal ++ ecb mode. ++ ++Triple CBC Mode (des_ede3_cbc_encrypt()) ++- Encrypt with key1, decrypt with key2 and then encrypt with key3. ++- As for CBC encryption but increases the key length to 168 bits with ++ the same restrictions as for triple ecb mode. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des_crypt.man Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,508 @@ ++.TH DES_CRYPT 3 ++.SH NAME ++des_read_password, des_read_2password, ++des_string_to_key, des_string_to_2key, des_read_pw_string, ++des_random_key, des_set_key, ++des_key_sched, des_ecb_encrypt, des_ecb3_encrypt, des_cbc_encrypt, ++des_3cbc_encrypt, ++des_pcbc_encrypt, des_cfb_encrypt, des_ofb_encrypt, ++des_cbc_cksum, des_quad_cksum, ++des_enc_read, des_enc_write, des_set_odd_parity, ++des_is_weak_key, crypt \- (non USA) DES encryption ++.SH SYNOPSIS ++.nf ++.nj ++.ft B ++#include ++.PP ++.B int des_read_password(key,prompt,verify) ++des_cblock *key; ++char *prompt; ++int verify; ++.PP ++.B int des_read_2password(key1,key2,prompt,verify) ++des_cblock *key1,*key2; ++char *prompt; ++int verify; ++.PP ++.B int des_string_to_key(str,key) ++char *str; ++des_cblock *key; ++.PP ++.B int des_string_to_2keys(str,key1,key2) ++char *str; ++des_cblock *key1,*key2; ++.PP ++.B int des_read_pw_string(buf,length,prompt,verify) ++char *buf; ++int length; ++char *prompt; ++int verify; ++.PP ++.B int des_random_key(key) ++des_cblock *key; ++.PP ++.B int des_set_key(key,schedule) ++des_cblock *key; ++des_key_schedule schedule; ++.PP ++.B int des_key_sched(key,schedule) ++des_cblock *key; ++des_key_schedule schedule; ++.PP ++.B int des_ecb_encrypt(input,output,schedule,encrypt) ++des_cblock *input; ++des_cblock *output; ++des_key_schedule schedule; ++int encrypt; ++.PP ++.B int des_ecb3_encrypt(input,output,ks1,ks2,encrypt) ++des_cblock *input; ++des_cblock *output; ++des_key_schedule ks1,ks2; ++int encrypt; ++.PP ++.B int des_cbc_encrypt(input,output,length,schedule,ivec,encrypt) ++des_cblock *input; ++des_cblock *output; ++long length; ++des_key_schedule schedule; ++des_cblock *ivec; ++int encrypt; ++.PP ++.B int des_3cbc_encrypt(input,output,length,sk1,sk2,ivec1,ivec2,encrypt) ++des_cblock *input; ++des_cblock *output; ++long length; ++des_key_schedule sk1; ++des_key_schedule sk2; ++des_cblock *ivec1; ++des_cblock *ivec2; ++int encrypt; ++.PP ++.B int des_pcbc_encrypt(input,output,length,schedule,ivec,encrypt) ++des_cblock *input; ++des_cblock *output; ++long length; ++des_key_schedule schedule; ++des_cblock *ivec; ++int encrypt; ++.PP ++.B int des_cfb_encrypt(input,output,numbits,length,schedule,ivec,encrypt) ++unsigned char *input; ++unsigned char *output; ++int numbits; ++long length; ++des_key_schedule schedule; ++des_cblock *ivec; ++int encrypt; ++.PP ++.B int des_ofb_encrypt(input,output,numbits,length,schedule,ivec) ++unsigned char *input,*output; ++int numbits; ++long length; ++des_key_schedule schedule; ++des_cblock *ivec; ++.PP ++.B unsigned long des_cbc_cksum(input,output,length,schedule,ivec) ++des_cblock *input; ++des_cblock *output; ++long length; ++des_key_schedule schedule; ++des_cblock *ivec; ++.PP ++.B unsigned long des_quad_cksum(input,output,length,out_count,seed) ++des_cblock *input; ++des_cblock *output; ++long length; ++int out_count; ++des_cblock *seed; ++.PP ++.B int des_check_key; ++.PP ++.B int des_enc_read(fd,buf,len,sched,iv) ++int fd; ++char *buf; ++int len; ++des_key_schedule sched; ++des_cblock *iv; ++.PP ++.B int des_enc_write(fd,buf,len,sched,iv) ++int fd; ++char *buf; ++int len; ++des_key_schedule sched; ++des_cblock *iv; ++.PP ++.B extern int des_rw_mode; ++.PP ++.B void des_set_odd_parity(key) ++des_cblock *key; ++.PP ++.B int des_is_weak_key(key) ++des_cblock *key; ++.PP ++.B char *crypt(passwd,salt) ++char *passwd; ++char *salt; ++.PP ++.fi ++.SH DESCRIPTION ++This library contains a fast implementation of the DES encryption ++algorithm. ++.PP ++There are two phases to the use of DES encryption. ++The first is the generation of a ++.I des_key_schedule ++from a key, ++the second is the actual encryption. ++A des key is of type ++.I des_cblock. ++This type is made from 8 characters with odd parity. ++The least significant bit in the character is the parity bit. ++The key schedule is an expanded form of the key; it is used to speed the ++encryption process. ++.PP ++.I des_read_password ++writes the string specified by prompt to the standard output, ++turns off echo and reads an input string from standard input ++until terminated with a newline. ++If verify is non-zero, it prompts and reads the input again and verifies ++that both entered passwords are the same. ++The entered string is converted into a des key by using the ++.I des_string_to_key ++routine. ++The new key is placed in the ++.I des_cblock ++that was passed (by reference) to the routine. ++If there were no errors, ++.I des_read_password ++returns 0, ++-1 is returned if there was a terminal error and 1 is returned for ++any other error. ++.PP ++.I des_read_2password ++operates in the same way as ++.I des_read_password ++except that it generates 2 keys by using the ++.I des_string_to_2key ++function. ++.PP ++.I des_read_pw_string ++is called by ++.I des_read_password ++to read and verify a string from a terminal device. ++The string is returned in ++.I buf. ++The size of ++.I buf ++is passed to the routine via the ++.I length ++parameter. ++.PP ++.I des_string_to_key ++converts a string into a valid des key. ++.PP ++.I des_string_to_2key ++converts a string into 2 valid des keys. ++This routine is best suited for used to generate keys for use with ++.I des_ecb3_encrypt. ++.PP ++.I des_random_key ++returns a random key that is made of a combination of process id, ++time and an increasing counter. ++.PP ++Before a des key can be used it is converted into a ++.I des_key_schedule ++via the ++.I des_set_key ++routine. ++If the ++.I des_check_key ++flag is non-zero, ++.I des_set_key ++will check that the key passed is of odd parity and is not a week or ++semi-weak key. ++If the parity is wrong, ++then -1 is returned. ++If the key is a weak key, ++then -2 is returned. ++If an error is returned, ++the key schedule is not generated. ++.PP ++.I des_key_sched ++is another name for the ++.I des_set_key ++function. ++.PP ++The following routines mostly operate on an input and output stream of ++.I des_cblock's. ++.PP ++.I des_ecb_encrypt ++is the basic DES encryption routine that encrypts or decrypts a single 8-byte ++.I des_cblock ++in ++.I electronic code book ++mode. ++It always transforms the input data, pointed to by ++.I input, ++into the output data, ++pointed to by the ++.I output ++argument. ++If the ++.I encrypt ++argument is non-zero (DES_ENCRYPT), ++the ++.I input ++(cleartext) is encrypted in to the ++.I output ++(ciphertext) using the key_schedule specified by the ++.I schedule ++argument, ++previously set via ++.I des_set_key. ++If ++.I encrypt ++is zero (DES_DECRYPT), ++the ++.I input ++(now ciphertext) ++is decrypted into the ++.I output ++(now cleartext). ++Input and output may overlap. ++No meaningful value is returned. ++.PP ++.I des_ecb3_encrypt ++encrypts/decrypts the ++.I input ++block by using triple ecb DES encryption. ++This involves encrypting the input with ++.I ks1, ++decryption with the key schedule ++.I ks2, ++and then encryption with the first again. ++This routine greatly reduces the chances of brute force breaking of ++DES and has the advantage of if ++.I ks1 ++and ++.I ks2 ++are the same, it is equivalent to just encryption using ecb mode and ++.I ks1 ++as the key. ++.PP ++.I des_cbc_encrypt ++encrypts/decrypts using the ++.I cipher-block-chaining ++mode of DES. ++If the ++.I encrypt ++argument is non-zero, ++the routine cipher-block-chain encrypts the cleartext data pointed to by the ++.I input ++argument into the ciphertext pointed to by the ++.I output ++argument, ++using the key schedule provided by the ++.I schedule ++argument, ++and initialisation vector provided by the ++.I ivec ++argument. ++If the ++.I length ++argument is not an integral multiple of eight bytes, ++the last block is copied to a temporary area and zero filled. ++The output is always ++an integral multiple of eight bytes. ++To make multiple cbc encrypt calls on a large amount of data appear to ++be one ++.I des_cbc_encrypt ++call, the ++.I ivec ++of subsequent calls should be the last 8 bytes of the output. ++.PP ++.I des_3cbc_encrypt ++encrypts/decrypts the ++.I input ++block by using triple cbc DES encryption. ++This involves encrypting the input with key schedule ++.I ks1, ++decryption with the key schedule ++.I ks2, ++and then encryption with the first again. ++2 initialisation vectors are required, ++.I ivec1 ++and ++.I ivec2. ++Unlike ++.I des_cbc_encrypt, ++these initialisation vectors are modified by the subroutine. ++This routine greatly reduces the chances of brute force breaking of ++DES and has the advantage of if ++.I ks1 ++and ++.I ks2 ++are the same, it is equivalent to just encryption using cbc mode and ++.I ks1 ++as the key. ++.PP ++.I des_pcbc_encrypt ++encrypt/decrypts using a modified block chaining mode. ++It provides better error propagation characteristics than cbc ++encryption. ++.PP ++.I des_cfb_encrypt ++encrypt/decrypts using cipher feedback mode. This method takes an ++array of characters as input and outputs and array of characters. It ++does not require any padding to 8 character groups. Note: the ivec ++variable is changed and the new changed value needs to be passed to ++the next call to this function. Since this function runs a complete ++DES ecb encryption per numbits, this function is only suggested for ++use when sending small numbers of characters. ++.PP ++.I des_ofb_encrypt ++encrypt using output feedback mode. This method takes an ++array of characters as input and outputs and array of characters. It ++does not require any padding to 8 character groups. Note: the ivec ++variable is changed and the new changed value needs to be passed to ++the next call to this function. Since this function runs a complete ++DES ecb encryption per numbits, this function is only suggested for ++use when sending small numbers of characters. ++.PP ++.I des_cbc_cksum ++produces an 8 byte checksum based on the input stream (via cbc encryption). ++The last 4 bytes of the checksum is returned and the complete 8 bytes is ++placed in ++.I output. ++.PP ++.I des_quad_cksum ++returns a 4 byte checksum from the input bytes. ++The algorithm can be iterated over the input, ++depending on ++.I out_count, ++1, 2, 3 or 4 times. ++If ++.I output ++is non-NULL, ++the 8 bytes generated by each pass are written into ++.I output. ++.PP ++.I des_enc_write ++is used to write ++.I len ++bytes ++to file descriptor ++.I fd ++from buffer ++.I buf. ++The data is encrypted via ++.I pcbc_encrypt ++(default) using ++.I sched ++for the key and ++.I iv ++as a starting vector. ++The actual data send down ++.I fd ++consists of 4 bytes (in network byte order) containing the length of the ++following encrypted data. The encrypted data then follows, padded with random ++data out to a multiple of 8 bytes. ++.PP ++.I des_enc_read ++is used to read ++.I len ++bytes ++from file descriptor ++.I fd ++into buffer ++.I buf. ++The data being read from ++.I fd ++is assumed to have come from ++.I des_enc_write ++and is decrypted using ++.I sched ++for the key schedule and ++.I iv ++for the initial vector. ++The ++.I des_enc_read/des_enc_write ++pair can be used to read/write to files, pipes and sockets. ++I have used them in implementing a version of rlogin in which all ++data is encrypted. ++.PP ++.I des_rw_mode ++is used to specify the encryption mode to use with ++.I des_enc_read ++and ++.I des_end_write. ++If set to ++.I DES_PCBC_MODE ++(the default), des_pcbc_encrypt is used. ++If set to ++.I DES_CBC_MODE ++des_cbc_encrypt is used. ++These two routines and the variable are not part of the normal MIT library. ++.PP ++.I des_set_odd_parity ++sets the parity of the passed ++.I key ++to odd. This routine is not part of the standard MIT library. ++.PP ++.I des_is_weak_key ++returns 1 is the passed key is a weak key (pick again :-), ++0 if it is ok. ++This routine is not part of the standard MIT library. ++.PP ++.I crypt ++is a replacement for the normal system crypt. ++It is much faster than the system crypt. ++.PP ++.SH FILES ++/usr/include/des.h ++.br ++/usr/lib/libdes.a ++.PP ++The encryption routines have been tested on 16bit, 32bit and 64bit ++machines of various endian and even works under VMS. ++.PP ++.SH BUGS ++.PP ++If you think this manual is sparse, ++read the des_crypt(3) manual from the MIT kerberos (or bones outside ++of the USA) distribution. ++.PP ++.I des_cfb_encrypt ++and ++.I des_ofb_encrypt ++operates on input of 8 bits. What this means is that if you set ++numbits to 12, and length to 2, the first 12 bits will come from the 1st ++input byte and the low half of the second input byte. The second 12 ++bits will have the low 8 bits taken from the 3rd input byte and the ++top 4 bits taken from the 4th input byte. The same holds for output. ++This function has been implemented this way because most people will ++be using a multiple of 8 and because once you get into pulling bytes input ++bytes apart things get ugly! ++.PP ++.I des_read_pw_string ++is the most machine/OS dependent function and normally generates the ++most problems when porting this code. ++.PP ++.I des_string_to_key ++is probably different from the MIT version since there are lots ++of fun ways to implement one-way encryption of a text string. ++.PP ++The routines are optimised for 32 bit machines and so are not efficient ++on IBM PCs. ++.PP ++NOTE: extensive work has been done on this library since this document ++was origionally written. Please try to read des.doc from the libdes ++distribution since it is far more upto date and documents more of the ++functions. Libdes is now also being shipped as part of SSLeay, a ++general cryptographic library that amonst other things implements ++netscapes SSL protocoll. The most recent version can be found in ++SSLeay distributions. ++.SH AUTHOR ++Eric Young (eay@cryptsoft.com) +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des_enc.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,502 @@ ++/* crypto/des/des_enc.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++#include "des_locl.h" ++ ++void des_encrypt(data, ks, enc) ++DES_LONG *data; ++des_key_schedule ks; ++int enc; ++ { ++ register DES_LONG l,r,t,u; ++#ifdef DES_PTR ++ register unsigned char *des_SP=(unsigned char *)des_SPtrans; ++#endif ++#ifndef DES_UNROLL ++ register int i; ++#endif ++ register DES_LONG *s; ++ ++ r=data[0]; ++ l=data[1]; ++ ++ IP(r,l); ++ /* Things have been modified so that the initial rotate is ++ * done outside the loop. This required the ++ * des_SPtrans values in sp.h to be rotated 1 bit to the right. ++ * One perl script later and things have a 5% speed up on a sparc2. ++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> ++ * for pointing this out. */ ++ /* clear the top bits on machines with 8byte longs */ ++ /* shift left by 2 */ ++ r=ROTATE(r,29)&0xffffffffL; ++ l=ROTATE(l,29)&0xffffffffL; ++ ++ s=(DES_LONG *)ks; ++ /* I don't know if it is worth the effort of loop unrolling the ++ * inner loop */ ++ if (enc) ++ { ++#ifdef DES_UNROLL ++ D_ENCRYPT(l,r, 0); /* 1 */ ++ D_ENCRYPT(r,l, 2); /* 2 */ ++ D_ENCRYPT(l,r, 4); /* 3 */ ++ D_ENCRYPT(r,l, 6); /* 4 */ ++ D_ENCRYPT(l,r, 8); /* 5 */ ++ D_ENCRYPT(r,l,10); /* 6 */ ++ D_ENCRYPT(l,r,12); /* 7 */ ++ D_ENCRYPT(r,l,14); /* 8 */ ++ D_ENCRYPT(l,r,16); /* 9 */ ++ D_ENCRYPT(r,l,18); /* 10 */ ++ D_ENCRYPT(l,r,20); /* 11 */ ++ D_ENCRYPT(r,l,22); /* 12 */ ++ D_ENCRYPT(l,r,24); /* 13 */ ++ D_ENCRYPT(r,l,26); /* 14 */ ++ D_ENCRYPT(l,r,28); /* 15 */ ++ D_ENCRYPT(r,l,30); /* 16 */ ++#else ++ for (i=0; i<32; i+=8) ++ { ++ D_ENCRYPT(l,r,i+0); /* 1 */ ++ D_ENCRYPT(r,l,i+2); /* 2 */ ++ D_ENCRYPT(l,r,i+4); /* 3 */ ++ D_ENCRYPT(r,l,i+6); /* 4 */ ++ } ++#endif ++ } ++ else ++ { ++#ifdef DES_UNROLL ++ D_ENCRYPT(l,r,30); /* 16 */ ++ D_ENCRYPT(r,l,28); /* 15 */ ++ D_ENCRYPT(l,r,26); /* 14 */ ++ D_ENCRYPT(r,l,24); /* 13 */ ++ D_ENCRYPT(l,r,22); /* 12 */ ++ D_ENCRYPT(r,l,20); /* 11 */ ++ D_ENCRYPT(l,r,18); /* 10 */ ++ D_ENCRYPT(r,l,16); /* 9 */ ++ D_ENCRYPT(l,r,14); /* 8 */ ++ D_ENCRYPT(r,l,12); /* 7 */ ++ D_ENCRYPT(l,r,10); /* 6 */ ++ D_ENCRYPT(r,l, 8); /* 5 */ ++ D_ENCRYPT(l,r, 6); /* 4 */ ++ D_ENCRYPT(r,l, 4); /* 3 */ ++ D_ENCRYPT(l,r, 2); /* 2 */ ++ D_ENCRYPT(r,l, 0); /* 1 */ ++#else ++ for (i=30; i>0; i-=8) ++ { ++ D_ENCRYPT(l,r,i-0); /* 16 */ ++ D_ENCRYPT(r,l,i-2); /* 15 */ ++ D_ENCRYPT(l,r,i-4); /* 14 */ ++ D_ENCRYPT(r,l,i-6); /* 13 */ ++ } ++#endif ++ } ++ ++ /* rotate and clear the top bits on machines with 8byte longs */ ++ l=ROTATE(l,3)&0xffffffffL; ++ r=ROTATE(r,3)&0xffffffffL; ++ ++ FP(r,l); ++ data[0]=l; ++ data[1]=r; ++ l=r=t=u=0; ++ } ++ ++void des_encrypt2(data, ks, enc) ++DES_LONG *data; ++des_key_schedule ks; ++int enc; ++ { ++ register DES_LONG l,r,t,u; ++#ifdef DES_PTR ++ register unsigned char *des_SP=(unsigned char *)des_SPtrans; ++#endif ++#ifndef DES_UNROLL ++ register int i; ++#endif ++ register DES_LONG *s; ++ ++ r=data[0]; ++ l=data[1]; ++ ++ /* Things have been modified so that the initial rotate is ++ * done outside the loop. This required the ++ * des_SPtrans values in sp.h to be rotated 1 bit to the right. ++ * One perl script later and things have a 5% speed up on a sparc2. ++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> ++ * for pointing this out. */ ++ /* clear the top bits on machines with 8byte longs */ ++ r=ROTATE(r,29)&0xffffffffL; ++ l=ROTATE(l,29)&0xffffffffL; ++ ++ s=(DES_LONG *)ks; ++ /* I don't know if it is worth the effort of loop unrolling the ++ * inner loop */ ++ if (enc) ++ { ++#ifdef DES_UNROLL ++ D_ENCRYPT(l,r, 0); /* 1 */ ++ D_ENCRYPT(r,l, 2); /* 2 */ ++ D_ENCRYPT(l,r, 4); /* 3 */ ++ D_ENCRYPT(r,l, 6); /* 4 */ ++ D_ENCRYPT(l,r, 8); /* 5 */ ++ D_ENCRYPT(r,l,10); /* 6 */ ++ D_ENCRYPT(l,r,12); /* 7 */ ++ D_ENCRYPT(r,l,14); /* 8 */ ++ D_ENCRYPT(l,r,16); /* 9 */ ++ D_ENCRYPT(r,l,18); /* 10 */ ++ D_ENCRYPT(l,r,20); /* 11 */ ++ D_ENCRYPT(r,l,22); /* 12 */ ++ D_ENCRYPT(l,r,24); /* 13 */ ++ D_ENCRYPT(r,l,26); /* 14 */ ++ D_ENCRYPT(l,r,28); /* 15 */ ++ D_ENCRYPT(r,l,30); /* 16 */ ++#else ++ for (i=0; i<32; i+=8) ++ { ++ D_ENCRYPT(l,r,i+0); /* 1 */ ++ D_ENCRYPT(r,l,i+2); /* 2 */ ++ D_ENCRYPT(l,r,i+4); /* 3 */ ++ D_ENCRYPT(r,l,i+6); /* 4 */ ++ } ++#endif ++ } ++ else ++ { ++#ifdef DES_UNROLL ++ D_ENCRYPT(l,r,30); /* 16 */ ++ D_ENCRYPT(r,l,28); /* 15 */ ++ D_ENCRYPT(l,r,26); /* 14 */ ++ D_ENCRYPT(r,l,24); /* 13 */ ++ D_ENCRYPT(l,r,22); /* 12 */ ++ D_ENCRYPT(r,l,20); /* 11 */ ++ D_ENCRYPT(l,r,18); /* 10 */ ++ D_ENCRYPT(r,l,16); /* 9 */ ++ D_ENCRYPT(l,r,14); /* 8 */ ++ D_ENCRYPT(r,l,12); /* 7 */ ++ D_ENCRYPT(l,r,10); /* 6 */ ++ D_ENCRYPT(r,l, 8); /* 5 */ ++ D_ENCRYPT(l,r, 6); /* 4 */ ++ D_ENCRYPT(r,l, 4); /* 3 */ ++ D_ENCRYPT(l,r, 2); /* 2 */ ++ D_ENCRYPT(r,l, 0); /* 1 */ ++#else ++ for (i=30; i>0; i-=8) ++ { ++ D_ENCRYPT(l,r,i-0); /* 16 */ ++ D_ENCRYPT(r,l,i-2); /* 15 */ ++ D_ENCRYPT(l,r,i-4); /* 14 */ ++ D_ENCRYPT(r,l,i-6); /* 13 */ ++ } ++#endif ++ } ++ /* rotate and clear the top bits on machines with 8byte longs */ ++ data[0]=ROTATE(l,3)&0xffffffffL; ++ data[1]=ROTATE(r,3)&0xffffffffL; ++ l=r=t=u=0; ++ } ++ ++void des_encrypt3(data,ks1,ks2,ks3) ++DES_LONG *data; ++des_key_schedule ks1; ++des_key_schedule ks2; ++des_key_schedule ks3; ++ { ++ register DES_LONG l,r; ++ ++ l=data[0]; ++ r=data[1]; ++ IP(l,r); ++ data[0]=l; ++ data[1]=r; ++ des_encrypt2((DES_LONG *)data,ks1,DES_ENCRYPT); ++ des_encrypt2((DES_LONG *)data,ks2,DES_DECRYPT); ++ des_encrypt2((DES_LONG *)data,ks3,DES_ENCRYPT); ++ l=data[0]; ++ r=data[1]; ++ FP(r,l); ++ data[0]=l; ++ data[1]=r; ++ } ++ ++void des_decrypt3(data,ks1,ks2,ks3) ++DES_LONG *data; ++des_key_schedule ks1; ++des_key_schedule ks2; ++des_key_schedule ks3; ++ { ++ register DES_LONG l,r; ++ ++ l=data[0]; ++ r=data[1]; ++ IP(l,r); ++ data[0]=l; ++ data[1]=r; ++ des_encrypt2((DES_LONG *)data,ks3,DES_DECRYPT); ++ des_encrypt2((DES_LONG *)data,ks2,DES_ENCRYPT); ++ des_encrypt2((DES_LONG *)data,ks1,DES_DECRYPT); ++ l=data[0]; ++ r=data[1]; ++ FP(r,l); ++ data[0]=l; ++ data[1]=r; ++ } ++ ++#ifndef DES_DEFAULT_OPTIONS ++ ++void des_ncbc_encrypt(input, output, length, schedule, ivec, enc) ++des_cblock (*input); ++des_cblock (*output); ++long length; ++des_key_schedule schedule; ++des_cblock (*ivec); ++int enc; ++ { ++ register DES_LONG tin0,tin1; ++ register DES_LONG tout0,tout1,xor0,xor1; ++ register unsigned char *in,*out; ++ register long l=length; ++ DES_LONG tin[2]; ++ unsigned char *iv; ++ ++ in=(unsigned char *)input; ++ out=(unsigned char *)output; ++ iv=(unsigned char *)ivec; ++ ++ if (enc) ++ { ++ c2l(iv,tout0); ++ c2l(iv,tout1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); ++ c2l(in,tin1); ++ tin0^=tout0; tin[0]=tin0; ++ tin1^=tout1; tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); ++ tout0=tin[0]; l2c(tout0,out); ++ tout1=tin[1]; l2c(tout1,out); ++ } ++ if (l != -8) ++ { ++ c2ln(in,tin0,tin1,l+8); ++ tin0^=tout0; tin[0]=tin0; ++ tin1^=tout1; tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); ++ tout0=tin[0]; l2c(tout0,out); ++ tout1=tin[1]; l2c(tout1,out); ++ } ++ iv=(unsigned char *)ivec; ++ l2c(tout0,iv); ++ l2c(tout1,iv); ++ } ++ else ++ { ++ c2l(iv,xor0); ++ c2l(iv,xor1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); tin[0]=tin0; ++ c2l(in,tin1); tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); ++ tout0=tin[0]^xor0; ++ tout1=tin[1]^xor1; ++ l2c(tout0,out); ++ l2c(tout1,out); ++ xor0=tin0; ++ xor1=tin1; ++ } ++ if (l != -8) ++ { ++ c2l(in,tin0); tin[0]=tin0; ++ c2l(in,tin1); tin[1]=tin1; ++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); ++ tout0=tin[0]^xor0; ++ tout1=tin[1]^xor1; ++ l2cn(tout0,tout1,out,l+8); ++ xor0=tin0; ++ xor1=tin1; ++ } ++ ++ iv=(unsigned char *)ivec; ++ l2c(xor0,iv); ++ l2c(xor1,iv); ++ } ++ tin0=tin1=tout0=tout1=xor0=xor1=0; ++ tin[0]=tin[1]=0; ++ } ++ ++void des_ede3_cbc_encrypt(input, output, length, ks1, ks2, ks3, ivec, enc) ++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; ++ { ++ register DES_LONG tin0,tin1; ++ register DES_LONG tout0,tout1,xor0,xor1; ++ register unsigned char *in,*out; ++ register long l=length; ++ DES_LONG tin[2]; ++ unsigned char *iv; ++ ++ in=(unsigned char *)input; ++ out=(unsigned char *)output; ++ iv=(unsigned char *)ivec; ++ ++ if (enc) ++ { ++ c2l(iv,tout0); ++ c2l(iv,tout1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); ++ c2l(in,tin1); ++ tin0^=tout0; ++ tin1^=tout1; ++ ++ tin[0]=tin0; ++ tin[1]=tin1; ++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); ++ tout0=tin[0]; ++ tout1=tin[1]; ++ ++ l2c(tout0,out); ++ l2c(tout1,out); ++ } ++ if (l != -8) ++ { ++ c2ln(in,tin0,tin1,l+8); ++ tin0^=tout0; ++ tin1^=tout1; ++ ++ tin[0]=tin0; ++ tin[1]=tin1; ++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); ++ tout0=tin[0]; ++ tout1=tin[1]; ++ ++ l2c(tout0,out); ++ l2c(tout1,out); ++ } ++ iv=(unsigned char *)ivec; ++ l2c(tout0,iv); ++ l2c(tout1,iv); ++ } ++ else ++ { ++ register DES_LONG t0,t1; ++ ++ c2l(iv,xor0); ++ c2l(iv,xor1); ++ for (l-=8; l>=0; l-=8) ++ { ++ c2l(in,tin0); ++ c2l(in,tin1); ++ ++ t0=tin0; ++ t1=tin1; ++ ++ tin[0]=tin0; ++ tin[1]=tin1; ++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); ++ tout0=tin[0]; ++ tout1=tin[1]; ++ ++ tout0^=xor0; ++ tout1^=xor1; ++ l2c(tout0,out); ++ l2c(tout1,out); ++ xor0=t0; ++ xor1=t1; ++ } ++ if (l != -8) ++ { ++ c2l(in,tin0); ++ c2l(in,tin1); ++ ++ t0=tin0; ++ t1=tin1; ++ ++ tin[0]=tin0; ++ tin[1]=tin1; ++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); ++ tout0=tin[0]; ++ tout1=tin[1]; ++ ++ tout0^=xor0; ++ tout1^=xor1; ++ l2cn(tout0,tout1,out,l+8); ++ xor0=t0; ++ xor1=t1; ++ } ++ ++ iv=(unsigned char *)ivec; ++ l2c(xor0,iv); ++ l2c(xor1,iv); ++ } ++ tin0=tin1=tout0=tout1=xor0=xor1=0; ++ tin[0]=tin[1]=0; ++ } ++ ++#endif /* DES_DEFAULT_OPTIONS */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des_locl.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,515 @@ ++/* crypto/des/des_locl.org */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ++ * ++ * Always modify des_locl.org since des_locl.h is automatically generated from ++ * it during SSLeay configuration. ++ * ++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ++ */ ++ ++#ifndef HEADER_DES_LOCL_H ++#define HEADER_DES_LOCL_H ++ ++#if defined(WIN32) || defined(WIN16) ++#ifndef MSDOS ++#define MSDOS ++#endif ++#endif ++ ++#include "crypto/des.h" ++ ++#ifndef DES_DEFAULT_OPTIONS ++/* the following is tweaked from a config script, that is why it is a ++ * protected undef/define */ ++#ifndef DES_PTR ++#define DES_PTR ++#endif ++ ++/* This helps C compiler generate the correct code for multiple functional ++ * units. It reduces register dependancies at the expense of 2 more ++ * registers */ ++#ifndef DES_RISC1 ++#define DES_RISC1 ++#endif ++ ++#ifndef DES_RISC2 ++#undef DES_RISC2 ++#endif ++ ++#if defined(DES_RISC1) && defined(DES_RISC2) ++YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!! ++#endif ++ ++/* Unroll the inner loop, this sometimes helps, sometimes hinders. ++ * Very mucy CPU dependant */ ++#ifndef DES_UNROLL ++#define DES_UNROLL ++#endif ++ ++/* These default values were supplied by ++ * Peter Gutman ++ * They are only used if nothing else has been defined */ ++#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL) ++/* Special defines which change the way the code is built depending on the ++ CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find ++ even newer MIPS CPU's, but at the moment one size fits all for ++ optimization options. Older Sparc's work better with only UNROLL, but ++ there's no way to tell at compile time what it is you're running on */ ++ ++#if defined( sun ) /* Newer Sparc's */ ++ #define DES_PTR ++ #define DES_RISC1 ++ #define DES_UNROLL ++#elif defined( __ultrix ) /* Older MIPS */ ++ #define DES_PTR ++ #define DES_RISC2 ++ #define DES_UNROLL ++#elif defined( __osf1__ ) /* Alpha */ ++ #define DES_PTR ++ #define DES_RISC2 ++#elif defined ( _AIX ) /* RS6000 */ ++ /* Unknown */ ++#elif defined( __hpux ) /* HP-PA */ ++ /* Unknown */ ++#elif defined( __aux ) /* 68K */ ++ /* Unknown */ ++#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */ ++ #define DES_UNROLL ++#elif defined( __sgi ) /* Newer MIPS */ ++ #define DES_PTR ++ #define DES_RISC2 ++ #define DES_UNROLL ++#elif defined( i386 ) /* x86 boxes, should be gcc */ ++ #define DES_PTR ++ #define DES_RISC1 ++ #define DES_UNROLL ++#endif /* Systems-specific speed defines */ ++#endif ++ ++#endif /* DES_DEFAULT_OPTIONS */ ++ ++#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */ ++#include ++#include ++#include ++#include ++#ifndef RAND ++#define RAND ++#endif ++#undef NOPROTO ++#endif ++ ++#if defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS) ++#ifndef __KERNEL__ ++#include ++#else ++#include ++#endif ++#endif ++ ++#ifndef RAND ++#define RAND ++#endif ++ ++#ifdef linux ++#undef RAND ++#endif ++ ++#ifdef MSDOS ++#define getpid() 2 ++#define RAND ++#undef NOPROTO ++#endif ++ ++#if defined(NOCONST) ++#define const ++#endif ++ ++#ifdef __STDC__ ++#undef NOPROTO ++#endif ++ ++#ifdef RAND ++#define srandom(s) srand(s) ++#define random rand ++#endif ++ ++#define ITERATIONS 16 ++#define HALF_ITERATIONS 8 ++ ++/* used in des_read and des_write */ ++#define MAXWRITE (1024*16) ++#define BSIZE (MAXWRITE+4) ++ ++#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ ++ l|=((DES_LONG)(*((c)++)))<< 8L, \ ++ l|=((DES_LONG)(*((c)++)))<<16L, \ ++ l|=((DES_LONG)(*((c)++)))<<24L) ++ ++/* NOTE - c is not incremented as per c2l */ ++#define c2ln(c,l1,l2,n) { \ ++ c+=n; \ ++ l1=l2=0; \ ++ switch (n) { \ ++ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ ++ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ ++ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ ++ case 5: l2|=((DES_LONG)(*(--(c)))); \ ++ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ ++ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ ++ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ ++ case 1: l1|=((DES_LONG)(*(--(c)))); \ ++ } \ ++ } ++ ++#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ ++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ ++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ ++ *((c)++)=(unsigned char)(((l)>>24L)&0xff)) ++ ++/* replacements for htonl and ntohl since I have no idea what to do ++ * when faced with machines with 8 byte longs. */ ++#define HDRSIZE 4 ++ ++#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \ ++ l|=((DES_LONG)(*((c)++)))<<16L, \ ++ l|=((DES_LONG)(*((c)++)))<< 8L, \ ++ l|=((DES_LONG)(*((c)++)))) ++ ++#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ ++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ ++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ ++ *((c)++)=(unsigned char)(((l) )&0xff)) ++ ++/* NOTE - c is not incremented as per l2c */ ++#define l2cn(l1,l2,c,n) { \ ++ c+=n; \ ++ switch (n) { \ ++ case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ ++ case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ ++ case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ ++ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ ++ case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ ++ case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ ++ case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ ++ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ ++ } \ ++ } ++ ++#if defined(WIN32) ++#define ROTATE(a,n) (_lrotr(a,n)) ++#else ++#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) ++#endif ++ ++/* Don't worry about the LOAD_DATA() stuff, that is used by ++ * fcrypt() to add it's little bit to the front */ ++ ++#ifdef DES_FCRYPT ++ ++#define LOAD_DATA_tmp(R,S,u,t,E0,E1) \ ++ { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); } ++ ++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ ++ t=R^(R>>16L); \ ++ u=t&E0; t&=E1; \ ++ tmp=(u<<16); u^=R^s[S ]; u^=tmp; \ ++ tmp=(t<<16); t^=R^s[S+1]; t^=tmp ++#else ++#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) ++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ ++ u=R^s[S ]; \ ++ t=R^s[S+1] ++#endif ++ ++/* The changes to this macro may help or hinder, depending on the ++ * compiler and the achitecture. gcc2 always seems to do well :-). ++ * Inspired by Dana How ++ * DO NOT use the alternative version on machines with 8 byte longs. ++ * It does not seem to work on the Alpha, even when DES_LONG is 4 ++ * bytes, probably an issue of accessing non-word aligned objects :-( */ ++#ifdef DES_PTR ++ ++/* It recently occured to me that 0^0^0^0^0^0^0 == 0, so there ++ * is no reason to not xor all the sub items together. This potentially ++ * saves a register since things can be xored directly into L */ ++ ++#if defined(DES_RISC1) || defined(DES_RISC2) ++#ifdef DES_RISC1 ++#define D_ENCRYPT(LL,R,S) { \ ++ unsigned int u1,u2,u3; \ ++ LOAD_DATA(R,S,u,t,E0,E1,u1); \ ++ u2=(int)u>>8L; \ ++ u1=(int)u&0xfc; \ ++ u2&=0xfc; \ ++ t=ROTATE(t,4); \ ++ u>>=16L; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ ++ u3=(int)(u>>8L); \ ++ u1=(int)u&0xfc; \ ++ u3&=0xfc; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+u3); \ ++ u2=(int)t>>8L; \ ++ u1=(int)t&0xfc; \ ++ u2&=0xfc; \ ++ t>>=16L; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ ++ u3=(int)t>>8L; \ ++ u1=(int)t&0xfc; \ ++ u3&=0xfc; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+u3); } ++#endif ++#ifdef DES_RISC2 ++#define D_ENCRYPT(LL,R,S) { \ ++ unsigned int u1,u2,s1,s2; \ ++ LOAD_DATA(R,S,u,t,E0,E1,u1); \ ++ u2=(int)u>>8L; \ ++ u1=(int)u&0xfc; \ ++ u2&=0xfc; \ ++ t=ROTATE(t,4); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ ++ s1=(int)(u>>16L); \ ++ s2=(int)(u>>24L); \ ++ s1&=0xfc; \ ++ s2&=0xfc; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+s1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+s2); \ ++ u2=(int)t>>8L; \ ++ u1=(int)t&0xfc; \ ++ u2&=0xfc; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ ++ s1=(int)(t>>16L); \ ++ s2=(int)(t>>24L); \ ++ s1&=0xfc; \ ++ s2&=0xfc; \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+s1); \ ++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+s2); } ++#endif ++#else ++#define D_ENCRYPT(LL,R,S) { \ ++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \ ++ t=ROTATE(t,4); \ ++ LL^= \ ++ *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8L)&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16L)&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24L)&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8L)&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16L)&0xfc))^ \ ++ *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24L)&0xfc)); } ++#endif ++ ++#else /* original version */ ++ ++#if defined(DES_RISC1) || defined(DES_RISC2) ++#ifdef DES_RISC1 ++#define D_ENCRYPT(LL,R,S) {\ ++ unsigned int u1,u2,u3; \ ++ LOAD_DATA(R,S,u,t,E0,E1,u1); \ ++ u>>=2L; \ ++ t=ROTATE(t,6); \ ++ u2=(int)u>>8L; \ ++ u1=(int)u&0x3f; \ ++ u2&=0x3f; \ ++ u>>=16L; \ ++ LL^=des_SPtrans[0][u1]; \ ++ LL^=des_SPtrans[2][u2]; \ ++ u3=(int)u>>8L; \ ++ u1=(int)u&0x3f; \ ++ u3&=0x3f; \ ++ LL^=des_SPtrans[4][u1]; \ ++ LL^=des_SPtrans[6][u3]; \ ++ u2=(int)t>>8L; \ ++ u1=(int)t&0x3f; \ ++ u2&=0x3f; \ ++ t>>=16L; \ ++ LL^=des_SPtrans[1][u1]; \ ++ LL^=des_SPtrans[3][u2]; \ ++ u3=(int)t>>8L; \ ++ u1=(int)t&0x3f; \ ++ u3&=0x3f; \ ++ LL^=des_SPtrans[5][u1]; \ ++ LL^=des_SPtrans[7][u3]; } ++#endif ++#ifdef DES_RISC2 ++#define D_ENCRYPT(LL,R,S) {\ ++ unsigned int u1,u2,s1,s2; \ ++ LOAD_DATA(R,S,u,t,E0,E1,u1); \ ++ u>>=2L; \ ++ t=ROTATE(t,6); \ ++ u2=(int)u>>8L; \ ++ u1=(int)u&0x3f; \ ++ u2&=0x3f; \ ++ LL^=des_SPtrans[0][u1]; \ ++ LL^=des_SPtrans[2][u2]; \ ++ s1=(int)u>>16L; \ ++ s2=(int)u>>24L; \ ++ s1&=0x3f; \ ++ s2&=0x3f; \ ++ LL^=des_SPtrans[4][s1]; \ ++ LL^=des_SPtrans[6][s2]; \ ++ u2=(int)t>>8L; \ ++ u1=(int)t&0x3f; \ ++ u2&=0x3f; \ ++ LL^=des_SPtrans[1][u1]; \ ++ LL^=des_SPtrans[3][u2]; \ ++ s1=(int)t>>16; \ ++ s2=(int)t>>24L; \ ++ s1&=0x3f; \ ++ s2&=0x3f; \ ++ LL^=des_SPtrans[5][s1]; \ ++ LL^=des_SPtrans[7][s2]; } ++#endif ++ ++#else ++ ++#define D_ENCRYPT(LL,R,S) {\ ++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \ ++ t=ROTATE(t,4); \ ++ LL^=\ ++ des_SPtrans[0][(u>> 2L)&0x3f]^ \ ++ des_SPtrans[2][(u>>10L)&0x3f]^ \ ++ des_SPtrans[4][(u>>18L)&0x3f]^ \ ++ des_SPtrans[6][(u>>26L)&0x3f]^ \ ++ des_SPtrans[1][(t>> 2L)&0x3f]^ \ ++ des_SPtrans[3][(t>>10L)&0x3f]^ \ ++ des_SPtrans[5][(t>>18L)&0x3f]^ \ ++ des_SPtrans[7][(t>>26L)&0x3f]; } ++#endif ++#endif ++ ++ /* IP and FP ++ * The problem is more of a geometric problem that random bit fiddling. ++ 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 ++ 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 ++ 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 ++ 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 ++ ++ 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 ++ 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 ++ 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 ++ 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 ++ ++ The output has been subject to swaps of the form ++ 0 1 -> 3 1 but the odd and even bits have been put into ++ 2 3 2 0 ++ different words. The main trick is to remember that ++ t=((l>>size)^r)&(mask); ++ r^=t; ++ l^=(t<>(n))^(b))&(m)),\ ++ (b)^=(t),\ ++ (a)^=((t)<<(n))) ++ ++#define IP(l,r) \ ++ { \ ++ register DES_LONG tt; \ ++ PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ ++ PERM_OP(l,r,tt,16,0x0000ffffL); \ ++ PERM_OP(r,l,tt, 2,0x33333333L); \ ++ PERM_OP(l,r,tt, 8,0x00ff00ffL); \ ++ PERM_OP(r,l,tt, 1,0x55555555L); \ ++ } ++ ++#define FP(l,r) \ ++ { \ ++ register DES_LONG tt; \ ++ PERM_OP(l,r,tt, 1,0x55555555L); \ ++ PERM_OP(r,l,tt, 8,0x00ff00ffL); \ ++ PERM_OP(l,r,tt, 2,0x33333333L); \ ++ PERM_OP(r,l,tt,16,0x0000ffffL); \ ++ PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ ++ } ++ ++extern const DES_LONG des_SPtrans[8][64]; ++ ++#ifndef NOPROTO ++void fcrypt_body(DES_LONG *out,des_key_schedule ks, ++ DES_LONG Eswap0, DES_LONG Eswap1); ++#else ++void fcrypt_body(); ++#endif ++ ++#endif +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des_opts.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,620 @@ ++/* crypto/des/des_opts.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* define PART1, PART2, PART3 or PART4 to build only with a few of the options. ++ * This is for machines with 64k code segment size restrictions. */ ++ ++#ifndef MSDOS ++#define TIMES ++#endif ++ ++#include ++#ifndef MSDOS ++#include ++#else ++#include ++extern void exit(); ++#endif ++#include ++#ifndef VMS ++#ifndef _IRIX ++#include ++#endif ++#ifdef TIMES ++#include ++#include ++#endif ++#else /* VMS */ ++#include ++struct tms { ++ time_t tms_utime; ++ time_t tms_stime; ++ time_t tms_uchild; /* I dunno... */ ++ time_t tms_uchildsys; /* so these names are a guess :-) */ ++ } ++#endif ++#ifndef TIMES ++#include ++#endif ++ ++#ifdef sun ++#include ++#include ++#endif ++ ++#include "des_locl.h" ++#include "spr.h" ++ ++#define DES_DEFAULT_OPTIONS ++ ++#if !defined(PART1) && !defined(PART2) && !defined(PART3) && !defined(PART4) ++#define PART1 ++#define PART2 ++#define PART3 ++#define PART4 ++#endif ++ ++#ifdef PART1 ++ ++#undef DES_UNROLL ++#undef DES_RISC1 ++#undef DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#define des_encrypt des_encrypt_u4_cisc_idx ++#define des_encrypt2 des_encrypt2_u4_cisc_idx ++#define des_encrypt3 des_encrypt3_u4_cisc_idx ++#define des_decrypt3 des_decrypt3_u4_cisc_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#undef DES_RISC1 ++#undef DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_cisc_idx ++#define des_encrypt2 des_encrypt2_u16_cisc_idx ++#define des_encrypt3 des_encrypt3_u16_cisc_idx ++#define des_decrypt3 des_decrypt3_u16_cisc_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#undef DES_UNROLL ++#define DES_RISC1 ++#undef DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u4_risc1_idx ++#define des_encrypt2 des_encrypt2_u4_risc1_idx ++#define des_encrypt3 des_encrypt3_u4_risc1_idx ++#define des_decrypt3 des_decrypt3_u4_risc1_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#endif ++ ++#ifdef PART2 ++ ++#undef DES_UNROLL ++#undef DES_RISC1 ++#define DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u4_risc2_idx ++#define des_encrypt2 des_encrypt2_u4_risc2_idx ++#define des_encrypt3 des_encrypt3_u4_risc2_idx ++#define des_decrypt3 des_decrypt3_u4_risc2_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#define DES_RISC1 ++#undef DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_risc1_idx ++#define des_encrypt2 des_encrypt2_u16_risc1_idx ++#define des_encrypt3 des_encrypt3_u16_risc1_idx ++#define des_decrypt3 des_decrypt3_u16_risc1_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#undef DES_RISC1 ++#define DES_RISC2 ++#undef DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_risc2_idx ++#define des_encrypt2 des_encrypt2_u16_risc2_idx ++#define des_encrypt3 des_encrypt3_u16_risc2_idx ++#define des_decrypt3 des_decrypt3_u16_risc2_idx ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#endif ++ ++#ifdef PART3 ++ ++#undef DES_UNROLL ++#undef DES_RISC1 ++#undef DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u4_cisc_ptr ++#define des_encrypt2 des_encrypt2_u4_cisc_ptr ++#define des_encrypt3 des_encrypt3_u4_cisc_ptr ++#define des_decrypt3 des_decrypt3_u4_cisc_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#undef DES_RISC1 ++#undef DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_cisc_ptr ++#define des_encrypt2 des_encrypt2_u16_cisc_ptr ++#define des_encrypt3 des_encrypt3_u16_cisc_ptr ++#define des_decrypt3 des_decrypt3_u16_cisc_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#undef DES_UNROLL ++#define DES_RISC1 ++#undef DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u4_risc1_ptr ++#define des_encrypt2 des_encrypt2_u4_risc1_ptr ++#define des_encrypt3 des_encrypt3_u4_risc1_ptr ++#define des_decrypt3 des_decrypt3_u4_risc1_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#endif ++ ++#ifdef PART4 ++ ++#undef DES_UNROLL ++#undef DES_RISC1 ++#define DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u4_risc2_ptr ++#define des_encrypt2 des_encrypt2_u4_risc2_ptr ++#define des_encrypt3 des_encrypt3_u4_risc2_ptr ++#define des_decrypt3 des_decrypt3_u4_risc2_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#define DES_RISC1 ++#undef DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_risc1_ptr ++#define des_encrypt2 des_encrypt2_u16_risc1_ptr ++#define des_encrypt3 des_encrypt3_u16_risc1_ptr ++#define des_decrypt3 des_decrypt3_u16_risc1_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#define DES_UNROLL ++#undef DES_RISC1 ++#define DES_RISC2 ++#define DES_PTR ++#undef D_ENCRYPT ++#undef des_encrypt ++#undef des_encrypt2 ++#undef des_encrypt3 ++#undef des_decrypt3 ++#define des_encrypt des_encrypt_u16_risc2_ptr ++#define des_encrypt2 des_encrypt2_u16_risc2_ptr ++#define des_encrypt3 des_encrypt3_u16_risc2_ptr ++#define des_decrypt3 des_decrypt3_u16_risc2_ptr ++#undef HEADER_DES_LOCL_H ++#include "des_enc.c" ++ ++#endif ++ ++/* The following if from times(3) man page. It may need to be changed */ ++#ifndef HZ ++# ifndef CLK_TCK ++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */ ++# ifndef VMS ++# define HZ 100.0 ++# else /* VMS */ ++# define HZ 100.0 ++# endif ++# else /* _BSD_CLK_TCK_ */ ++# define HZ ((double)_BSD_CLK_TCK_) ++# endif ++# else /* CLK_TCK */ ++# define HZ ((double)CLK_TCK) ++# endif ++#endif ++ ++#define BUFSIZE ((long)1024) ++long run=0; ++ ++#ifndef NOPROTO ++double Time_F(int s); ++#else ++double Time_F(); ++#endif ++ ++#ifdef SIGALRM ++#if defined(__STDC__) || defined(sgi) ++#define SIGRETTYPE void ++#else ++#define SIGRETTYPE int ++#endif ++ ++#ifndef NOPROTO ++SIGRETTYPE sig_done(int sig); ++#else ++SIGRETTYPE sig_done(); ++#endif ++ ++SIGRETTYPE sig_done(sig) ++int sig; ++ { ++ signal(SIGALRM,sig_done); ++ run=0; ++#ifdef LINT ++ sig=sig; ++#endif ++ } ++#endif ++ ++#define START 0 ++#define STOP 1 ++ ++double Time_F(s) ++int s; ++ { ++ double ret; ++#ifdef TIMES ++ static struct tms tstart,tend; ++ ++ if (s == START) ++ { ++ times(&tstart); ++ return(0); ++ } ++ else ++ { ++ times(&tend); ++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ; ++ return((ret == 0.0)?1e-6:ret); ++ } ++#else /* !times() */ ++ static struct timeb tstart,tend; ++ long i; ++ ++ if (s == START) ++ { ++ ftime(&tstart); ++ return(0); ++ } ++ else ++ { ++ ftime(&tend); ++ i=(long)tend.millitm-(long)tstart.millitm; ++ ret=((double)(tend.time-tstart.time))+((double)i)/1000.0; ++ return((ret == 0.0)?1e-6:ret); ++ } ++#endif ++ } ++ ++#ifdef SIGALRM ++#define print_name(name) fprintf(stderr,"Doing %s's for 10 seconds\n",name); alarm(10); ++#else ++#define print_name(name) fprintf(stderr,"Doing %s %ld times\n",name,cb); ++#endif ++ ++#define time_it(func,name,index) \ ++ print_name(name); \ ++ Time_F(START); \ ++ for (count=0,run=1; COND(cb); count++) \ ++ { \ ++ unsigned long d[2]; \ ++ func(d,&(sch[0]),DES_ENCRYPT); \ ++ } \ ++ tm[index]=Time_F(STOP); \ ++ fprintf(stderr,"%ld %s's in %.2f second\n",count,name,tm[index]); \ ++ tm[index]=((double)COUNT(cb))/tm[index]; ++ ++#define print_it(name,index) \ ++ fprintf(stderr,"%s bytes per sec = %12.2f (%5.1fuS)\n",name, \ ++ tm[index]*8,1.0e6/tm[index]); ++ ++int main(argc,argv) ++int argc; ++char **argv; ++ { ++ long count; ++ static unsigned char buf[BUFSIZE]; ++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; ++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12}; ++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34}; ++ des_key_schedule sch,sch2,sch3; ++ double d,tm[16],max=0; ++ int rank[16]; ++ char *str[16]; ++ int max_idx=0,i,num=0,j; ++#ifndef SIGALARM ++ long ca,cb,cc,cd,ce; ++#endif ++ ++ for (i=0; i<12; i++) ++ { ++ tm[i]=0.0; ++ rank[i]=0; ++ } ++ ++#ifndef TIMES ++ fprintf(stderr,"To get the most acurate results, try to run this\n"); ++ fprintf(stderr,"program when this computer is idle.\n"); ++#endif ++ ++ des_set_key((C_Block *)key,sch); ++ des_set_key((C_Block *)key2,sch2); ++ des_set_key((C_Block *)key3,sch3); ++ ++#ifndef SIGALRM ++ fprintf(stderr,"First we calculate the approximate speed ...\n"); ++ des_set_key((C_Block *)key,sch); ++ count=10; ++ do { ++ long i; ++ unsigned long data[2]; ++ ++ count*=2; ++ Time_F(START); ++ for (i=count; i; i--) ++ des_encrypt(data,&(sch[0]),DES_ENCRYPT); ++ d=Time_F(STOP); ++ } while (d < 3.0); ++ ca=count; ++ cb=count*3; ++ cc=count*3*8/BUFSIZE+1; ++ cd=count*8/BUFSIZE+1; ++ ++ ce=count/20+1; ++#define COND(d) (count != (d)) ++#define COUNT(d) (d) ++#else ++#define COND(c) (run) ++#define COUNT(d) (count) ++ signal(SIGALRM,sig_done); ++ alarm(10); ++#endif ++ ++#ifdef PART1 ++ time_it(des_encrypt_u4_cisc_idx, "des_encrypt_u4_cisc_idx ", 0); ++ time_it(des_encrypt_u16_cisc_idx, "des_encrypt_u16_cisc_idx ", 1); ++ time_it(des_encrypt_u4_risc1_idx, "des_encrypt_u4_risc1_idx ", 2); ++ num+=3; ++#endif ++#ifdef PART2 ++ time_it(des_encrypt_u16_risc1_idx,"des_encrypt_u16_risc1_idx", 3); ++ time_it(des_encrypt_u4_risc2_idx, "des_encrypt_u4_risc2_idx ", 4); ++ time_it(des_encrypt_u16_risc2_idx,"des_encrypt_u16_risc2_idx", 5); ++ num+=3; ++#endif ++#ifdef PART3 ++ time_it(des_encrypt_u4_cisc_ptr, "des_encrypt_u4_cisc_ptr ", 6); ++ time_it(des_encrypt_u16_cisc_ptr, "des_encrypt_u16_cisc_ptr ", 7); ++ time_it(des_encrypt_u4_risc1_ptr, "des_encrypt_u4_risc1_ptr ", 8); ++ num+=3; ++#endif ++#ifdef PART4 ++ time_it(des_encrypt_u16_risc1_ptr,"des_encrypt_u16_risc1_ptr", 9); ++ time_it(des_encrypt_u4_risc2_ptr, "des_encrypt_u4_risc2_ptr ",10); ++ time_it(des_encrypt_u16_risc2_ptr,"des_encrypt_u16_risc2_ptr",11); ++ num+=3; ++#endif ++ ++#ifdef PART1 ++ str[0]=" 4 c i"; ++ print_it("des_encrypt_u4_cisc_idx ",0); ++ max=tm[0]; ++ max_idx=0; ++ str[1]="16 c i"; ++ print_it("des_encrypt_u16_cisc_idx ",1); ++ if (max < tm[1]) { max=tm[1]; max_idx=1; } ++ str[2]=" 4 r1 i"; ++ print_it("des_encrypt_u4_risc1_idx ",2); ++ if (max < tm[2]) { max=tm[2]; max_idx=2; } ++#endif ++#ifdef PART2 ++ str[3]="16 r1 i"; ++ print_it("des_encrypt_u16_risc1_idx",3); ++ if (max < tm[3]) { max=tm[3]; max_idx=3; } ++ str[4]=" 4 r2 i"; ++ print_it("des_encrypt_u4_risc2_idx ",4); ++ if (max < tm[4]) { max=tm[4]; max_idx=4; } ++ str[5]="16 r2 i"; ++ print_it("des_encrypt_u16_risc2_idx",5); ++ if (max < tm[5]) { max=tm[5]; max_idx=5; } ++#endif ++#ifdef PART3 ++ str[6]=" 4 c p"; ++ print_it("des_encrypt_u4_cisc_ptr ",6); ++ if (max < tm[6]) { max=tm[6]; max_idx=6; } ++ str[7]="16 c p"; ++ print_it("des_encrypt_u16_cisc_ptr ",7); ++ if (max < tm[7]) { max=tm[7]; max_idx=7; } ++ str[8]=" 4 r1 p"; ++ print_it("des_encrypt_u4_risc1_ptr ",8); ++ if (max < tm[8]) { max=tm[8]; max_idx=8; } ++#endif ++#ifdef PART4 ++ str[9]="16 r1 p"; ++ print_it("des_encrypt_u16_risc1_ptr",9); ++ if (max < tm[9]) { max=tm[9]; max_idx=9; } ++ str[10]=" 4 r2 p"; ++ print_it("des_encrypt_u4_risc2_ptr ",10); ++ if (max < tm[10]) { max=tm[10]; max_idx=10; } ++ str[11]="16 r2 p"; ++ print_it("des_encrypt_u16_risc2_ptr",11); ++ if (max < tm[11]) { max=tm[11]; max_idx=11; } ++#endif ++ printf("options des ecb/s\n"); ++ printf("%s %12.2f 100.0%%\n",str[max_idx],tm[max_idx]); ++ d=tm[max_idx]; ++ tm[max_idx]= -2.0; ++ max= -1.0; ++ for (;;) ++ { ++ for (i=0; i<12; i++) ++ { ++ if (max < tm[i]) { max=tm[i]; j=i; } ++ } ++ if (max < 0.0) break; ++ printf("%s %12.2f %4.1f%%\n",str[j],tm[j],tm[j]/d*100.0); ++ tm[j]= -2.0; ++ max= -1.0; ++ } ++ ++ switch (max_idx) ++ { ++ case 0: ++ printf("-DDES_DEFAULT_OPTIONS\n"); ++ break; ++ case 1: ++ printf("-DDES_UNROLL\n"); ++ break; ++ case 2: ++ printf("-DDES_RISC1\n"); ++ break; ++ case 3: ++ printf("-DDES_UNROLL -DDES_RISC1\n"); ++ break; ++ case 4: ++ printf("-DDES_RISC2\n"); ++ break; ++ case 5: ++ printf("-DDES_UNROLL -DDES_RISC2\n"); ++ break; ++ case 6: ++ printf("-DDES_PTR\n"); ++ break; ++ case 7: ++ printf("-DDES_UNROLL -DDES_PTR\n"); ++ break; ++ case 8: ++ printf("-DDES_RISC1 -DDES_PTR\n"); ++ break; ++ case 9: ++ printf("-DDES_UNROLL -DDES_RISC1 -DDES_PTR\n"); ++ break; ++ case 10: ++ printf("-DDES_RISC2 -DDES_PTR\n"); ++ break; ++ case 11: ++ printf("-DDES_UNROLL -DDES_RISC2 -DDES_PTR\n"); ++ break; ++ } ++ exit(0); ++#if defined(LINT) || defined(MSDOS) ++ return(0); ++#endif ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/des_ver.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,60 @@ ++/* crypto/des/des_ver.h */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++extern char *DES_version; /* SSLeay version string */ ++extern char *libdes_version; /* old libdes version string */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/destest.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,871 @@ ++/* crypto/des/destest.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++#if defined(WIN32) || defined(WIN16) || defined(WINDOWS) ++#ifndef MSDOS ++#define MSDOS ++#endif ++#endif ++ ++#include ++#include ++#ifndef MSDOS ++#include ++#else ++#include ++#endif ++#include ++#include "des_locl.h" ++ ++/* tisk tisk - the test keys don't all have odd parity :-( */ ++/* test data */ ++#define NUM_TESTS 34 ++static unsigned char key_data[NUM_TESTS][8]={ ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, ++ {0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}, ++ {0x7C,0xA1,0x10,0x45,0x4A,0x1A,0x6E,0x57}, ++ {0x01,0x31,0xD9,0x61,0x9D,0xC1,0x37,0x6E}, ++ {0x07,0xA1,0x13,0x3E,0x4A,0x0B,0x26,0x86}, ++ {0x38,0x49,0x67,0x4C,0x26,0x02,0x31,0x9E}, ++ {0x04,0xB9,0x15,0xBA,0x43,0xFE,0xB5,0xB6}, ++ {0x01,0x13,0xB9,0x70,0xFD,0x34,0xF2,0xCE}, ++ {0x01,0x70,0xF1,0x75,0x46,0x8F,0xB5,0xE6}, ++ {0x43,0x29,0x7F,0xAD,0x38,0xE3,0x73,0xFE}, ++ {0x07,0xA7,0x13,0x70,0x45,0xDA,0x2A,0x16}, ++ {0x04,0x68,0x91,0x04,0xC2,0xFD,0x3B,0x2F}, ++ {0x37,0xD0,0x6B,0xB5,0x16,0xCB,0x75,0x46}, ++ {0x1F,0x08,0x26,0x0D,0x1A,0xC2,0x46,0x5E}, ++ {0x58,0x40,0x23,0x64,0x1A,0xBA,0x61,0x76}, ++ {0x02,0x58,0x16,0x16,0x46,0x29,0xB0,0x07}, ++ {0x49,0x79,0x3E,0xBC,0x79,0xB3,0x25,0x8F}, ++ {0x4F,0xB0,0x5E,0x15,0x15,0xAB,0x73,0xA7}, ++ {0x49,0xE9,0x5D,0x6D,0x4C,0xA2,0x29,0xBF}, ++ {0x01,0x83,0x10,0xDC,0x40,0x9B,0x26,0xD6}, ++ {0x1C,0x58,0x7F,0x1C,0x13,0x92,0x4F,0xEF}, ++ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, ++ {0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E}, ++ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE}, ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}}; ++ ++static unsigned char plain_data[NUM_TESTS][8]={ ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, ++ {0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, ++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, ++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0x01,0xA1,0xD6,0xD0,0x39,0x77,0x67,0x42}, ++ {0x5C,0xD5,0x4C,0xA8,0x3D,0xEF,0x57,0xDA}, ++ {0x02,0x48,0xD4,0x38,0x06,0xF6,0x71,0x72}, ++ {0x51,0x45,0x4B,0x58,0x2D,0xDF,0x44,0x0A}, ++ {0x42,0xFD,0x44,0x30,0x59,0x57,0x7F,0xA2}, ++ {0x05,0x9B,0x5E,0x08,0x51,0xCF,0x14,0x3A}, ++ {0x07,0x56,0xD8,0xE0,0x77,0x47,0x61,0xD2}, ++ {0x76,0x25,0x14,0xB8,0x29,0xBF,0x48,0x6A}, ++ {0x3B,0xDD,0x11,0x90,0x49,0x37,0x28,0x02}, ++ {0x26,0x95,0x5F,0x68,0x35,0xAF,0x60,0x9A}, ++ {0x16,0x4D,0x5E,0x40,0x4F,0x27,0x52,0x32}, ++ {0x6B,0x05,0x6E,0x18,0x75,0x9F,0x5C,0xCA}, ++ {0x00,0x4B,0xD6,0xEF,0x09,0x17,0x60,0x62}, ++ {0x48,0x0D,0x39,0x00,0x6E,0xE7,0x62,0xF2}, ++ {0x43,0x75,0x40,0xC8,0x69,0x8F,0x3C,0xFA}, ++ {0x07,0x2D,0x43,0xA0,0x77,0x07,0x52,0x92}, ++ {0x02,0xFE,0x55,0x77,0x81,0x17,0xF1,0x2A}, ++ {0x1D,0x9D,0x5C,0x50,0x18,0xF7,0x28,0xC2}, ++ {0x30,0x55,0x32,0x28,0x6D,0x6F,0x29,0x5A}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, ++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, ++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; ++ ++static unsigned char cipher_data[NUM_TESTS][8]={ ++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7}, ++ {0x73,0x59,0xB2,0x16,0x3E,0x4E,0xDC,0x58}, ++ {0x95,0x8E,0x6E,0x62,0x7A,0x05,0x55,0x7B}, ++ {0xF4,0x03,0x79,0xAB,0x9E,0x0E,0xC5,0x33}, ++ {0x17,0x66,0x8D,0xFC,0x72,0x92,0x53,0x2D}, ++ {0x8A,0x5A,0xE1,0xF8,0x1A,0xB8,0xF2,0xDD}, ++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7}, ++ {0xED,0x39,0xD9,0x50,0xFA,0x74,0xBC,0xC4}, ++ {0x69,0x0F,0x5B,0x0D,0x9A,0x26,0x93,0x9B}, ++ {0x7A,0x38,0x9D,0x10,0x35,0x4B,0xD2,0x71}, ++ {0x86,0x8E,0xBB,0x51,0xCA,0xB4,0x59,0x9A}, ++ {0x71,0x78,0x87,0x6E,0x01,0xF1,0x9B,0x2A}, ++ {0xAF,0x37,0xFB,0x42,0x1F,0x8C,0x40,0x95}, ++ {0x86,0xA5,0x60,0xF1,0x0E,0xC6,0xD8,0x5B}, ++ {0x0C,0xD3,0xDA,0x02,0x00,0x21,0xDC,0x09}, ++ {0xEA,0x67,0x6B,0x2C,0xB7,0xDB,0x2B,0x7A}, ++ {0xDF,0xD6,0x4A,0x81,0x5C,0xAF,0x1A,0x0F}, ++ {0x5C,0x51,0x3C,0x9C,0x48,0x86,0xC0,0x88}, ++ {0x0A,0x2A,0xEE,0xAE,0x3F,0xF4,0xAB,0x77}, ++ {0xEF,0x1B,0xF0,0x3E,0x5D,0xFA,0x57,0x5A}, ++ {0x88,0xBF,0x0D,0xB6,0xD7,0x0D,0xEE,0x56}, ++ {0xA1,0xF9,0x91,0x55,0x41,0x02,0x0B,0x56}, ++ {0x6F,0xBF,0x1C,0xAF,0xCF,0xFD,0x05,0x56}, ++ {0x2F,0x22,0xE4,0x9B,0xAB,0x7C,0xA1,0xAC}, ++ {0x5A,0x6B,0x61,0x2C,0xC2,0x6C,0xCE,0x4A}, ++ {0x5F,0x4C,0x03,0x8E,0xD1,0x2B,0x2E,0x41}, ++ {0x63,0xFA,0xC0,0xD0,0x34,0xD9,0xF7,0x93}, ++ {0x61,0x7B,0x3A,0x0C,0xE8,0xF0,0x71,0x00}, ++ {0xDB,0x95,0x86,0x05,0xF8,0xC8,0xC6,0x06}, ++ {0xED,0xBF,0xD1,0xC6,0x6C,0x29,0xCC,0xC7}, ++ {0x35,0x55,0x50,0xB2,0x15,0x0E,0x24,0x51}, ++ {0xCA,0xAA,0xAF,0x4D,0xEA,0xF1,0xDB,0xAE}, ++ {0xD5,0xD4,0x4F,0xF7,0x20,0x68,0x3D,0x0D}, ++ {0x2A,0x2B,0xB0,0x08,0xDF,0x97,0xC2,0xF2}}; ++ ++static unsigned char cipher_ecb2[NUM_TESTS-1][8]={ ++ {0x92,0x95,0xB5,0x9B,0xB3,0x84,0x73,0x6E}, ++ {0x19,0x9E,0x9D,0x6D,0xF3,0x9A,0xA8,0x16}, ++ {0x2A,0x4B,0x4D,0x24,0x52,0x43,0x84,0x27}, ++ {0x35,0x84,0x3C,0x01,0x9D,0x18,0xC5,0xB6}, ++ {0x4A,0x5B,0x2F,0x42,0xAA,0x77,0x19,0x25}, ++ {0xA0,0x6B,0xA9,0xB8,0xCA,0x5B,0x17,0x8A}, ++ {0xAB,0x9D,0xB7,0xFB,0xED,0x95,0xF2,0x74}, ++ {0x3D,0x25,0x6C,0x23,0xA7,0x25,0x2F,0xD6}, ++ {0xB7,0x6F,0xAB,0x4F,0xBD,0xBD,0xB7,0x67}, ++ {0x8F,0x68,0x27,0xD6,0x9C,0xF4,0x1A,0x10}, ++ {0x82,0x57,0xA1,0xD6,0x50,0x5E,0x81,0x85}, ++ {0xA2,0x0F,0x0A,0xCD,0x80,0x89,0x7D,0xFA}, ++ {0xCD,0x2A,0x53,0x3A,0xDB,0x0D,0x7E,0xF3}, ++ {0xD2,0xC2,0xBE,0x27,0xE8,0x1B,0x68,0xE3}, ++ {0xE9,0x24,0xCF,0x4F,0x89,0x3C,0x5B,0x0A}, ++ {0xA7,0x18,0xC3,0x9F,0xFA,0x9F,0xD7,0x69}, ++ {0x77,0x2C,0x79,0xB1,0xD2,0x31,0x7E,0xB1}, ++ {0x49,0xAB,0x92,0x7F,0xD0,0x22,0x00,0xB7}, ++ {0xCE,0x1C,0x6C,0x7D,0x85,0xE3,0x4A,0x6F}, ++ {0xBE,0x91,0xD6,0xE1,0x27,0xB2,0xE9,0x87}, ++ {0x70,0x28,0xAE,0x8F,0xD1,0xF5,0x74,0x1A}, ++ {0xAA,0x37,0x80,0xBB,0xF3,0x22,0x1D,0xDE}, ++ {0xA6,0xC4,0xD2,0x5E,0x28,0x93,0xAC,0xB3}, ++ {0x22,0x07,0x81,0x5A,0xE4,0xB7,0x1A,0xAD}, ++ {0xDC,0xCE,0x05,0xE7,0x07,0xBD,0xF5,0x84}, ++ {0x26,0x1D,0x39,0x2C,0xB3,0xBA,0xA5,0x85}, ++ {0xB4,0xF7,0x0F,0x72,0xFB,0x04,0xF0,0xDC}, ++ {0x95,0xBA,0xA9,0x4E,0x87,0x36,0xF2,0x89}, ++ {0xD4,0x07,0x3A,0xF1,0x5A,0x17,0x82,0x0E}, ++ {0xEF,0x6F,0xAF,0xA7,0x66,0x1A,0x7E,0x89}, ++ {0xC1,0x97,0xF5,0x58,0x74,0x8A,0x20,0xE7}, ++ {0x43,0x34,0xCF,0xDA,0x22,0xC4,0x86,0xC8}, ++ {0x08,0xD7,0xB4,0xFB,0x62,0x9D,0x08,0x85}}; ++ ++static unsigned char cbc_key [8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; ++static unsigned char cbc2_key[8]={0xf0,0xe1,0xd2,0xc3,0xb4,0xa5,0x96,0x87}; ++static unsigned char cbc3_key[8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; ++static unsigned char cbc_iv [8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; ++static char cbc_data[40]="7654321 Now is the time for \0001"; ++ ++static unsigned char cbc_ok[32]={ ++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4, ++ 0xac,0xd8,0xae,0xfd,0xdf,0xd8,0xa1,0xeb, ++ 0x46,0x8e,0x91,0x15,0x78,0x88,0xba,0x68, ++ 0x1d,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4}; ++ ++static unsigned char xcbc_ok[32]={ ++ 0x86,0x74,0x81,0x0D,0x61,0xA4,0xA5,0x48, ++ 0xB9,0x93,0x03,0xE1,0xB8,0xBB,0xBD,0xBD, ++ 0x64,0x30,0x0B,0xB9,0x06,0x65,0x81,0x76, ++ 0x04,0x1D,0x77,0x62,0x17,0xCA,0x2B,0xD2, ++ }; ++ ++static unsigned char cbc3_ok[32]={ ++ 0x3F,0xE3,0x01,0xC9,0x62,0xAC,0x01,0xD0, ++ 0x22,0x13,0x76,0x3C,0x1C,0xBD,0x4C,0xDC, ++ 0x79,0x96,0x57,0xC0,0x64,0xEC,0xF5,0xD4, ++ 0x1C,0x67,0x38,0x12,0xCF,0xDE,0x96,0x75}; ++ ++static unsigned char pcbc_ok[32]={ ++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4, ++ 0x6d,0xec,0xb4,0x70,0xa0,0xe5,0x6b,0x15, ++ 0xae,0xa6,0xbf,0x61,0xed,0x7d,0x9c,0x9f, ++ 0xf7,0x17,0x46,0x3b,0x8a,0xb3,0xcc,0x88}; ++ ++static unsigned char cfb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; ++static unsigned char cfb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef}; ++static unsigned char cfb_buf1[40],cfb_buf2[40],cfb_tmp[8]; ++static unsigned char plain[24]= ++ { ++ 0x4e,0x6f,0x77,0x20,0x69,0x73, ++ 0x20,0x74,0x68,0x65,0x20,0x74, ++ 0x69,0x6d,0x65,0x20,0x66,0x6f, ++ 0x72,0x20,0x61,0x6c,0x6c,0x20 ++ }; ++static unsigned char cfb_cipher8[24]= { ++ 0xf3,0x1f,0xda,0x07,0x01,0x14, 0x62,0xee,0x18,0x7f,0x43,0xd8, ++ 0x0a,0x7c,0xd9,0xb5,0xb0,0xd2, 0x90,0xda,0x6e,0x5b,0x9a,0x87 }; ++static unsigned char cfb_cipher16[24]={ ++ 0xF3,0x09,0x87,0x87,0x7F,0x57, 0xF7,0x3C,0x36,0xB6,0xDB,0x70, ++ 0xD8,0xD5,0x34,0x19,0xD3,0x86, 0xB2,0x23,0xB7,0xB2,0xAD,0x1B }; ++static unsigned char cfb_cipher32[24]={ ++ 0xF3,0x09,0x62,0x49,0xA4,0xDF, 0xA4,0x9F,0x33,0xDC,0x7B,0xAD, ++ 0x4C,0xC8,0x9F,0x64,0xE4,0x53, 0xE5,0xEC,0x67,0x20,0xDA,0xB6 }; ++static unsigned char cfb_cipher48[24]={ ++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x30,0xB5,0x15,0xEC,0xBB,0x85, ++ 0x97,0x5A,0x13,0x8C,0x68,0x60, 0xE2,0x38,0x34,0x3C,0xDC,0x1F }; ++static unsigned char cfb_cipher64[24]={ ++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x6E,0x51,0xA6,0x9E,0x83,0x9B, ++ 0x1A,0x92,0xF7,0x84,0x03,0x46, 0x71,0x33,0x89,0x8E,0xA6,0x22 }; ++ ++static unsigned char ofb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; ++static unsigned char ofb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef}; ++static unsigned char ofb_buf1[24],ofb_buf2[24],ofb_tmp[8]; ++static unsigned char ofb_cipher[24]= ++ { ++ 0xf3,0x09,0x62,0x49,0xc7,0xf4,0x6e,0x51, ++ 0x35,0xf2,0x4a,0x24,0x2e,0xeb,0x3d,0x3f, ++ 0x3d,0x6d,0x5b,0xe3,0x25,0x5a,0xf8,0xc3 ++ }; ++ ++DES_LONG cbc_cksum_ret=0xB462FEF7L; ++unsigned char cbc_cksum_data[8]={0x1D,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4}; ++ ++#ifndef NOPROTO ++static char *pt(unsigned char *p); ++static int cfb_test(int bits, unsigned char *cfb_cipher); ++static int cfb64_test(unsigned char *cfb_cipher); ++static int ede_cfb64_test(unsigned char *cfb_cipher); ++#else ++static char *pt(); ++static int cfb_test(); ++static int cfb64_test(); ++static int ede_cfb64_test(); ++#endif ++ ++int main(argc,argv) ++int argc; ++char *argv[]; ++ { ++ int i,j,err=0; ++ des_cblock in,out,outin,iv3; ++ des_key_schedule ks,ks2,ks3; ++ unsigned char cbc_in[40]; ++ unsigned char cbc_out[40]; ++ DES_LONG cs; ++ unsigned char qret[4][4],cret[8]; ++ DES_LONG lqret[4]; ++ int num; ++ char *str; ++ ++ printf("Doing ecb\n"); ++ for (i=0; i>4)&0xf]; ++ ret[i*2+1]=f[p[i]&0xf]; ++ } ++ ret[16]='\0'; ++ return(ret); ++ } ++ ++#ifndef LIBDES_LIT ++ ++static int cfb_test(bits, cfb_cipher) ++int bits; ++unsigned char *cfb_cipher; ++ { ++ des_key_schedule ks; ++ int i,err=0; ++ ++ des_key_sched((C_Block *)cfb_key,ks); ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ des_cfb_encrypt(plain,cfb_buf1,bits,(long)sizeof(plain),ks, ++ (C_Block *)cfb_tmp,DES_ENCRYPT); ++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("cfb_encrypt encrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf1[i]))); ++ } ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ des_cfb_encrypt(cfb_buf1,cfb_buf2,bits,(long)sizeof(plain),ks, ++ (C_Block *)cfb_tmp,DES_DECRYPT); ++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("cfb_encrypt decrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf1[i]))); ++ } ++ return(err); ++ } ++ ++static int cfb64_test(cfb_cipher) ++unsigned char *cfb_cipher; ++ { ++ des_key_schedule ks; ++ int err=0,i,n; ++ ++ des_key_sched((C_Block *)cfb_key,ks); ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ n=0; ++ des_cfb64_encrypt(plain,cfb_buf1,(long)12,ks, ++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT); ++ des_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]), ++ (long)sizeof(plain)-12,ks, ++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT); ++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("cfb_encrypt encrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf1[i]))); ++ } ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ n=0; ++ des_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks, ++ (C_Block *)cfb_tmp,&n,DES_DECRYPT); ++ des_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]), ++ (long)sizeof(plain)-17,ks, ++ (C_Block *)cfb_tmp,&n,DES_DECRYPT); ++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("cfb_encrypt decrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf2[i]))); ++ } ++ return(err); ++ } ++ ++static int ede_cfb64_test(cfb_cipher) ++unsigned char *cfb_cipher; ++ { ++ des_key_schedule ks; ++ int err=0,i,n; ++ ++ des_key_sched((C_Block *)cfb_key,ks); ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ n=0; ++ des_ede3_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,ks,ks, ++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT); ++ des_ede3_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]), ++ (long)sizeof(plain)-12,ks,ks,ks, ++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT); ++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("ede_cfb_encrypt encrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf1[i]))); ++ } ++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); ++ n=0; ++ des_ede3_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,ks,ks, ++ (C_Block *)cfb_tmp,&n,DES_DECRYPT); ++ des_ede3_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]), ++ (long)sizeof(plain)-17,ks,ks,ks, ++ (C_Block *)cfb_tmp,&n,DES_DECRYPT); ++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) ++ { ++ err=1; ++ printf("ede_cfb_encrypt decrypt error\n"); ++ for (i=0; i<24; i+=8) ++ printf("%s\n",pt(&(cfb_buf2[i]))); ++ } ++ return(err); ++ } ++ ++#endif ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/dx86unix.S Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,3160 @@ ++/* ++ * This file was originally generated by Michael Richardson ++ * via the perl scripts found in the ASM subdir. It remains copyright of ++ * Eric Young, see the file COPYRIGHT. ++ * ++ * This was last done on October 9, 2002. ++ * ++ * While this file does not need to go through cpp, we pass it through ++ * CPP by naming it dx86unix.S instead of dx86unix.s because there is ++ * a bug in Rules.make for .s builds - specifically it references EXTRA_CFLAGS ++ * which may contain stuff that AS doesn't understand instead of ++ * referencing EXTRA_AFLAGS. ++ */ ++ ++ .file "dx86unix.S" ++ .version "01.01" ++.text ++ .align 16 ++.globl des_encrypt ++ .type des_encrypt , @function ++des_encrypt: ++ pushl %esi ++ pushl %edi ++ ++ ++ movl 12(%esp), %esi ++ xorl %ecx, %ecx ++ pushl %ebx ++ pushl %ebp ++ movl (%esi), %eax ++ movl 28(%esp), %ebx ++ movl 4(%esi), %edi ++ ++ ++ roll $4, %eax ++ movl %eax, %esi ++ xorl %edi, %eax ++ andl $0xf0f0f0f0, %eax ++ xorl %eax, %esi ++ xorl %eax, %edi ++ ++ roll $20, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0xfff0000f, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $14, %eax ++ movl %eax, %edi ++ xorl %esi, %eax ++ andl $0x33333333, %eax ++ xorl %eax, %edi ++ xorl %eax, %esi ++ ++ roll $22, %esi ++ movl %esi, %eax ++ xorl %edi, %esi ++ andl $0x03fc03fc, %esi ++ xorl %esi, %eax ++ xorl %esi, %edi ++ ++ roll $9, %eax ++ movl %eax, %esi ++ xorl %edi, %eax ++ andl $0xaaaaaaaa, %eax ++ xorl %eax, %esi ++ xorl %eax, %edi ++ ++.byte 209 ++.byte 199 ++ movl 24(%esp), %ebp ++ cmpl $0, %ebx ++ je .L000start_decrypt ++ ++ ++ movl (%ebp), %eax ++ xorl %ebx, %ebx ++ movl 4(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 8(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 12(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 16(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 20(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 24(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 28(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 32(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 36(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 40(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 44(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 48(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 52(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 56(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 60(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 64(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 68(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 72(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 76(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 80(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 84(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 88(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 92(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 96(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 100(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 104(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 108(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 112(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 116(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 120(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 124(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ jmp .L001end ++.L000start_decrypt: ++ ++ ++ movl 120(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 124(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 112(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 116(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 104(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 108(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 96(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 100(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 88(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 92(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 80(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 84(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 72(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 76(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 64(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 68(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 56(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 60(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 48(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 52(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 40(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 44(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 32(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 36(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 24(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 28(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 16(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 20(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 8(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 12(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl (%ebp), %eax ++ xorl %ebx, %ebx ++ movl 4(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++.L001end: ++ ++ ++ movl 20(%esp), %edx ++.byte 209 ++.byte 206 ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0xaaaaaaaa, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $23, %eax ++ movl %eax, %edi ++ xorl %esi, %eax ++ andl $0x03fc03fc, %eax ++ xorl %eax, %edi ++ xorl %eax, %esi ++ ++ roll $10, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0x33333333, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $18, %esi ++ movl %esi, %edi ++ xorl %eax, %esi ++ andl $0xfff0000f, %esi ++ xorl %esi, %edi ++ xorl %esi, %eax ++ ++ roll $12, %edi ++ movl %edi, %esi ++ xorl %eax, %edi ++ andl $0xf0f0f0f0, %edi ++ xorl %edi, %esi ++ xorl %edi, %eax ++ ++ rorl $4, %eax ++ movl %eax, (%edx) ++ movl %esi, 4(%edx) ++ popl %ebp ++ popl %ebx ++ popl %edi ++ popl %esi ++ ret ++.des_encrypt_end: ++ .size des_encrypt , .des_encrypt_end-des_encrypt ++.ident "desasm.pl" ++.text ++ .align 16 ++.globl des_encrypt2 ++ .type des_encrypt2 , @function ++des_encrypt2: ++ pushl %esi ++ pushl %edi ++ ++ ++ movl 12(%esp), %eax ++ xorl %ecx, %ecx ++ pushl %ebx ++ pushl %ebp ++ movl (%eax), %esi ++ movl 28(%esp), %ebx ++ roll $3, %esi ++ movl 4(%eax), %edi ++ roll $3, %edi ++ movl 24(%esp), %ebp ++ cmpl $0, %ebx ++ je .L002start_decrypt ++ ++ ++ movl (%ebp), %eax ++ xorl %ebx, %ebx ++ movl 4(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 8(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 12(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 16(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 20(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 24(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 28(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 32(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 36(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 40(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 44(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 48(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 52(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 56(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 60(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 64(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 68(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 72(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 76(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 80(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 84(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 88(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 92(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 96(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 100(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 104(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 108(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 112(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 116(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 120(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 124(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ jmp .L003end ++.L002start_decrypt: ++ ++ ++ movl 120(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 124(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 112(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 116(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 104(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 108(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 96(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 100(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 88(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 92(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 80(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 84(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 72(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 76(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 64(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 68(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 56(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 60(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 48(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 52(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 40(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 44(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 32(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 36(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 24(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 28(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl 16(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 20(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++ ++ ++ movl 8(%ebp), %eax ++ xorl %ebx, %ebx ++ movl 12(%ebp), %edx ++ xorl %esi, %eax ++ xorl %esi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %edi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %edi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %edi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %edi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %edi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %edi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %edi ++ ++ ++ movl (%ebp), %eax ++ xorl %ebx, %ebx ++ movl 4(%ebp), %edx ++ xorl %edi, %eax ++ xorl %edi, %edx ++ andl $0xfcfcfcfc, %eax ++ andl $0xcfcfcfcf, %edx ++ movb %al, %bl ++ movb %ah, %cl ++ rorl $4, %edx ++ movl des_SPtrans(%ebx),%ebp ++ movb %dl, %bl ++ xorl %ebp, %esi ++ movl 0x200+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movb %dh, %cl ++ shrl $16, %eax ++ movl 0x100+des_SPtrans(%ebx),%ebp ++ xorl %ebp, %esi ++ movb %ah, %bl ++ shrl $16, %edx ++ movl 0x300+des_SPtrans(%ecx),%ebp ++ xorl %ebp, %esi ++ movl 24(%esp), %ebp ++ movb %dh, %cl ++ andl $0xff, %eax ++ andl $0xff, %edx ++ movl 0x600+des_SPtrans(%ebx),%ebx ++ xorl %ebx, %esi ++ movl 0x700+des_SPtrans(%ecx),%ebx ++ xorl %ebx, %esi ++ movl 0x400+des_SPtrans(%eax),%ebx ++ xorl %ebx, %esi ++ movl 0x500+des_SPtrans(%edx),%ebx ++ xorl %ebx, %esi ++.L003end: ++ ++ ++ rorl $3, %edi ++ movl 20(%esp), %eax ++ rorl $3, %esi ++ movl %edi, (%eax) ++ movl %esi, 4(%eax) ++ popl %ebp ++ popl %ebx ++ popl %edi ++ popl %esi ++ ret ++.des_encrypt2_end: ++ .size des_encrypt2 , .des_encrypt2_end-des_encrypt2 ++.ident "desasm.pl" ++.text ++ .align 16 ++.globl des_encrypt3 ++ .type des_encrypt3 , @function ++des_encrypt3: ++ pushl %ebx ++ movl 8(%esp), %ebx ++ pushl %ebp ++ pushl %esi ++ pushl %edi ++ ++ ++ movl (%ebx), %edi ++ movl 4(%ebx), %esi ++ subl $12, %esp ++ ++ ++ roll $4, %edi ++ movl %edi, %edx ++ xorl %esi, %edi ++ andl $0xf0f0f0f0, %edi ++ xorl %edi, %edx ++ xorl %edi, %esi ++ ++ roll $20, %esi ++ movl %esi, %edi ++ xorl %edx, %esi ++ andl $0xfff0000f, %esi ++ xorl %esi, %edi ++ xorl %esi, %edx ++ ++ roll $14, %edi ++ movl %edi, %esi ++ xorl %edx, %edi ++ andl $0x33333333, %edi ++ xorl %edi, %esi ++ xorl %edi, %edx ++ ++ roll $22, %edx ++ movl %edx, %edi ++ xorl %esi, %edx ++ andl $0x03fc03fc, %edx ++ xorl %edx, %edi ++ xorl %edx, %esi ++ ++ roll $9, %edi ++ movl %edi, %edx ++ xorl %esi, %edi ++ andl $0xaaaaaaaa, %edi ++ xorl %edi, %edx ++ xorl %edi, %esi ++ ++ rorl $3, %edx ++ rorl $2, %esi ++ movl %esi, 4(%ebx) ++ movl 36(%esp), %eax ++ movl %edx, (%ebx) ++ movl 40(%esp), %edi ++ movl 44(%esp), %esi ++ movl $1, 8(%esp) ++ movl %eax, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ movl $0, 8(%esp) ++ movl %edi, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ movl $1, 8(%esp) ++ movl %esi, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ addl $12, %esp ++ movl (%ebx), %edi ++ movl 4(%ebx), %esi ++ ++ ++ roll $2, %esi ++ roll $3, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0xaaaaaaaa, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $23, %eax ++ movl %eax, %edi ++ xorl %esi, %eax ++ andl $0x03fc03fc, %eax ++ xorl %eax, %edi ++ xorl %eax, %esi ++ ++ roll $10, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0x33333333, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $18, %esi ++ movl %esi, %edi ++ xorl %eax, %esi ++ andl $0xfff0000f, %esi ++ xorl %esi, %edi ++ xorl %esi, %eax ++ ++ roll $12, %edi ++ movl %edi, %esi ++ xorl %eax, %edi ++ andl $0xf0f0f0f0, %edi ++ xorl %edi, %esi ++ xorl %edi, %eax ++ ++ rorl $4, %eax ++ movl %eax, (%ebx) ++ movl %esi, 4(%ebx) ++ popl %edi ++ popl %esi ++ popl %ebp ++ popl %ebx ++ ret ++.des_encrypt3_end: ++ .size des_encrypt3 , .des_encrypt3_end-des_encrypt3 ++.ident "desasm.pl" ++.text ++ .align 16 ++.globl des_decrypt3 ++ .type des_decrypt3 , @function ++des_decrypt3: ++ pushl %ebx ++ movl 8(%esp), %ebx ++ pushl %ebp ++ pushl %esi ++ pushl %edi ++ ++ ++ movl (%ebx), %edi ++ movl 4(%ebx), %esi ++ subl $12, %esp ++ ++ ++ roll $4, %edi ++ movl %edi, %edx ++ xorl %esi, %edi ++ andl $0xf0f0f0f0, %edi ++ xorl %edi, %edx ++ xorl %edi, %esi ++ ++ roll $20, %esi ++ movl %esi, %edi ++ xorl %edx, %esi ++ andl $0xfff0000f, %esi ++ xorl %esi, %edi ++ xorl %esi, %edx ++ ++ roll $14, %edi ++ movl %edi, %esi ++ xorl %edx, %edi ++ andl $0x33333333, %edi ++ xorl %edi, %esi ++ xorl %edi, %edx ++ ++ roll $22, %edx ++ movl %edx, %edi ++ xorl %esi, %edx ++ andl $0x03fc03fc, %edx ++ xorl %edx, %edi ++ xorl %edx, %esi ++ ++ roll $9, %edi ++ movl %edi, %edx ++ xorl %esi, %edi ++ andl $0xaaaaaaaa, %edi ++ xorl %edi, %edx ++ xorl %edi, %esi ++ ++ rorl $3, %edx ++ rorl $2, %esi ++ movl %esi, 4(%ebx) ++ movl 36(%esp), %esi ++ movl %edx, (%ebx) ++ movl 40(%esp), %edi ++ movl 44(%esp), %eax ++ movl $0, 8(%esp) ++ movl %eax, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ movl $1, 8(%esp) ++ movl %edi, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ movl $0, 8(%esp) ++ movl %esi, 4(%esp) ++ movl %ebx, (%esp) ++ call des_encrypt2 ++ addl $12, %esp ++ movl (%ebx), %edi ++ movl 4(%ebx), %esi ++ ++ ++ roll $2, %esi ++ roll $3, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0xaaaaaaaa, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $23, %eax ++ movl %eax, %edi ++ xorl %esi, %eax ++ andl $0x03fc03fc, %eax ++ xorl %eax, %edi ++ xorl %eax, %esi ++ ++ roll $10, %edi ++ movl %edi, %eax ++ xorl %esi, %edi ++ andl $0x33333333, %edi ++ xorl %edi, %eax ++ xorl %edi, %esi ++ ++ roll $18, %esi ++ movl %esi, %edi ++ xorl %eax, %esi ++ andl $0xfff0000f, %esi ++ xorl %esi, %edi ++ xorl %esi, %eax ++ ++ roll $12, %edi ++ movl %edi, %esi ++ xorl %eax, %edi ++ andl $0xf0f0f0f0, %edi ++ xorl %edi, %esi ++ xorl %edi, %eax ++ ++ rorl $4, %eax ++ movl %eax, (%ebx) ++ movl %esi, 4(%ebx) ++ popl %edi ++ popl %esi ++ popl %ebp ++ popl %ebx ++ ret ++.des_decrypt3_end: ++ .size des_decrypt3 , .des_decrypt3_end-des_decrypt3 ++.ident "desasm.pl" ++.text ++ .align 16 ++.globl des_ncbc_encrypt ++ .type des_ncbc_encrypt , @function ++des_ncbc_encrypt: ++ ++ pushl %ebp ++ pushl %ebx ++ pushl %esi ++ pushl %edi ++ movl 28(%esp), %ebp ++ ++ movl 36(%esp), %ebx ++ movl (%ebx), %esi ++ movl 4(%ebx), %edi ++ pushl %edi ++ pushl %esi ++ pushl %edi ++ pushl %esi ++ movl %esp, %ebx ++ movl 36(%esp), %esi ++ movl 40(%esp), %edi ++ ++ movl 56(%esp), %ecx ++ ++ pushl %ecx ++ ++ movl 52(%esp), %eax ++ pushl %eax ++ pushl %ebx ++ cmpl $0, %ecx ++ jz .L004decrypt ++ andl $4294967288, %ebp ++ movl 12(%esp), %eax ++ movl 16(%esp), %ebx ++ jz .L005encrypt_finish ++.L006encrypt_loop: ++ movl (%esi), %ecx ++ movl 4(%esi), %edx ++ xorl %ecx, %eax ++ xorl %edx, %ebx ++ movl %eax, 12(%esp) ++ movl %ebx, 16(%esp) ++ call des_encrypt ++ movl 12(%esp), %eax ++ movl 16(%esp), %ebx ++ movl %eax, (%edi) ++ movl %ebx, 4(%edi) ++ addl $8, %esi ++ addl $8, %edi ++ subl $8, %ebp ++ jnz .L006encrypt_loop ++.L005encrypt_finish: ++ movl 56(%esp), %ebp ++ andl $7, %ebp ++ jz .L007finish ++ xorl %ecx, %ecx ++ xorl %edx, %edx ++ movl .L008cbc_enc_jmp_table(,%ebp,4),%ebp ++ jmp *%ebp ++.L009ej7: ++ movb 6(%esi), %dh ++ sall $8, %edx ++.L010ej6: ++ movb 5(%esi), %dh ++.L011ej5: ++ movb 4(%esi), %dl ++.L012ej4: ++ movl (%esi), %ecx ++ jmp .L013ejend ++.L014ej3: ++ movb 2(%esi), %ch ++ sall $8, %ecx ++.L015ej2: ++ movb 1(%esi), %ch ++.L016ej1: ++ movb (%esi), %cl ++.L013ejend: ++ xorl %ecx, %eax ++ xorl %edx, %ebx ++ movl %eax, 12(%esp) ++ movl %ebx, 16(%esp) ++ call des_encrypt ++ movl 12(%esp), %eax ++ movl 16(%esp), %ebx ++ movl %eax, (%edi) ++ movl %ebx, 4(%edi) ++ jmp .L007finish ++.align 16 ++.L004decrypt: ++ andl $4294967288, %ebp ++ movl 20(%esp), %eax ++ movl 24(%esp), %ebx ++ jz .L017decrypt_finish ++.L018decrypt_loop: ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %eax, 12(%esp) ++ movl %ebx, 16(%esp) ++ call des_encrypt ++ movl 12(%esp), %eax ++ movl 16(%esp), %ebx ++ movl 20(%esp), %ecx ++ movl 24(%esp), %edx ++ xorl %eax, %ecx ++ xorl %ebx, %edx ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %ecx, (%edi) ++ movl %edx, 4(%edi) ++ movl %eax, 20(%esp) ++ movl %ebx, 24(%esp) ++ addl $8, %esi ++ addl $8, %edi ++ subl $8, %ebp ++ jnz .L018decrypt_loop ++.L017decrypt_finish: ++ movl 56(%esp), %ebp ++ andl $7, %ebp ++ jz .L007finish ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %eax, 12(%esp) ++ movl %ebx, 16(%esp) ++ call des_encrypt ++ movl 12(%esp), %eax ++ movl 16(%esp), %ebx ++ movl 20(%esp), %ecx ++ movl 24(%esp), %edx ++ xorl %eax, %ecx ++ xorl %ebx, %edx ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++.L019dj7: ++ rorl $16, %edx ++ movb %dl, 6(%edi) ++ shrl $16, %edx ++.L020dj6: ++ movb %dh, 5(%edi) ++.L021dj5: ++ movb %dl, 4(%edi) ++.L022dj4: ++ movl %ecx, (%edi) ++ jmp .L023djend ++.L024dj3: ++ rorl $16, %ecx ++ movb %cl, 2(%edi) ++ sall $16, %ecx ++.L025dj2: ++ movb %ch, 1(%esi) ++.L026dj1: ++ movb %cl, (%esi) ++.L023djend: ++ jmp .L007finish ++.align 16 ++.L007finish: ++ movl 64(%esp), %ecx ++ addl $28, %esp ++ movl %eax, (%ecx) ++ movl %ebx, 4(%ecx) ++ popl %edi ++ popl %esi ++ popl %ebx ++ popl %ebp ++ ret ++.align 16 ++.L008cbc_enc_jmp_table: ++ .long 0 ++ .long .L016ej1 ++ .long .L015ej2 ++ .long .L014ej3 ++ .long .L012ej4 ++ .long .L011ej5 ++ .long .L010ej6 ++ .long .L009ej7 ++.align 16 ++.L027cbc_dec_jmp_table: ++ .long 0 ++ .long .L026dj1 ++ .long .L025dj2 ++ .long .L024dj3 ++ .long .L022dj4 ++ .long .L021dj5 ++ .long .L020dj6 ++ .long .L019dj7 ++.des_ncbc_encrypt_end: ++ .size des_ncbc_encrypt , .des_ncbc_encrypt_end-des_ncbc_encrypt ++.ident "desasm.pl" ++.text ++ .align 16 ++.globl des_ede3_cbc_encrypt ++ .type des_ede3_cbc_encrypt , @function ++des_ede3_cbc_encrypt: ++ ++ pushl %ebp ++ pushl %ebx ++ pushl %esi ++ pushl %edi ++ movl 28(%esp), %ebp ++ ++ movl 44(%esp), %ebx ++ movl (%ebx), %esi ++ movl 4(%ebx), %edi ++ pushl %edi ++ pushl %esi ++ pushl %edi ++ pushl %esi ++ movl %esp, %ebx ++ movl 36(%esp), %esi ++ movl 40(%esp), %edi ++ ++ movl 64(%esp), %ecx ++ ++ movl 56(%esp), %eax ++ pushl %eax ++ ++ movl 56(%esp), %eax ++ pushl %eax ++ ++ movl 56(%esp), %eax ++ pushl %eax ++ pushl %ebx ++ cmpl $0, %ecx ++ jz .L028decrypt ++ andl $4294967288, %ebp ++ movl 16(%esp), %eax ++ movl 20(%esp), %ebx ++ jz .L029encrypt_finish ++.L030encrypt_loop: ++ movl (%esi), %ecx ++ movl 4(%esi), %edx ++ xorl %ecx, %eax ++ xorl %edx, %ebx ++ movl %eax, 16(%esp) ++ movl %ebx, 20(%esp) ++ call des_encrypt3 ++ movl 16(%esp), %eax ++ movl 20(%esp), %ebx ++ movl %eax, (%edi) ++ movl %ebx, 4(%edi) ++ addl $8, %esi ++ addl $8, %edi ++ subl $8, %ebp ++ jnz .L030encrypt_loop ++.L029encrypt_finish: ++ movl 60(%esp), %ebp ++ andl $7, %ebp ++ jz .L031finish ++ xorl %ecx, %ecx ++ xorl %edx, %edx ++ movl .L032cbc_enc_jmp_table(,%ebp,4),%ebp ++ jmp *%ebp ++.L033ej7: ++ movb 6(%esi), %dh ++ sall $8, %edx ++.L034ej6: ++ movb 5(%esi), %dh ++.L035ej5: ++ movb 4(%esi), %dl ++.L036ej4: ++ movl (%esi), %ecx ++ jmp .L037ejend ++.L038ej3: ++ movb 2(%esi), %ch ++ sall $8, %ecx ++.L039ej2: ++ movb 1(%esi), %ch ++.L040ej1: ++ movb (%esi), %cl ++.L037ejend: ++ xorl %ecx, %eax ++ xorl %edx, %ebx ++ movl %eax, 16(%esp) ++ movl %ebx, 20(%esp) ++ call des_encrypt3 ++ movl 16(%esp), %eax ++ movl 20(%esp), %ebx ++ movl %eax, (%edi) ++ movl %ebx, 4(%edi) ++ jmp .L031finish ++.align 16 ++.L028decrypt: ++ andl $4294967288, %ebp ++ movl 24(%esp), %eax ++ movl 28(%esp), %ebx ++ jz .L041decrypt_finish ++.L042decrypt_loop: ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %eax, 16(%esp) ++ movl %ebx, 20(%esp) ++ call des_decrypt3 ++ movl 16(%esp), %eax ++ movl 20(%esp), %ebx ++ movl 24(%esp), %ecx ++ movl 28(%esp), %edx ++ xorl %eax, %ecx ++ xorl %ebx, %edx ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %ecx, (%edi) ++ movl %edx, 4(%edi) ++ movl %eax, 24(%esp) ++ movl %ebx, 28(%esp) ++ addl $8, %esi ++ addl $8, %edi ++ subl $8, %ebp ++ jnz .L042decrypt_loop ++.L041decrypt_finish: ++ movl 60(%esp), %ebp ++ andl $7, %ebp ++ jz .L031finish ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++ movl %eax, 16(%esp) ++ movl %ebx, 20(%esp) ++ call des_decrypt3 ++ movl 16(%esp), %eax ++ movl 20(%esp), %ebx ++ movl 24(%esp), %ecx ++ movl 28(%esp), %edx ++ xorl %eax, %ecx ++ xorl %ebx, %edx ++ movl (%esi), %eax ++ movl 4(%esi), %ebx ++.L043dj7: ++ rorl $16, %edx ++ movb %dl, 6(%edi) ++ shrl $16, %edx ++.L044dj6: ++ movb %dh, 5(%edi) ++.L045dj5: ++ movb %dl, 4(%edi) ++.L046dj4: ++ movl %ecx, (%edi) ++ jmp .L047djend ++.L048dj3: ++ rorl $16, %ecx ++ movb %cl, 2(%edi) ++ sall $16, %ecx ++.L049dj2: ++ movb %ch, 1(%esi) ++.L050dj1: ++ movb %cl, (%esi) ++.L047djend: ++ jmp .L031finish ++.align 16 ++.L031finish: ++ movl 76(%esp), %ecx ++ addl $32, %esp ++ movl %eax, (%ecx) ++ movl %ebx, 4(%ecx) ++ popl %edi ++ popl %esi ++ popl %ebx ++ popl %ebp ++ ret ++.align 16 ++.L032cbc_enc_jmp_table: ++ .long 0 ++ .long .L040ej1 ++ .long .L039ej2 ++ .long .L038ej3 ++ .long .L036ej4 ++ .long .L035ej5 ++ .long .L034ej6 ++ .long .L033ej7 ++.align 16 ++.L051cbc_dec_jmp_table: ++ .long 0 ++ .long .L050dj1 ++ .long .L049dj2 ++ .long .L048dj3 ++ .long .L046dj4 ++ .long .L045dj5 ++ .long .L044dj6 ++ .long .L043dj7 ++.des_ede3_cbc_encrypt_end: ++ .size des_ede3_cbc_encrypt , .des_ede3_cbc_encrypt_end-des_ede3_cbc_encrypt ++.ident "desasm.pl" +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/ecb_enc.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,128 @@ ++/* crypto/des/ecb_enc.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++#include "des_locl.h" ++#include "spr.h" ++ ++char *libdes_version="libdes v 3.24 - 20-Apr-1996 - eay"; ++char *DES_version="DES part of SSLeay 0.8.2b 08-Jan-1998"; ++ ++/* RCSID $Id: ecb_enc.c,v 1.6 2002/04/24 07:36:38 mcr Exp $ */ ++/* This function ifdef'ed out for FreeS/WAN project. */ ++#ifdef notdef ++char *des_options() ++ { ++ static int init=1; ++ static char buf[32]; ++ ++ if (init) ++ { ++ char *ptr,*unroll,*risc,*size; ++ ++ init=0; ++#ifdef DES_PTR ++ ptr="ptr"; ++#else ++ ptr="idx"; ++#endif ++#if defined(DES_RISC1) || defined(DES_RISC2) ++#ifdef DES_RISC1 ++ risc="risc1"; ++#endif ++#ifdef DES_RISC2 ++ risc="risc2"; ++#endif ++#else ++ risc="cisc"; ++#endif ++#ifdef DES_UNROLL ++ unroll="16"; ++#else ++ unroll="4"; ++#endif ++ if (sizeof(DES_LONG) != sizeof(long)) ++ size="int"; ++ else ++ size="long"; ++ sprintf(buf,"des(%s,%s,%s,%s)",ptr,risc,unroll,size); ++ } ++ return(buf); ++ } ++#endif ++ ++ ++void des_ecb_encrypt(input, output, ks, enc) ++des_cblock (*input); ++des_cblock (*output); ++des_key_schedule ks; ++int enc; ++ { ++ register DES_LONG l; ++ register unsigned char *in,*out; ++ DES_LONG ll[2]; ++ ++ in=(unsigned char *)input; ++ out=(unsigned char *)output; ++ c2l(in,l); ll[0]=l; ++ c2l(in,l); ll[1]=l; ++ des_encrypt(ll,ks,enc); ++ l=ll[0]; l2c(l,out); ++ l=ll[1]; l2c(l,out); ++ l=ll[0]=ll[1]=0; ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/fcrypt.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,152 @@ ++/* NOCW */ ++ ++/* This version of crypt has been developed from my MIT compatable ++ * DES library. ++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au ++ * Eric Young (eay@cryptsoft.com) ++ */ ++ ++/* Modification by Jens Kupferschmidt (Cu) ++ * I have included directive PARA for shared memory computers. ++ * I have included a directive LONGCRYPT to using this routine to cipher ++ * passwords with more then 8 bytes like HP-UX 10.x it used. The MAXPLEN ++ * definition is the maximum of lenght of password and can changed. I have ++ * defined 24. ++ */ ++ ++#include "des_locl.h" ++ ++/* Added more values to handle illegal salt values the way normal ++ * crypt() implementations do. The patch was sent by ++ * Bjorn Gronvall ++ */ ++static unsigned const char con_salt[128]={ ++0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9, ++0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1, ++0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, ++0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1, ++0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9, ++0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01, ++0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, ++0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A, ++0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12, ++0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, ++0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22, ++0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24, ++0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C, ++0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34, ++0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C, ++0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44, ++}; ++ ++static unsigned const char cov_2char[64]={ ++0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35, ++0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44, ++0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C, ++0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54, ++0x55,0x56,0x57,0x58,0x59,0x5A,0x61,0x62, ++0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A, ++0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72, ++0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A ++}; ++ ++#ifndef NOPROTO ++void fcrypt_body(DES_LONG *out,des_key_schedule ks, ++ DES_LONG Eswap0, DES_LONG Eswap1); ++ ++#ifdef PERL5 ++char *des_crypt(const char *buf,const char *salt); ++#else ++char *crypt(const char *buf,const char *salt); ++#endif ++#else ++void fcrypt_body(); ++#ifdef PERL5 ++char *des_crypt(); ++#else ++char *crypt(); ++#endif ++#endif ++ ++#ifdef PERL5 ++char *des_crypt(buf,salt) ++#else ++char *crypt(buf,salt) ++#endif ++const char *buf; ++const char *salt; ++ { ++ static char buff[14]; ++ ++ return(des_fcrypt(buf,salt,buff)); ++ } ++ ++ ++char *des_fcrypt(buf,salt,ret) ++const char *buf; ++const char *salt; ++char *ret; ++ { ++ unsigned int i,j,x,y; ++ DES_LONG Eswap0,Eswap1; ++ DES_LONG out[2],ll; ++ des_cblock key; ++ des_key_schedule ks; ++ unsigned char bb[9]; ++ unsigned char *b=bb; ++ unsigned char c,u; ++ ++ /* eay 25/08/92 ++ * If you call crypt("pwd","*") as often happens when you ++ * have * as the pwd field in /etc/passwd, the function ++ * returns *\0XXXXXXXXX ++ * The \0 makes the string look like * so the pwd "*" would ++ * crypt to "*". This was found when replacing the crypt in ++ * our shared libraries. People found that the disbled ++ * accounts effectivly had no passwd :-(. */ ++ x=ret[0]=((salt[0] == '\0')?'A':salt[0]); ++ Eswap0=con_salt[x]<<2; ++ x=ret[1]=((salt[1] == '\0')?'A':salt[1]); ++ Eswap1=con_salt[x]<<6; ++ ++/* EAY ++r=strlen(buf); ++r=(r+7)/8; ++*/ ++ for (i=0; i<8; i++) ++ { ++ c= *(buf++); ++ if (!c) break; ++ key[i]=(c<<1); ++ } ++ for (; i<8; i++) ++ key[i]=0; ++ ++ des_set_key((des_cblock *)(key),ks); ++ fcrypt_body(&(out[0]),ks,Eswap0,Eswap1); ++ ++ ll=out[0]; l2c(ll,b); ++ ll=out[1]; l2c(ll,b); ++ y=0; ++ u=0x80; ++ bb[8]=0; ++ for (i=2; i<13; i++) ++ { ++ c=0; ++ for (j=0; j<6; j++) ++ { ++ c<<=1; ++ if (bb[y] & u) c|=1; ++ u>>=1; ++ if (!u) ++ { ++ y++; ++ u=0x80; ++ } ++ } ++ ret[i]=cov_2char[c]; ++ } ++ ret[13]='\0'; ++ return(ret); ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/fcrypt_b.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,148 @@ ++/* crypto/des/fcrypt_b.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* #include */ ++ ++/* This version of crypt has been developed from my MIT compatable ++ * DES library. ++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au ++ * Eric Young (eay@cryptsoft.com) ++ */ ++ ++#define DES_FCRYPT ++#include "des_locl.h" ++#undef DES_FCRYPT ++ ++#undef PERM_OP ++#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ ++ (b)^=(t),\ ++ (a)^=((t)<<(n))) ++ ++#undef HPERM_OP ++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ ++ (a)=(a)^(t)^(t>>(16-(n))))\ ++ ++void fcrypt_body(out, ks, Eswap0, Eswap1) ++DES_LONG *out; ++des_key_schedule ks; ++DES_LONG Eswap0; ++DES_LONG Eswap1; ++ { ++ register DES_LONG l,r,t,u; ++#ifdef DES_PTR ++ register unsigned char *des_SP=(unsigned char *)des_SPtrans; ++#endif ++ register DES_LONG *s; ++ register int j; ++ register DES_LONG E0,E1; ++ ++ l=0; ++ r=0; ++ ++ s=(DES_LONG *)ks; ++ E0=Eswap0; ++ E1=Eswap1; ++ ++ for (j=0; j<25; j++) ++ { ++#ifdef DES_UNROLL ++ register int i; ++ ++ for (i=0; i<32; i+=8) ++ { ++ D_ENCRYPT(l,r,i+0); /* 1 */ ++ D_ENCRYPT(r,l,i+2); /* 2 */ ++ D_ENCRYPT(l,r,i+4); /* 1 */ ++ D_ENCRYPT(r,l,i+6); /* 2 */ ++ } ++#else ++ D_ENCRYPT(l,r, 0); /* 1 */ ++ D_ENCRYPT(r,l, 2); /* 2 */ ++ D_ENCRYPT(l,r, 4); /* 3 */ ++ D_ENCRYPT(r,l, 6); /* 4 */ ++ D_ENCRYPT(l,r, 8); /* 5 */ ++ D_ENCRYPT(r,l,10); /* 6 */ ++ D_ENCRYPT(l,r,12); /* 7 */ ++ D_ENCRYPT(r,l,14); /* 8 */ ++ D_ENCRYPT(l,r,16); /* 9 */ ++ D_ENCRYPT(r,l,18); /* 10 */ ++ D_ENCRYPT(l,r,20); /* 11 */ ++ D_ENCRYPT(r,l,22); /* 12 */ ++ D_ENCRYPT(l,r,24); /* 13 */ ++ D_ENCRYPT(r,l,26); /* 14 */ ++ D_ENCRYPT(l,r,28); /* 15 */ ++ D_ENCRYPT(r,l,30); /* 16 */ ++#endif ++ ++ t=l; ++ l=r; ++ r=t; ++ } ++ l=ROTATE(l,3)&0xffffffffL; ++ r=ROTATE(r,3)&0xffffffffL; ++ ++ PERM_OP(l,r,t, 1,0x55555555L); ++ PERM_OP(r,l,t, 8,0x00ff00ffL); ++ PERM_OP(l,r,t, 2,0x33333333L); ++ PERM_OP(r,l,t,16,0x0000ffffL); ++ PERM_OP(l,r,t, 4,0x0f0f0f0fL); ++ ++ out[0]=r; ++ out[1]=l; ++ } ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/options.txt Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,39 @@ ++Note that the UNROLL option makes the 'inner' des loop unroll all 16 rounds ++instead of the default 4. ++RISC1 and RISC2 are 2 alternatives for the inner loop and ++PTR means to use pointers arithmatic instead of arrays. ++ ++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - assembler 577,000 4620k/s ++IRIX 6.2 - R10000 195mhz - cc (-O3 -n32) - UNROLL RISC2 PTR 496,000 3968k/s ++solaris 2.5.1 usparc 167mhz?? - SC4.0 - UNROLL RISC1 PTR [1] 459,400 3672k/s ++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - UNROLL RISC1 433,000 3468k/s ++solaris 2.5.1 usparc 167mhz?? - gcc 2.7.2 - UNROLL 380,000 3041k/s ++linux - pentium 100mhz - gcc 2.7.0 - assembler 281,000 2250k/s ++NT 4.0 - pentium 100mhz - VC 4.2 - assembler 281,000 2250k/s ++AIX 4.1? - PPC604 100mhz - cc - UNROLL 275,000 2200k/s ++IRIX 5.3 - R4400 200mhz - gcc 2.6.3 - UNROLL RISC2 PTR 235,300 1882k/s ++IRIX 5.3 - R4400 200mhz - cc - UNROLL RISC2 PTR 233,700 1869k/s ++NT 4.0 - pentium 100mhz - VC 4.2 - UNROLL RISC1 PTR 191,000 1528k/s ++DEC Alpha 165mhz?? - cc - RISC2 PTR [2] 181,000 1448k/s ++linux - pentium 100mhz - gcc 2.7.0 - UNROLL RISC1 PTR 158,500 1268k/s ++HPUX 10 - 9000/887 - cc - UNROLL [3] 148,000 1190k/s ++solaris 2.5.1 - sparc 10 50mhz - gcc 2.7.2 - UNROLL 123,600 989k/s ++IRIX 5.3 - R4000 100mhz - cc - UNROLL RISC2 PTR 101,000 808k/s ++DGUX - 88100 50mhz(?) - gcc 2.6.3 - UNROLL 81,000 648k/s ++solaris 2.4 486 50mhz - gcc 2.6.3 - assembler 65,000 522k/s ++HPUX 10 - 9000/887 - k&r cc (default compiler) - UNROLL PTR 76,000 608k/s ++solaris 2.4 486 50mhz - gcc 2.6.3 - UNROLL RISC2 43,500 344k/s ++AIX - old slow one :-) - cc - 39,000 312k/s ++ ++Notes. ++[1] For the ultra sparc, SunC 4.0 ++ cc -xtarget=ultra -xarch=v8plus -Xa -xO5, running 'des_opts' ++ gives a speed of 344,000 des/s while 'speed' gives 459,000 des/s. ++ I'll record the higher since it is coming from the library but it ++ is all rather weird. ++[2] Similar to the ultra sparc ([1]), 181,000 for 'des_opts' vs 175,000. ++[3] I was unable to get access to this machine when it was not heavily loaded. ++ As such, my timing program was never able to get more that %30 of the CPU. ++ This would cause the program to give much lower speed numbers because ++ it would be 'fighting' to stay in the cache with the other CPU burning ++ processes. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/podd.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,75 @@ ++/* crypto/des/podd.h */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++static const unsigned char odd_parity[256]={ ++ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, ++ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, ++ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, ++ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, ++ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, ++ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, ++ 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, ++112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, ++128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, ++145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, ++161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, ++176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, ++193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, ++208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, ++224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, ++241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/set_key.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,246 @@ ++/* crypto/des/set_key.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* set_key.c v 1.4 eay 24/9/91 ++ * 1.4 Speed up by 400% :-) ++ * 1.3 added register declarations. ++ * 1.2 unrolled make_key_sched a bit more ++ * 1.1 added norm_expand_bits ++ * 1.0 First working version ++ */ ++#include "des_locl.h" ++#include "podd.h" ++#include "sk.h" ++ ++#ifndef NOPROTO ++static int check_parity(des_cblock (*key)); ++#else ++static int check_parity(); ++#endif ++ ++int des_check_key=0; ++ ++void des_set_odd_parity(key) ++des_cblock (*key); ++ { ++ int i; ++ ++ for (i=0; i>(n))^(b))&(m)),\ ++ * (b)^=(t),\ ++ * (a)=((a)^((t)<<(n)))) ++ */ ++ ++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ ++ (a)=(a)^(t)^(t>>(16-(n)))) ++ ++/* return 0 if key parity is odd (correct), ++ * return -1 if key parity error, ++ * return -2 if illegal weak key. ++ */ ++int des_set_key(key, schedule) ++des_cblock (*key); ++des_key_schedule schedule; ++ { ++ static int shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; ++ register DES_LONG c,d,t,s,t2; ++ register unsigned char *in; ++ register DES_LONG *k; ++ register int i; ++ ++ if (des_check_key) ++ { ++ if (!check_parity(key)) ++ return(-1); ++ ++ if (des_is_weak_key(key)) ++ return(-2); ++ } ++ ++ k=(DES_LONG *)schedule; ++ in=(unsigned char *)key; ++ ++ c2l(in,c); ++ c2l(in,d); ++ ++ /* do PC1 in 60 simple operations */ ++/* PERM_OP(d,c,t,4,0x0f0f0f0fL); ++ HPERM_OP(c,t,-2, 0xcccc0000L); ++ HPERM_OP(c,t,-1, 0xaaaa0000L); ++ HPERM_OP(c,t, 8, 0x00ff0000L); ++ HPERM_OP(c,t,-1, 0xaaaa0000L); ++ HPERM_OP(d,t,-8, 0xff000000L); ++ HPERM_OP(d,t, 8, 0x00ff0000L); ++ HPERM_OP(d,t, 2, 0x33330000L); ++ d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L); ++ d=(d>>8)|((c&0xf0000000L)>>4); ++ c&=0x0fffffffL; */ ++ ++ /* I now do it in 47 simple operations :-) ++ * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) ++ * for the inspiration. :-) */ ++ PERM_OP (d,c,t,4,0x0f0f0f0fL); ++ HPERM_OP(c,t,-2,0xcccc0000L); ++ HPERM_OP(d,t,-2,0xcccc0000L); ++ PERM_OP (d,c,t,1,0x55555555L); ++ PERM_OP (c,d,t,8,0x00ff00ffL); ++ PERM_OP (d,c,t,1,0x55555555L); ++ d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) | ++ ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L)); ++ c&=0x0fffffffL; ++ ++ for (i=0; i>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); } ++ else ++ { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); } ++ c&=0x0fffffffL; ++ d&=0x0fffffffL; ++ /* could be a few less shifts but I am to lazy at this ++ * point in time to investigate */ ++ s= des_skb[0][ (c )&0x3f ]| ++ des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]| ++ des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]| ++ des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) | ++ ((c>>22L)&0x38)]; ++ t= des_skb[4][ (d )&0x3f ]| ++ des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]| ++ des_skb[6][ (d>>15L)&0x3f ]| ++ des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)]; ++ ++ /* table contained 0213 4657 */ ++ t2=((t<<16L)|(s&0x0000ffffL))&0xffffffffL; ++ *(k++)=ROTATE(t2,30)&0xffffffffL; ++ ++ t2=((s>>16L)|(t&0xffff0000L)); ++ *(k++)=ROTATE(t2,26)&0xffffffffL; ++ } ++ return(0); ++ } ++ ++int des_key_sched(key, schedule) ++des_cblock (*key); ++des_key_schedule schedule; ++ { ++ return(des_set_key(key,schedule)); ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/sk.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,204 @@ ++/* crypto/des/sk.h */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++static const DES_LONG des_skb[8][64]={ ++{ ++/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ ++0x00000000L,0x00000010L,0x20000000L,0x20000010L, ++0x00010000L,0x00010010L,0x20010000L,0x20010010L, ++0x00000800L,0x00000810L,0x20000800L,0x20000810L, ++0x00010800L,0x00010810L,0x20010800L,0x20010810L, ++0x00000020L,0x00000030L,0x20000020L,0x20000030L, ++0x00010020L,0x00010030L,0x20010020L,0x20010030L, ++0x00000820L,0x00000830L,0x20000820L,0x20000830L, ++0x00010820L,0x00010830L,0x20010820L,0x20010830L, ++0x00080000L,0x00080010L,0x20080000L,0x20080010L, ++0x00090000L,0x00090010L,0x20090000L,0x20090010L, ++0x00080800L,0x00080810L,0x20080800L,0x20080810L, ++0x00090800L,0x00090810L,0x20090800L,0x20090810L, ++0x00080020L,0x00080030L,0x20080020L,0x20080030L, ++0x00090020L,0x00090030L,0x20090020L,0x20090030L, ++0x00080820L,0x00080830L,0x20080820L,0x20080830L, ++0x00090820L,0x00090830L,0x20090820L,0x20090830L, ++},{ ++/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ ++0x00000000L,0x02000000L,0x00002000L,0x02002000L, ++0x00200000L,0x02200000L,0x00202000L,0x02202000L, ++0x00000004L,0x02000004L,0x00002004L,0x02002004L, ++0x00200004L,0x02200004L,0x00202004L,0x02202004L, ++0x00000400L,0x02000400L,0x00002400L,0x02002400L, ++0x00200400L,0x02200400L,0x00202400L,0x02202400L, ++0x00000404L,0x02000404L,0x00002404L,0x02002404L, ++0x00200404L,0x02200404L,0x00202404L,0x02202404L, ++0x10000000L,0x12000000L,0x10002000L,0x12002000L, ++0x10200000L,0x12200000L,0x10202000L,0x12202000L, ++0x10000004L,0x12000004L,0x10002004L,0x12002004L, ++0x10200004L,0x12200004L,0x10202004L,0x12202004L, ++0x10000400L,0x12000400L,0x10002400L,0x12002400L, ++0x10200400L,0x12200400L,0x10202400L,0x12202400L, ++0x10000404L,0x12000404L,0x10002404L,0x12002404L, ++0x10200404L,0x12200404L,0x10202404L,0x12202404L, ++},{ ++/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ ++0x00000000L,0x00000001L,0x00040000L,0x00040001L, ++0x01000000L,0x01000001L,0x01040000L,0x01040001L, ++0x00000002L,0x00000003L,0x00040002L,0x00040003L, ++0x01000002L,0x01000003L,0x01040002L,0x01040003L, ++0x00000200L,0x00000201L,0x00040200L,0x00040201L, ++0x01000200L,0x01000201L,0x01040200L,0x01040201L, ++0x00000202L,0x00000203L,0x00040202L,0x00040203L, ++0x01000202L,0x01000203L,0x01040202L,0x01040203L, ++0x08000000L,0x08000001L,0x08040000L,0x08040001L, ++0x09000000L,0x09000001L,0x09040000L,0x09040001L, ++0x08000002L,0x08000003L,0x08040002L,0x08040003L, ++0x09000002L,0x09000003L,0x09040002L,0x09040003L, ++0x08000200L,0x08000201L,0x08040200L,0x08040201L, ++0x09000200L,0x09000201L,0x09040200L,0x09040201L, ++0x08000202L,0x08000203L,0x08040202L,0x08040203L, ++0x09000202L,0x09000203L,0x09040202L,0x09040203L, ++},{ ++/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ ++0x00000000L,0x00100000L,0x00000100L,0x00100100L, ++0x00000008L,0x00100008L,0x00000108L,0x00100108L, ++0x00001000L,0x00101000L,0x00001100L,0x00101100L, ++0x00001008L,0x00101008L,0x00001108L,0x00101108L, ++0x04000000L,0x04100000L,0x04000100L,0x04100100L, ++0x04000008L,0x04100008L,0x04000108L,0x04100108L, ++0x04001000L,0x04101000L,0x04001100L,0x04101100L, ++0x04001008L,0x04101008L,0x04001108L,0x04101108L, ++0x00020000L,0x00120000L,0x00020100L,0x00120100L, ++0x00020008L,0x00120008L,0x00020108L,0x00120108L, ++0x00021000L,0x00121000L,0x00021100L,0x00121100L, ++0x00021008L,0x00121008L,0x00021108L,0x00121108L, ++0x04020000L,0x04120000L,0x04020100L,0x04120100L, ++0x04020008L,0x04120008L,0x04020108L,0x04120108L, ++0x04021000L,0x04121000L,0x04021100L,0x04121100L, ++0x04021008L,0x04121008L,0x04021108L,0x04121108L, ++},{ ++/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ ++0x00000000L,0x10000000L,0x00010000L,0x10010000L, ++0x00000004L,0x10000004L,0x00010004L,0x10010004L, ++0x20000000L,0x30000000L,0x20010000L,0x30010000L, ++0x20000004L,0x30000004L,0x20010004L,0x30010004L, ++0x00100000L,0x10100000L,0x00110000L,0x10110000L, ++0x00100004L,0x10100004L,0x00110004L,0x10110004L, ++0x20100000L,0x30100000L,0x20110000L,0x30110000L, ++0x20100004L,0x30100004L,0x20110004L,0x30110004L, ++0x00001000L,0x10001000L,0x00011000L,0x10011000L, ++0x00001004L,0x10001004L,0x00011004L,0x10011004L, ++0x20001000L,0x30001000L,0x20011000L,0x30011000L, ++0x20001004L,0x30001004L,0x20011004L,0x30011004L, ++0x00101000L,0x10101000L,0x00111000L,0x10111000L, ++0x00101004L,0x10101004L,0x00111004L,0x10111004L, ++0x20101000L,0x30101000L,0x20111000L,0x30111000L, ++0x20101004L,0x30101004L,0x20111004L,0x30111004L, ++},{ ++/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ ++0x00000000L,0x08000000L,0x00000008L,0x08000008L, ++0x00000400L,0x08000400L,0x00000408L,0x08000408L, ++0x00020000L,0x08020000L,0x00020008L,0x08020008L, ++0x00020400L,0x08020400L,0x00020408L,0x08020408L, ++0x00000001L,0x08000001L,0x00000009L,0x08000009L, ++0x00000401L,0x08000401L,0x00000409L,0x08000409L, ++0x00020001L,0x08020001L,0x00020009L,0x08020009L, ++0x00020401L,0x08020401L,0x00020409L,0x08020409L, ++0x02000000L,0x0A000000L,0x02000008L,0x0A000008L, ++0x02000400L,0x0A000400L,0x02000408L,0x0A000408L, ++0x02020000L,0x0A020000L,0x02020008L,0x0A020008L, ++0x02020400L,0x0A020400L,0x02020408L,0x0A020408L, ++0x02000001L,0x0A000001L,0x02000009L,0x0A000009L, ++0x02000401L,0x0A000401L,0x02000409L,0x0A000409L, ++0x02020001L,0x0A020001L,0x02020009L,0x0A020009L, ++0x02020401L,0x0A020401L,0x02020409L,0x0A020409L, ++},{ ++/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ ++0x00000000L,0x00000100L,0x00080000L,0x00080100L, ++0x01000000L,0x01000100L,0x01080000L,0x01080100L, ++0x00000010L,0x00000110L,0x00080010L,0x00080110L, ++0x01000010L,0x01000110L,0x01080010L,0x01080110L, ++0x00200000L,0x00200100L,0x00280000L,0x00280100L, ++0x01200000L,0x01200100L,0x01280000L,0x01280100L, ++0x00200010L,0x00200110L,0x00280010L,0x00280110L, ++0x01200010L,0x01200110L,0x01280010L,0x01280110L, ++0x00000200L,0x00000300L,0x00080200L,0x00080300L, ++0x01000200L,0x01000300L,0x01080200L,0x01080300L, ++0x00000210L,0x00000310L,0x00080210L,0x00080310L, ++0x01000210L,0x01000310L,0x01080210L,0x01080310L, ++0x00200200L,0x00200300L,0x00280200L,0x00280300L, ++0x01200200L,0x01200300L,0x01280200L,0x01280300L, ++0x00200210L,0x00200310L,0x00280210L,0x00280310L, ++0x01200210L,0x01200310L,0x01280210L,0x01280310L, ++},{ ++/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ ++0x00000000L,0x04000000L,0x00040000L,0x04040000L, ++0x00000002L,0x04000002L,0x00040002L,0x04040002L, ++0x00002000L,0x04002000L,0x00042000L,0x04042000L, ++0x00002002L,0x04002002L,0x00042002L,0x04042002L, ++0x00000020L,0x04000020L,0x00040020L,0x04040020L, ++0x00000022L,0x04000022L,0x00040022L,0x04040022L, ++0x00002020L,0x04002020L,0x00042020L,0x04042020L, ++0x00002022L,0x04002022L,0x00042022L,0x04042022L, ++0x00000800L,0x04000800L,0x00040800L,0x04040800L, ++0x00000802L,0x04000802L,0x00040802L,0x04040802L, ++0x00002800L,0x04002800L,0x00042800L,0x04042800L, ++0x00002802L,0x04002802L,0x00042802L,0x04042802L, ++0x00000820L,0x04000820L,0x00040820L,0x04040820L, ++0x00000822L,0x04000822L,0x00040822L,0x04040822L, ++0x00002820L,0x04002820L,0x00042820L,0x04042820L, ++0x00002822L,0x04002822L,0x00042822L,0x04042822L, ++}}; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/speed.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,329 @@ ++/* crypto/des/speed.c */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* 11-Sep-92 Andrew Daviel Support for Silicon Graphics IRIX added */ ++/* 06-Apr-92 Luke Brennan Support for VMS and add extra signal calls */ ++ ++#ifndef MSDOS ++#define TIMES ++#endif ++ ++#include ++#ifndef MSDOS ++#include ++#else ++#include ++extern int exit(); ++#endif ++#include ++#ifndef VMS ++#ifndef _IRIX ++#include ++#endif ++#ifdef TIMES ++#include ++#include ++#endif ++#else /* VMS */ ++#include ++struct tms { ++ time_t tms_utime; ++ time_t tms_stime; ++ time_t tms_uchild; /* I dunno... */ ++ time_t tms_uchildsys; /* so these names are a guess :-) */ ++ } ++#endif ++#ifndef TIMES ++#include ++#endif ++ ++#ifdef sun ++#include ++#include ++#endif ++ ++#include "des_locl.h" ++ ++/* The following if from times(3) man page. It may need to be changed */ ++#ifndef HZ ++# ifndef CLK_TCK ++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */ ++# ifndef VMS ++# define HZ 100.0 ++# else /* VMS */ ++# define HZ 100.0 ++# endif ++# else /* _BSD_CLK_TCK_ */ ++# define HZ ((double)_BSD_CLK_TCK_) ++# endif ++# else /* CLK_TCK */ ++# define HZ ((double)CLK_TCK) ++# endif ++#endif ++ ++#define BUFSIZE ((long)1024) ++long run=0; ++ ++#ifndef NOPROTO ++double Time_F(int s); ++#else ++double Time_F(); ++#endif ++ ++#ifdef SIGALRM ++#if defined(__STDC__) || defined(sgi) || defined(_AIX) ++#define SIGRETTYPE void ++#else ++#define SIGRETTYPE int ++#endif ++ ++#ifndef NOPROTO ++SIGRETTYPE sig_done(int sig); ++#else ++SIGRETTYPE sig_done(); ++#endif ++ ++SIGRETTYPE sig_done(sig) ++int sig; ++ { ++ signal(SIGALRM,sig_done); ++ run=0; ++#ifdef LINT ++ sig=sig; ++#endif ++ } ++#endif ++ ++#define START 0 ++#define STOP 1 ++ ++double Time_F(s) ++int s; ++ { ++ double ret; ++#ifdef TIMES ++ static struct tms tstart,tend; ++ ++ if (s == START) ++ { ++ times(&tstart); ++ return(0); ++ } ++ else ++ { ++ times(&tend); ++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ; ++ return((ret == 0.0)?1e-6:ret); ++ } ++#else /* !times() */ ++ static struct timeb tstart,tend; ++ long i; ++ ++ if (s == START) ++ { ++ ftime(&tstart); ++ return(0); ++ } ++ else ++ { ++ ftime(&tend); ++ i=(long)tend.millitm-(long)tstart.millitm; ++ ret=((double)(tend.time-tstart.time))+((double)i)/1e3; ++ return((ret == 0.0)?1e-6:ret); ++ } ++#endif ++ } ++ ++int main(argc,argv) ++int argc; ++char **argv; ++ { ++ long count; ++ static unsigned char buf[BUFSIZE]; ++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; ++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12}; ++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34}; ++ des_key_schedule sch,sch2,sch3; ++ double a,b,c,d,e; ++#ifndef SIGALRM ++ long ca,cb,cc,cd,ce; ++#endif ++ ++#ifndef TIMES ++ printf("To get the most acurate results, try to run this\n"); ++ printf("program when this computer is idle.\n"); ++#endif ++ ++ des_set_key((C_Block *)key2,sch2); ++ des_set_key((C_Block *)key3,sch3); ++ ++#ifndef SIGALRM ++ printf("First we calculate the approximate speed ...\n"); ++ des_set_key((C_Block *)key,sch); ++ count=10; ++ do { ++ long i; ++ DES_LONG data[2]; ++ ++ count*=2; ++ Time_F(START); ++ for (i=count; i; i--) ++ des_encrypt(data,&(sch[0]),DES_ENCRYPT); ++ d=Time_F(STOP); ++ } while (d < 3.0); ++ ca=count; ++ cb=count*3; ++ cc=count*3*8/BUFSIZE+1; ++ cd=count*8/BUFSIZE+1; ++ ce=count/20+1; ++ printf("Doing set_key %ld times\n",ca); ++#define COND(d) (count != (d)) ++#define COUNT(d) (d) ++#else ++#define COND(c) (run) ++#define COUNT(d) (count) ++ signal(SIGALRM,sig_done); ++ printf("Doing set_key for 10 seconds\n"); ++ alarm(10); ++#endif ++ ++ Time_F(START); ++ for (count=0,run=1; COND(ca); count++) ++ des_set_key((C_Block *)key,sch); ++ d=Time_F(STOP); ++ printf("%ld set_key's in %.2f seconds\n",count,d); ++ a=((double)COUNT(ca))/d; ++ ++#ifdef SIGALRM ++ printf("Doing des_encrypt's for 10 seconds\n"); ++ alarm(10); ++#else ++ printf("Doing des_encrypt %ld times\n",cb); ++#endif ++ Time_F(START); ++ for (count=0,run=1; COND(cb); count++) ++ { ++ DES_LONG data[2]; ++ ++ des_encrypt(data,&(sch[0]),DES_ENCRYPT); ++ } ++ d=Time_F(STOP); ++ printf("%ld des_encrypt's in %.2f second\n",count,d); ++ b=((double)COUNT(cb)*8)/d; ++ ++#ifdef SIGALRM ++ printf("Doing des_cbc_encrypt on %ld byte blocks for 10 seconds\n", ++ BUFSIZE); ++ alarm(10); ++#else ++ printf("Doing des_cbc_encrypt %ld times on %ld byte blocks\n",cc, ++ BUFSIZE); ++#endif ++ Time_F(START); ++ for (count=0,run=1; COND(cc); count++) ++ des_ncbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,&(sch[0]), ++ (C_Block *)&(key[0]),DES_ENCRYPT); ++ d=Time_F(STOP); ++ printf("%ld des_cbc_encrypt's of %ld byte blocks in %.2f second\n", ++ count,BUFSIZE,d); ++ c=((double)COUNT(cc)*BUFSIZE)/d; ++ ++#ifdef SIGALRM ++ printf("Doing des_ede_cbc_encrypt on %ld byte blocks for 10 seconds\n", ++ BUFSIZE); ++ alarm(10); ++#else ++ printf("Doing des_ede_cbc_encrypt %ld times on %ld byte blocks\n",cd, ++ BUFSIZE); ++#endif ++ Time_F(START); ++ for (count=0,run=1; COND(cd); count++) ++ des_ede3_cbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE, ++ &(sch[0]), ++ &(sch2[0]), ++ &(sch3[0]), ++ (C_Block *)&(key[0]), ++ DES_ENCRYPT); ++ d=Time_F(STOP); ++ printf("%ld des_ede_cbc_encrypt's of %ld byte blocks in %.2f second\n", ++ count,BUFSIZE,d); ++ d=((double)COUNT(cd)*BUFSIZE)/d; ++ ++#ifdef SIGALRM ++ printf("Doing crypt for 10 seconds\n"); ++ alarm(10); ++#else ++ printf("Doing crypt %ld times\n",ce); ++#endif ++ Time_F(START); ++ for (count=0,run=1; COND(ce); count++) ++ crypt("testing1","ef"); ++ e=Time_F(STOP); ++ printf("%ld crypts in %.2f second\n",count,e); ++ e=((double)COUNT(ce))/e; ++ ++ printf("set_key per sec = %12.2f (%9.3fuS)\n",a,1.0e6/a); ++ printf("DES raw ecb bytes per sec = %12.2f (%9.3fuS)\n",b,8.0e6/b); ++ printf("DES cbc bytes per sec = %12.2f (%9.3fuS)\n",c,8.0e6/c); ++ printf("DES ede cbc bytes per sec = %12.2f (%9.3fuS)\n",d,8.0e6/d); ++ printf("crypt per sec = %12.2f (%9.3fuS)\n",e,1.0e6/e); ++ exit(0); ++#if defined(LINT) || defined(MSDOS) ++ return(0); ++#endif ++ } +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/crypto/ciphers/des/spr.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,204 @@ ++/* crypto/des/spr.h */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++const DES_LONG des_SPtrans[8][64]={ ++{ ++/* nibble 0 */ ++0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L, ++0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L, ++0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L, ++0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L, ++0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L, ++0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L, ++0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L, ++0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L, ++0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L, ++0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L, ++0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L, ++0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L, ++0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L, ++0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L, ++0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L, ++0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L, ++},{ ++/* nibble 1 */ ++0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L, ++0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L, ++0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L, ++0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L, ++0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L, ++0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L, ++0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L, ++0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L, ++0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L, ++0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L, ++0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L, ++0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L, ++0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L, ++0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L, ++0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L, ++0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L, ++},{ ++/* nibble 2 */ ++0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L, ++0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L, ++0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L, ++0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L, ++0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L, ++0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L, ++0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L, ++0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L, ++0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L, ++0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L, ++0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L, ++0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L, ++0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L, ++0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L, ++0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L, ++0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L, ++},{ ++/* nibble 3 */ ++0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L, ++0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L, ++0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L, ++0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L, ++0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L, ++0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L, ++0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L, ++0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L, ++0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L, ++0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L, ++0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L, ++0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L, ++0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L, ++0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L, ++0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L, ++0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L, ++},{ ++/* nibble 4 */ ++0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L, ++0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L, ++0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L, ++0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L, ++0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L, ++0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L, ++0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L, ++0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L, ++0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L, ++0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L, ++0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L, ++0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L, ++0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L, ++0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L, ++0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L, ++0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L, ++},{ ++/* nibble 5 */ ++0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L, ++0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L, ++0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L, ++0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L, ++0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L, ++0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L, ++0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L, ++0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L, ++0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L, ++0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L, ++0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L, ++0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L, ++0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L, ++0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L, ++0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L, ++0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L, ++},{ ++/* nibble 6 */ ++0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L, ++0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L, ++0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L, ++0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L, ++0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L, ++0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L, ++0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L, ++0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L, ++0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L, ++0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L, ++0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L, ++0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L, ++0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L, ++0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L, ++0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L, ++0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L, ++},{ ++/* nibble 7 */ ++0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L, ++0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L, ++0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L, ++0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L, ++0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L, ++0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L, ++0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L, ++0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L, ++0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L, ++0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L, ++0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L, ++0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L, ++0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L, ++0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L, ++0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L, ++0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, ++}}; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/crypto/aes.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,97 @@ ++// I retain copyright in this code but I encourage its free use provided ++// that I don't carry any responsibility for the results. I am especially ++// happy to see it used in free and open source software. If you do use ++// it I would appreciate an acknowledgement of its origin in the code or ++// the product that results and I would also appreciate knowing a little ++// about the use to which it is being put. I am grateful to Frank Yellin ++// for some ideas that are used in this implementation. ++// ++// Dr B. R. Gladman 6th April 2001. ++// ++// This is an implementation of the AES encryption algorithm (Rijndael) ++// designed by Joan Daemen and Vincent Rijmen. This version is designed ++// to provide both fixed and dynamic block and key lengths and can also ++// run with either big or little endian internal byte order (see aes.h). ++// It inputs block and key lengths in bytes with the legal values being ++// 16, 24 and 32. ++ ++/* ++ * Modified by Jari Ruusu, May 1 2001 ++ * - Fixed some compile warnings, code was ok but gcc warned anyway. ++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t ++ * - Major name space cleanup: Names visible to outside now begin ++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c ++ * - Removed C++ and DLL support as part of name space cleanup. ++ * - Eliminated unnecessary recomputation of tables. (actual bug fix) ++ * - Merged precomputed constant tables to aes.c file. ++ * - Removed data alignment restrictions for portability reasons. ++ * - Made block and key lengths accept bit count (128/192/256) ++ * as well byte count (16/24/32). ++ * - Removed all error checks. This change also eliminated the need ++ * to preinitialize the context struct to zero. ++ * - Removed some totally unused constants. ++ */ ++ ++#ifndef _AES_H ++#define _AES_H ++ ++#if defined(__linux__) && defined(__KERNEL__) ++# include ++#else ++# include ++#endif ++ ++// CONFIGURATION OPTIONS (see also aes.c) ++// ++// Define AES_BLOCK_SIZE to set the cipher block size (16, 24 or 32) or ++// leave this undefined for dynamically variable block size (this will ++// result in much slower code). ++// IMPORTANT NOTE: AES_BLOCK_SIZE is in BYTES (16, 24, 32 or undefined). If ++// left undefined a slower version providing variable block length is compiled ++ ++#define AES_BLOCK_SIZE 16 ++ ++// The number of key schedule words for different block and key lengths ++// allowing for method of computation which requires the length to be a ++// multiple of the key length ++// ++// Nk = 4 6 8 ++// ------------- ++// Nb = 4 | 60 60 64 ++// 6 | 96 90 96 ++// 8 | 120 120 120 ++ ++#if !defined(AES_BLOCK_SIZE) || (AES_BLOCK_SIZE == 32) ++#define AES_KS_LENGTH 120 ++#define AES_RC_LENGTH 29 ++#else ++#define AES_KS_LENGTH 4 * AES_BLOCK_SIZE ++#define AES_RC_LENGTH (9 * AES_BLOCK_SIZE) / 8 - 8 ++#endif ++ ++typedef struct ++{ ++ u_int32_t aes_Nkey; // the number of words in the key input block ++ u_int32_t aes_Nrnd; // the number of cipher rounds ++ u_int32_t aes_e_key[AES_KS_LENGTH]; // the encryption key schedule ++ u_int32_t aes_d_key[AES_KS_LENGTH]; // the decryption key schedule ++#if !defined(AES_BLOCK_SIZE) ++ u_int32_t aes_Ncol; // the number of columns in the cipher state ++#endif ++} aes_context; ++ ++// THE CIPHER INTERFACE ++ ++#if !defined(AES_BLOCK_SIZE) ++extern void aes_set_blk(aes_context *, const int); ++#endif ++extern void aes_set_key(aes_context *, const unsigned char [], const int, const int); ++extern void aes_encrypt(const aes_context *, const unsigned char [], unsigned char []); ++extern void aes_decrypt(const aes_context *, const unsigned char [], unsigned char []); ++ ++// The block length inputs to aes_set_block and aes_set_key are in numbers ++// of bytes or bits. The calls to subroutines must be made in the above ++// order but multiple calls can be made without repeating earlier calls ++// if their parameters have not changed. ++ ++#endif // _AES_H +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/crypto/aes_cbc.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,4 @@ ++/* Glue header */ ++#include "aes.h" ++int AES_set_key(aes_context *aes_ctx, const u_int8_t * key, int keysize); ++int AES_cbc_encrypt(aes_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt); +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/crypto/aes_xcbc_mac.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,12 @@ ++#ifndef _AES_XCBC_MAC_H ++#define _AES_XCBC_MAC_H ++ ++typedef u_int32_t aes_block[4]; ++typedef struct { ++ aes_context ctx_k1; ++ aes_block k2; ++ aes_block k3; ++} aes_context_mac; ++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen); ++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]); ++#endif /* _AES_XCBC_MAC_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/crypto/cbc_generic.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,110 @@ ++#ifndef _CBC_GENERIC_H ++#define _CBC_GENERIC_H ++/* ++ * CBC macro helpers ++ * ++ * Author: JuanJo Ciarlante ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ */ ++ ++/* ++ * Heavily inspired in loop_AES ++ */ ++#define CBC_IMPL_BLK16(name, ctx_type, addr_type, enc_func, dec_func) \ ++int name(ctx_type *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \ ++ int ret=ilen, pos; \ ++ const u_int32_t *iv_i; \ ++ if ((ilen) % 16) return 0; \ ++ if (encrypt) { \ ++ pos=0; \ ++ while(pos=0) { \ ++ dec_func(ctx, (const addr_type) in, (addr_type) out); \ ++ if (pos==0) \ ++ iv_i=(const u_int32_t*) (iv); \ ++ else \ ++ iv_i=(const u_int32_t*) (in-16); \ ++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \ ++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \ ++ *((u_int32_t *)(&out[ 8])) ^= iv_i[2]; \ ++ *((u_int32_t *)(&out[12])) ^= iv_i[3]; \ ++ in-=16; \ ++ out-=16; \ ++ pos-=16; \ ++ } \ ++ } \ ++ return ret; \ ++} ++#define CBC_IMPL_BLK8(name, ctx_type, addr_type, enc_func, dec_func) \ ++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \ ++ int ret=ilen, pos; \ ++ const u_int32_t *iv_i; \ ++ if ((ilen) % 8) return 0; \ ++ if (encrypt) { \ ++ pos=0; \ ++ while(pos=0) { \ ++ dec_func(ctx, (const addr_type)in, (addr_type)out); \ ++ if (pos==0) \ ++ iv_i=(const u_int32_t*) (iv); \ ++ else \ ++ iv_i=(const u_int32_t*) (in-8); \ ++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \ ++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \ ++ in-=8; \ ++ out-=8; \ ++ pos-=8; \ ++ } \ ++ } \ ++ return ret; \ ++} ++#define CBC_DECL(name, ctx_type) \ ++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) ++/* ++Eg.: ++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt); ++CBC_DECL(AES_cbc_encrypt, aes_context); ++*/ ++#endif /* _CBC_GENERIC_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/crypto/des.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,298 @@ ++/* crypto/des/des.org */ ++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) ++ * All rights reserved. ++ * ++ * This package is an SSL implementation written ++ * by Eric Young (eay@cryptsoft.com). ++ * The implementation was written so as to conform with Netscapes SSL. ++ * ++ * This library is free for commercial and non-commercial use as long as ++ * the following conditions are aheared to. The following conditions ++ * apply to all code found in this distribution, be it the RC4, RSA, ++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation ++ * included with this distribution is covered by the same copyright terms ++ * except that the holder is Tim Hudson (tjh@cryptsoft.com). ++ * ++ * Copyright remains Eric Young's, and as such any Copyright notices in ++ * the code are not to be removed. ++ * If this package is used in a product, Eric Young should be given attribution ++ * as the author of the parts of the library used. ++ * This can be in the form of a textual message at program startup or ++ * in documentation (online or textual) provided with the package. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * "This product includes cryptographic software written by ++ * Eric Young (eay@cryptsoft.com)" ++ * The word 'cryptographic' can be left out if the rouines from the library ++ * being used are not cryptographic related :-). ++ * 4. If you include any Windows specific code (or a derivative thereof) from ++ * the apps directory (application code) you must include an acknowledgement: ++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" ++ * ++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * The licence and distribution terms for any publically available version or ++ * derivative of this code cannot be changed. i.e. this code cannot simply be ++ * copied and put under another distribution licence ++ * [including the GNU Public Licence.] ++ */ ++ ++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ++ * ++ * Always modify des.org since des.h is automatically generated from ++ * it during SSLeay configuration. ++ * ++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ++ */ ++ ++#ifndef HEADER_DES_H ++#define HEADER_DES_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a ++ * %20 speed up (longs are 8 bytes, int's are 4). */ ++/* Must be unsigned int on ia64/Itanium or DES breaks badly */ ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif ++ ++#ifndef DES_LONG ++#define DES_LONG u_int32_t ++#endif ++ ++typedef unsigned char des_cblock[8]; ++typedef struct { des_cblock ks; } des_key_schedule[16]; ++ ++#define DES_KEY_SZ (sizeof(des_cblock)) ++#define DES_SCHEDULE_SZ (sizeof(des_key_schedule)) ++ ++#define DES_ENCRYPT 1 ++#define DES_DECRYPT 0 ++ ++#define DES_CBC_MODE 0 ++#define DES_PCBC_MODE 1 ++ ++#define des_ecb2_encrypt(i,o,k1,k2,e) \ ++ des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) ++ ++#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ ++ des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) ++ ++#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ ++ des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) ++ ++#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ ++ des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) ++ ++#define C_Block des_cblock ++#define Key_schedule des_key_schedule ++#ifdef KERBEROS ++#define ENCRYPT DES_ENCRYPT ++#define DECRYPT DES_DECRYPT ++#endif ++#define KEY_SZ DES_KEY_SZ ++#define string_to_key des_string_to_key ++#define read_pw_string des_read_pw_string ++#define random_key des_random_key ++#define pcbc_encrypt des_pcbc_encrypt ++#define set_key des_set_key ++#define key_sched des_key_sched ++#define ecb_encrypt des_ecb_encrypt ++#define cbc_encrypt des_cbc_encrypt ++#define ncbc_encrypt des_ncbc_encrypt ++#define xcbc_encrypt des_xcbc_encrypt ++#define cbc_cksum des_cbc_cksum ++#define quad_cksum des_quad_cksum ++ ++/* For compatibility with the MIT lib - eay 20/05/92 */ ++typedef des_key_schedule bit_64; ++#define des_fixup_key_parity des_set_odd_parity ++#define des_check_key_parity check_parity ++ ++extern int des_check_key; /* defaults to false */ ++extern int des_rw_mode; /* defaults to DES_PCBC_MODE */ ++ ++/* The next line is used to disable full ANSI prototypes, if your ++ * compiler has problems with the prototypes, make sure this line always ++ * evaluates to true :-) */ ++#if defined(MSDOS) || defined(__STDC__) ++#undef NOPROTO ++#endif ++#ifndef NOPROTO ++char *des_options(void); ++void des_ecb3_encrypt(des_cblock *input,des_cblock *output, ++ des_key_schedule ks1,des_key_schedule ks2, ++ des_key_schedule ks3, int enc); ++DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output, ++ long length,des_key_schedule schedule,des_cblock *ivec); ++void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length, ++ des_key_schedule schedule,des_cblock *ivec,int enc); ++void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length, ++ des_key_schedule schedule,des_cblock *ivec,int enc); ++void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length, ++ des_key_schedule schedule,des_cblock *ivec, ++ des_cblock *inw,des_cblock *outw,int enc); ++void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits, ++ long length,des_key_schedule schedule,des_cblock *ivec,int enc); ++void des_ecb_encrypt(des_cblock *input,des_cblock *output, ++ des_key_schedule ks,int enc); ++void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc); ++void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc); ++void des_encrypt3(DES_LONG *data, des_key_schedule ks1, ++ des_key_schedule ks2, des_key_schedule ks3); ++void des_decrypt3(DES_LONG *data, des_key_schedule ks1, ++ des_key_schedule ks2, des_key_schedule ks3); ++void 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); ++void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out, ++ long length, des_key_schedule ks1, des_key_schedule ks2, ++ des_key_schedule ks3, des_cblock *ivec, int *num, int enc); ++void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out, ++ long length, des_key_schedule ks1, des_key_schedule ks2, ++ des_key_schedule ks3, des_cblock *ivec, int *num); ++ ++void des_xwhite_in2out(des_cblock (*des_key), des_cblock (*in_white), ++ des_cblock (*out_white)); ++ ++int des_enc_read(int fd,char *buf,int len,des_key_schedule sched, ++ des_cblock *iv); ++int des_enc_write(int fd,char *buf,int len,des_key_schedule sched, ++ des_cblock *iv); ++char *des_fcrypt(const char *buf,const char *salt, char *ret); ++#ifdef PERL5 ++char *des_crypt(const char *buf,const char *salt); ++#else ++/* some stupid compilers complain because I have declared char instead ++ * of const char */ ++#ifndef __KERNEL__ ++#ifdef HEADER_DES_LOCL_H ++char *crypt(const char *buf,const char *salt); ++#else /* HEADER_DES_LOCL_H */ ++char *crypt(void); ++#endif /* HEADER_DES_LOCL_H */ ++#endif /* __KERNEL__ */ ++#endif /* PERL5 */ ++void des_ofb_encrypt(unsigned char *in,unsigned char *out, ++ int numbits,long length,des_key_schedule schedule,des_cblock *ivec); ++void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length, ++ des_key_schedule schedule,des_cblock *ivec,int enc); ++DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output, ++ long length,int out_count,des_cblock *seed); ++void des_random_seed(des_cblock key); ++void des_random_key(des_cblock ret); ++int des_read_password(des_cblock *key,char *prompt,int verify); ++int des_read_2passwords(des_cblock *key1,des_cblock *key2, ++ char *prompt,int verify); ++int des_read_pw_string(char *buf,int length,char *prompt,int verify); ++void des_set_odd_parity(des_cblock *key); ++int des_is_weak_key(des_cblock *key); ++int des_set_key(des_cblock *key,des_key_schedule schedule); ++int des_key_sched(des_cblock *key,des_key_schedule schedule); ++void des_string_to_key(char *str,des_cblock *key); ++void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2); ++void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, ++ des_key_schedule schedule, des_cblock *ivec, int *num, int enc); ++void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, ++ des_key_schedule schedule, des_cblock *ivec, int *num); ++int des_read_pw(char *buf, char *buff, int size, char *prompt, int verify); ++ ++/* Extra functions from Mark Murray */ ++/* The following functions are not in the normal unix build or the ++ * SSLeay build. When using the SSLeay build, use RAND_seed() ++ * and RAND_bytes() instead. */ ++int des_new_random_key(des_cblock *key); ++void des_init_random_number_generator(des_cblock *key); ++void des_set_random_generator_seed(des_cblock *key); ++void des_set_sequence_number(des_cblock new_sequence_number); ++void des_generate_random_block(des_cblock *block); ++ ++#else ++ ++char *des_options(); ++void des_ecb3_encrypt(); ++DES_LONG des_cbc_cksum(); ++void des_cbc_encrypt(); ++void des_ncbc_encrypt(); ++void des_xcbc_encrypt(); ++void des_cfb_encrypt(); ++void des_ede3_cfb64_encrypt(); ++void des_ede3_ofb64_encrypt(); ++void des_ecb_encrypt(); ++void des_encrypt(); ++void des_encrypt2(); ++void des_encrypt3(); ++void des_decrypt3(); ++void des_ede3_cbc_encrypt(); ++int des_enc_read(); ++int des_enc_write(); ++char *des_fcrypt(); ++#ifdef PERL5 ++char *des_crypt(); ++#else ++char *crypt(); ++#endif ++void des_ofb_encrypt(); ++void des_pcbc_encrypt(); ++DES_LONG des_quad_cksum(); ++void des_random_seed(); ++void des_random_key(); ++int des_read_password(); ++int des_read_2passwords(); ++int des_read_pw_string(); ++void des_set_odd_parity(); ++int des_is_weak_key(); ++int des_set_key(); ++int des_key_sched(); ++void des_string_to_key(); ++void des_string_to_2keys(); ++void des_cfb64_encrypt(); ++void des_ofb64_encrypt(); ++int des_read_pw(); ++void des_xwhite_in2out(); ++ ++/* Extra functions from Mark Murray */ ++/* The following functions are not in the normal unix build or the ++ * SSLeay build. When using the SSLeay build, use RAND_seed() ++ * and RAND_bytes() instead. */ ++#ifdef FreeBSD ++int des_new_random_key(); ++void des_init_random_number_generator(); ++void des_set_random_generator_seed(); ++void des_set_sequence_number(); ++void des_generate_random_block(); ++#endif ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/mast.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,33 @@ ++struct mast_callbacks { ++ int (*packet_encap)(struct device *mast, void *context, ++ struct sk_buff *skb, int flowref); ++ int (*link_inquire)(struct device *mast, void *context); ++}; ++ ++ ++struct device *mast_init (int family, ++ struct mast_callbacks *callbacks, ++ unsigned int flags, ++ unsigned int desired_unit, ++ unsigned int max_flowref, ++ void *context); ++ ++int mast_destroy(struct device *mast); ++ ++int mast_recv(struct device *mast, struct sk_buff *skb, int flowref); ++ ++/* free this skb as being useless, increment failure count. */ ++int mast_toast(struct device *mast, struct sk_buff *skb, int flowref); ++ ++int mast_linkstat (struct device *mast, int flowref, ++ int status); ++ ++int mast_setreference (struct device *mast, ++ int defaultSA); ++ ++int mast_setneighbor (struct device *mast, ++ struct sockaddr *source, ++ struct sockaddr *destination, ++ int flowref); ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,513 @@ ++#ifndef _OPENSWAN_H ++/* ++ * header file for FreeS/WAN library functions ++ * Copyright (C) 1998, 1999, 2000 Henry Spencer. ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: openswan.h,v 1.89 2004/06/08 00:53:13 mcr Exp $ ++ */ ++#define _OPENSWAN_H /* seen it, no need to see it again */ ++ ++ ++ ++/* ++ * We've just got to have some datatypes defined... And annoyingly, just ++ * where we get them depends on whether we're in userland or not. ++ */ ++/* things that need to come from one place or the other, depending */ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include ++#include ++#define assert(foo) /* nothing */ ++#else ++#include ++#include ++#include ++#include ++#include ++#include ++ ++# define uint8_t u_int8_t ++# define uint16_t u_int16_t ++# define uint32_t u_int32_t ++# define uint64_t u_int64_t ++ ++ ++# define DEBUG_NO_STATIC static ++ ++#endif ++ ++#include ++ ++ ++/* ++ * Grab the kernel version to see if we have NET_21, and therefore ++ * IPv6. Some of this is repeated from ipsec_kversions.h. Of course, ++ * we aren't really testing if the kernel has IPv6, but rather if the ++ * the include files do. ++ */ ++#include ++#ifndef KERNEL_VERSION ++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) ++#define NET_21 ++#endif ++ ++#ifndef IPPROTO_COMP ++# define IPPROTO_COMP 108 ++#endif /* !IPPROTO_COMP */ ++ ++#ifndef IPPROTO_INT ++# define IPPROTO_INT 61 ++#endif /* !IPPROTO_INT */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++#ifndef DEBUG_NO_STATIC ++# define DEBUG_NO_STATIC ++#endif ++#else /* CONFIG_IPSEC_DEBUG */ ++#ifndef DEBUG_NO_STATIC ++# define DEBUG_NO_STATIC static ++#endif ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL /* KERNEL ifdef */ ++#ifndef NAT_TRAVERSAL ++#define NAT_TRAVERSAL ++#endif ++#endif ++#ifdef NAT_TRAVERSAL ++#define ESPINUDP_WITH_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ ++#define ESPINUDP_WITH_NON_ESP 2 /* draft-ietf-ipsec-nat-t-ike-02 */ ++#endif ++ ++/* ++ * Basic data types for the address-handling functions. ++ * ip_address and ip_subnet are supposed to be opaque types; do not ++ * use their definitions directly, they are subject to change! ++ */ ++ ++/* first, some quick fakes in case we're on an old system with no IPv6 */ ++#ifndef s6_addr16 ++struct in6_addr { ++ union ++ { ++ __u8 u6_addr8[16]; ++ __u16 u6_addr16[8]; ++ __u32 u6_addr32[4]; ++ } in6_u; ++#define s6_addr in6_u.u6_addr8 ++#define s6_addr16 in6_u.u6_addr16 ++#define s6_addr32 in6_u.u6_addr32 ++}; ++struct sockaddr_in6 { ++ unsigned short int sin6_family; /* AF_INET6 */ ++ __u16 sin6_port; /* Transport layer port # */ ++ __u32 sin6_flowinfo; /* IPv6 flow information */ ++ struct in6_addr sin6_addr; /* IPv6 address */ ++ __u32 sin6_scope_id; /* scope id (new in RFC2553) */ ++}; ++#endif /* !s6_addr16 */ ++ ++/* then the main types */ ++typedef struct { ++ union { ++ struct sockaddr_in v4; ++ struct sockaddr_in6 v6; ++ } u; ++} ip_address; ++typedef struct { ++ ip_address addr; ++ int maskbits; ++} ip_subnet; ++ ++/* and the SA ID stuff */ ++#ifdef __KERNEL__ ++typedef __u32 ipsec_spi_t; ++#else ++typedef u_int32_t ipsec_spi_t; ++#endif ++typedef struct { /* to identify an SA, we need: */ ++ ip_address dst; /* A. destination host */ ++ ipsec_spi_t spi; /* B. 32-bit SPI, assigned by dest. host */ ++# define SPI_PASS 256 /* magic values... */ ++# define SPI_DROP 257 /* ...for use... */ ++# define SPI_REJECT 258 /* ...with SA_INT */ ++# define SPI_HOLD 259 ++# define SPI_TRAP 260 ++# define SPI_TRAPSUBNET 261 ++ int proto; /* C. protocol */ ++# define SA_ESP 50 /* IPPROTO_ESP */ ++# define SA_AH 51 /* IPPROTO_AH */ ++# define SA_IPIP 4 /* IPPROTO_IPIP */ ++# define SA_COMP 108 /* IPPROTO_COMP */ ++# define SA_INT 61 /* IANA reserved for internal use */ ++} ip_said; ++ ++/* misc */ ++typedef const char *err_t; /* error message, or NULL for success */ ++struct prng { /* pseudo-random-number-generator guts */ ++ unsigned char sbox[256]; ++ int i, j; ++ unsigned long count; ++}; ++ ++ ++/* ++ * definitions for user space, taken from freeswan/ipsec_sa.h ++ */ ++typedef uint32_t IPsecSAref_t; ++ ++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t)) ++ ++#define IPsecSAref2NFmark(x) ((x) << (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) ++#define NFmark2IPsecSAref(x) ((x) >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) ++ ++#define IPSEC_SAREF_NULL (~((IPsecSAref_t)0)) ++ ++/* GCC magic for use in function definitions! */ ++#ifdef GCC_LINT ++# define PRINTF_LIKE(n) __attribute__ ((format(printf, n, n+1))) ++# define NEVER_RETURNS __attribute__ ((noreturn)) ++# define UNUSED __attribute__ ((unused)) ++# define BLANK_FORMAT " " /* GCC_LINT whines about empty formats */ ++#else ++# define PRINTF_LIKE(n) /* ignore */ ++# define NEVER_RETURNS /* ignore */ ++# define UNUSED /* ignore */ ++# define BLANK_FORMAT "" ++#endif ++ ++ ++ ++ ++ ++/* ++ * new IPv6-compatible functions ++ */ ++ ++/* text conversions */ ++err_t ttoul(const char *src, size_t srclen, int format, unsigned long *dst); ++size_t ultot(unsigned long src, int format, char *buf, size_t buflen); ++#define ULTOT_BUF (22+1) /* holds 64 bits in octal */ ++err_t ttoaddr(const char *src, size_t srclen, int af, ip_address *dst); ++err_t tnatoaddr(const char *src, size_t srclen, int af, ip_address *dst); ++size_t addrtot(const ip_address *src, int format, char *buf, size_t buflen); ++/* RFC 1886 old IPv6 reverse-lookup format is the bulkiest */ ++#define ADDRTOT_BUF (32*2 + 3 + 1 + 3 + 1 + 1) ++err_t ttosubnet(const char *src, size_t srclen, int af, ip_subnet *dst); ++size_t subnettot(const ip_subnet *src, int format, char *buf, size_t buflen); ++#define SUBNETTOT_BUF (ADDRTOT_BUF + 1 + 3) ++size_t subnetporttot(const ip_subnet *src, int format, char *buf, size_t buflen); ++#define SUBNETPROTOTOT_BUF (SUBNETTOTO_BUF + ULTOT_BUF) ++err_t ttosa(const char *src, size_t srclen, ip_said *dst); ++size_t satot(const ip_said *src, int format, char *bufptr, size_t buflen); ++#define SATOT_BUF (5 + ULTOA_BUF + 1 + ADDRTOT_BUF) ++err_t ttodata(const char *src, size_t srclen, int base, char *buf, ++ size_t buflen, size_t *needed); ++err_t ttodatav(const char *src, size_t srclen, int base, ++ char *buf, size_t buflen, size_t *needed, ++ char *errp, size_t errlen, unsigned int flags); ++#define TTODATAV_BUF 40 /* ttodatav's largest non-literal message */ ++#define TTODATAV_IGNORESPACE (1<<1) /* ignore spaces in base64 encodings*/ ++#define TTODATAV_SPACECOUNTS 0 /* do not ignore spaces in base64 */ ++ ++size_t datatot(const char *src, size_t srclen, int format, char *buf, ++ size_t buflen); ++size_t keyblobtoid(const unsigned char *src, size_t srclen, char *dst, ++ size_t dstlen); ++size_t splitkeytoid(const unsigned char *e, size_t elen, const unsigned char *m, ++ size_t mlen, char *dst, size_t dstlen); ++#define KEYID_BUF 10 /* up to 9 text digits plus NUL */ ++err_t ttoprotoport(char *src, size_t src_len, u_int8_t *proto, u_int16_t *port, ++ int *has_port_wildcard); ++ ++/* initializations */ ++void initsaid(const ip_address *addr, ipsec_spi_t spi, int proto, ip_said *dst); ++err_t loopbackaddr(int af, ip_address *dst); ++err_t unspecaddr(int af, ip_address *dst); ++err_t anyaddr(int af, ip_address *dst); ++err_t initaddr(const unsigned char *src, size_t srclen, int af, ip_address *dst); ++err_t initsubnet(const ip_address *addr, int maskbits, int clash, ip_subnet *dst); ++err_t addrtosubnet(const ip_address *addr, ip_subnet *dst); ++ ++/* misc. conversions and related */ ++err_t rangetosubnet(const ip_address *from, const ip_address *to, ip_subnet *dst); ++int addrtypeof(const ip_address *src); ++int subnettypeof(const ip_subnet *src); ++size_t addrlenof(const ip_address *src); ++size_t addrbytesptr(const ip_address *src, const unsigned char **dst); ++size_t addrbytesof(const ip_address *src, unsigned char *dst, size_t dstlen); ++int masktocount(const ip_address *src); ++void networkof(const ip_subnet *src, ip_address *dst); ++void maskof(const ip_subnet *src, ip_address *dst); ++ ++/* tests */ ++int sameaddr(const ip_address *a, const ip_address *b); ++int addrcmp(const ip_address *a, const ip_address *b); ++int samesubnet(const ip_subnet *a, const ip_subnet *b); ++int addrinsubnet(const ip_address *a, const ip_subnet *s); ++int subnetinsubnet(const ip_subnet *a, const ip_subnet *b); ++int subnetishost(const ip_subnet *s); ++int samesaid(const ip_said *a, const ip_said *b); ++int sameaddrtype(const ip_address *a, const ip_address *b); ++int samesubnettype(const ip_subnet *a, const ip_subnet *b); ++int isanyaddr(const ip_address *src); ++int isunspecaddr(const ip_address *src); ++int isloopbackaddr(const ip_address *src); ++ ++/* low-level grot */ ++int portof(const ip_address *src); ++void setportof(int port, ip_address *dst); ++struct sockaddr *sockaddrof(ip_address *src); ++size_t sockaddrlenof(const ip_address *src); ++ ++/* PRNG */ ++void prng_init(struct prng *prng, const unsigned char *key, size_t keylen); ++void prng_bytes(struct prng *prng, unsigned char *dst, size_t dstlen); ++unsigned long prng_count(struct prng *prng); ++void prng_final(struct prng *prng); ++ ++/* odds and ends */ ++const char *ipsec_version_code(void); ++const char *ipsec_version_string(void); ++const char **ipsec_copyright_notice(void); ++ ++const char *dns_string_rr(int rr, char *buf, int bufsize); ++const char *dns_string_datetime(time_t seconds, ++ char *buf, ++ int bufsize); ++ ++ ++/* ++ * old functions, to be deleted eventually ++ */ ++ ++/* unsigned long */ ++const char * /* NULL for success, else string literal */ ++atoul( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ int base, /* 0 means figure it out */ ++ unsigned long *resultp ++); ++size_t /* space needed for full conversion */ ++ultoa( ++ unsigned long n, ++ int base, ++ char *dst, ++ size_t dstlen ++); ++#define ULTOA_BUF 21 /* just large enough for largest result, */ ++ /* assuming 64-bit unsigned long! */ ++ ++/* Internet addresses */ ++const char * /* NULL for success, else string literal */ ++atoaddr( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ struct in_addr *addr ++); ++size_t /* space needed for full conversion */ ++addrtoa( ++ struct in_addr addr, ++ int format, /* character; 0 means default */ ++ char *dst, ++ size_t dstlen ++); ++#define ADDRTOA_BUF 16 /* just large enough for largest result */ ++ ++/* subnets */ ++const char * /* NULL for success, else string literal */ ++atosubnet( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ struct in_addr *addr, ++ struct in_addr *mask ++); ++size_t /* space needed for full conversion */ ++subnettoa( ++ struct in_addr addr, ++ struct in_addr mask, ++ int format, /* character; 0 means default */ ++ char *dst, ++ size_t dstlen ++); ++#define SUBNETTOA_BUF 32 /* large enough for worst case result */ ++ ++/* ranges */ ++const char * /* NULL for success, else string literal */ ++atoasr( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ char *type, /* 'a', 's', 'r' */ ++ struct in_addr *addrs /* two-element array */ ++); ++size_t /* space needed for full conversion */ ++rangetoa( ++ struct in_addr *addrs, /* two-element array */ ++ int format, /* character; 0 means default */ ++ char *dst, ++ size_t dstlen ++); ++#define RANGETOA_BUF 34 /* large enough for worst case result */ ++ ++/* data types for SA conversion functions */ ++ ++/* generic data, e.g. keys */ ++const char * /* NULL for success, else string literal */ ++atobytes( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ char *dst, ++ size_t dstlen, ++ size_t *lenp /* NULL means don't bother telling me */ ++); ++size_t /* 0 failure, else true size */ ++bytestoa( ++ const char *src, ++ size_t srclen, ++ int format, /* character; 0 means default */ ++ char *dst, ++ size_t dstlen ++); ++ ++/* old versions of generic-data functions; deprecated */ ++size_t /* 0 failure, else true size */ ++atodata( ++ const char *src, ++ size_t srclen, /* 0 means strlen(src) */ ++ char *dst, ++ size_t dstlen ++); ++size_t /* 0 failure, else true size */ ++datatoa( ++ const char *src, ++ size_t srclen, ++ int format, /* character; 0 means default */ ++ char *dst, ++ size_t dstlen ++); ++ ++/* part extraction and special addresses */ ++struct in_addr ++subnetof( ++ struct in_addr addr, ++ struct in_addr mask ++); ++struct in_addr ++hostof( ++ struct in_addr addr, ++ struct in_addr mask ++); ++struct in_addr ++broadcastof( ++ struct in_addr addr, ++ struct in_addr mask ++); ++ ++/* mask handling */ ++int ++goodmask( ++ struct in_addr mask ++); ++int ++masktobits( ++ struct in_addr mask ++); ++struct in_addr ++bitstomask( ++ int n ++); ++ ++ ++ ++/* ++ * general utilities ++ */ ++ ++#ifndef __KERNEL__ ++/* option pickup from files (userland only because of use of FILE) */ ++const char *optionsfrom(const char *filename, int *argcp, char ***argvp, ++ int optind, FILE *errorreport); ++ ++/* sanitize a string */ ++extern size_t sanitize_string(char *buf, size_t size); ++ ++#endif ++ ++ ++/* ++ * ENUM of klips debugging values. Not currently used in klips. ++ * debug flag is actually 32 -bits, but only one bit is ever used, ++ * so we can actually pack it all into a single 32-bit word. ++ */ ++enum klips_debug_flags { ++ KDF_VERBOSE = 0, ++ KDF_XMIT = 1, ++ KDF_NETLINK = 2, /* obsolete */ ++ KDF_XFORM = 3, ++ KDF_EROUTE = 4, ++ KDF_SPI = 5, ++ KDF_RADIJ = 6, ++ KDF_ESP = 7, ++ KDF_AH = 8, /* obsolete */ ++ KDF_RCV = 9, ++ KDF_TUNNEL = 10, ++ KDF_PFKEY = 11, ++ KDF_COMP = 12 ++}; ++ ++ ++/* ++ * Debugging levels for pfkey_lib_debug ++ */ ++#define PF_KEY_DEBUG_PARSE_NONE 0 ++#define PF_KEY_DEBUG_PARSE_PROBLEM 1 ++#define PF_KEY_DEBUG_PARSE_STRUCT 2 ++#define PF_KEY_DEBUG_PARSE_FLOW 4 ++#define PF_KEY_DEBUG_BUILD 8 ++#define PF_KEY_DEBUG_PARSE_MAX 15 ++ ++extern unsigned int pfkey_lib_debug; /* bits selecting what to report */ ++ ++/* ++ * pluto and lwdnsq need to know the maximum size of the commands to, ++ * and replies from lwdnsq. ++ */ ++ ++#define LWDNSQ_CMDBUF_LEN 1024 ++#define LWDNSQ_RESULT_LEN_MAX 4096 ++ ++ ++/* syntax for passthrough SA */ ++#ifndef PASSTHROUGHNAME ++#define PASSTHROUGHNAME "%passthrough" ++#define PASSTHROUGH4NAME "%passthrough4" ++#define PASSTHROUGH6NAME "%passthrough6" ++#define PASSTHROUGHIS "tun0@0.0.0.0" ++#define PASSTHROUGH4IS "tun0@0.0.0.0" ++#define PASSTHROUGH6IS "tun0@::" ++#define PASSTHROUGHTYPE "tun" ++#define PASSTHROUGHSPI 0 ++#define PASSTHROUGHDST 0 ++#endif ++ ++ ++ ++#endif /* _OPENSWAN_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipcomp.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,61 @@ ++/* ++ * IPCOMP zlib interface code. ++ * Copyright (C) 2000 Svenning Soerensen ++ * Copyright (C) 2000, 2001 Richard Guy Briggs ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ ++ RCSID $Id: ipcomp.h,v 1.13 2004/04/05 19:55:04 mcr Exp $ ++ ++ */ ++ ++/* SSS */ ++ ++#ifndef _IPCOMP_H ++#define _IPCOMP_H ++ ++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */ ++#ifndef IPCOMP_PREFIX ++#define IPCOMP_PREFIX ++#endif /* IPCOMP_PREFIX */ ++ ++#ifndef IPPROTO_COMP ++#define IPPROTO_COMP 108 ++#endif /* IPPROTO_COMP */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int sysctl_ipsec_debug_ipcomp; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++struct ipcomphdr { /* IPCOMP header */ ++ __u8 ipcomp_nh; /* Next header (protocol) */ ++ __u8 ipcomp_flags; /* Reserved, must be 0 */ ++ __u16 ipcomp_cpi; /* Compression Parameter Index */ ++}; ++ ++extern struct inet_protocol comp_protocol; ++extern int sysctl_ipsec_debug_ipcomp; ++ ++#define IPCOMP_UNCOMPRESSABLE 0x000000001 ++#define IPCOMP_COMPRESSIONERROR 0x000000002 ++#define IPCOMP_PARMERROR 0x000000004 ++#define IPCOMP_DECOMPRESSIONERROR 0x000000008 ++ ++#define IPCOMP_ADAPT_INITIAL_TRIES 8 ++#define IPCOMP_ADAPT_INITIAL_SKIP 4 ++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2 ++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8 ++ ++/* Function prototypes */ ++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); ++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); ++ ++#endif /* _IPCOMP_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_ah.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,190 @@ ++/* ++ * Authentication Header declarations ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_ah.h,v 1.23 2004/04/05 19:55:04 mcr Exp $ ++ */ ++ ++#include "ipsec_md5h.h" ++#include "ipsec_sha1.h" ++ ++#ifndef IPPROTO_AH ++#define IPPROTO_AH 51 ++#endif /* IPPROTO_AH */ ++ ++#include "ipsec_auth.h" ++ ++#ifdef __KERNEL__ ++ ++extern struct inet_protocol ah_protocol; ++ ++struct options; ++ ++struct ahhdr /* Generic AH header */ ++{ ++ __u8 ah_nh; /* Next header (protocol) */ ++ __u8 ah_hl; /* AH length, in 32-bit words */ ++ __u16 ah_rv; /* reserved, must be 0 */ ++ __u32 ah_spi; /* Security Parameters Index */ ++ __u32 ah_rpl; /* Replay prevention */ ++ __u8 ah_data[AHHMAC_HASHLEN];/* Authentication hash */ ++}; ++#define AH_BASIC_LEN 8 /* basic AH header is 8 bytes, nh,hl,rv,spi ++ * and the ah_hl, says how many bytes after that ++ * to cover. */ ++ ++extern struct xform_functions ah_xform_funcs[]; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_ah; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* __KERNEL__ */ ++ ++/* ++ * $Log: ipsec_ah.h,v $ ++ * Revision 1.23 2004/04/05 19:55:04 mcr ++ * Moved from linux/include/freeswan/ipsec_ah.h,v ++ * ++ * Revision 1.22 2004/04/05 19:41:05 mcr ++ * merged alg-branch code. ++ * ++ * Revision 1.21 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.22 2003/12/11 20:14:58 mcr ++ * refactored the xmit code, to move all encapsulation ++ * code into protocol functions. Note that all functions ++ * are essentially done by a single function, which is probably ++ * wrong. ++ * the rcv_functions structures are renamed xform_functions. ++ * ++ * Revision 1.21 2003/12/06 21:21:19 mcr ++ * split up receive path into per-transform files, for ++ * easier later removal. ++ * ++ * Revision 1.20.8.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.20 2003/02/06 02:21:34 rgb ++ * ++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . ++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". ++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. ++ * ++ * Revision 1.19 2002/09/16 21:19:13 mcr ++ * fixes for west-ah-icmp-01 - length of AH header must be ++ * calculated properly, and next_header field properly copied. ++ * ++ * Revision 1.18 2002/05/14 02:37:02 rgb ++ * Change reference from _TDB to _IPSA. ++ * ++ * Revision 1.17 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_ah.h,v ++ * ++ * Revision 1.16 2002/02/20 01:27:06 rgb ++ * Ditched a pile of structs only used by the old Netlink interface. ++ * ++ * Revision 1.15 2001/12/11 02:35:57 rgb ++ * Change "struct net_device" to "struct device" for 2.2 compatibility. ++ * ++ * Revision 1.14 2001/11/26 09:23:47 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.13.2.1 2001/09/25 02:18:24 mcr ++ * replace "struct device" with "struct netdevice" ++ * ++ * Revision 1.13 2001/06/14 19:35:08 rgb ++ * Update copyright date. ++ * ++ * Revision 1.12 2000/09/12 03:21:20 rgb ++ * Cleared out unused htonq. ++ * ++ * Revision 1.11 2000/09/08 19:12:55 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.10 2000/01/21 06:13:10 rgb ++ * Tidied up spacing. ++ * Added macros for HMAC padding magic numbers.(kravietz) ++ * ++ * Revision 1.9 1999/12/07 18:16:23 rgb ++ * Fixed comments at end of #endif lines. ++ * ++ * Revision 1.8 1999/04/11 00:28:56 henry ++ * GPL boilerplate ++ * ++ * Revision 1.7 1999/04/06 04:54:25 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.6 1999/01/26 02:06:01 rgb ++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro. ++ * ++ * Revision 1.5 1999/01/22 06:17:49 rgb ++ * Updated macro comments. ++ * Added context types to support algorithm switch code. ++ * 64-bit clean-up -- converting 'u long long' to __u64. ++ * ++ * Revision 1.4 1998/07/14 15:54:56 rgb ++ * Add #ifdef __KERNEL__ to protect kernel-only structures. ++ * ++ * Revision 1.3 1998/06/30 18:05:16 rgb ++ * Comment out references to htonq. ++ * ++ * Revision 1.2 1998/06/25 19:33:46 rgb ++ * Add prototype for protocol receive function. ++ * Rearrange for more logical layout. ++ * ++ * Revision 1.1 1998/06/18 21:27:43 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.4 1998/05/18 22:28:43 rgb ++ * Disable key printing facilities from /proc/net/ipsec_*. ++ * ++ * Revision 1.3 1998/04/21 21:29:07 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.2 1998/04/12 22:03:17 rgb ++ * Updated ESP-3DES-HMAC-MD5-96, ++ * ESP-DES-HMAC-MD5-96, ++ * AH-HMAC-MD5-96, ++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository ++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts. ++ * ++ * Fixed eroute references in /proc/net/ipsec*. ++ * ++ * Started to patch module unloading memory leaks in ipsec_netlink and ++ * radij tree unloading. ++ * ++ * Revision 1.1 1998/04/09 03:05:55 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * Added definitions for new AH transforms. ++ * ++ * Revision 0.3 1996/11/20 14:35:48 ji ++ * Minor Cleanup. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_alg.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,260 @@ ++/* ++ * Modular extensions service and registration functions interface ++ * ++ * Author: JuanJo Ciarlante ++ * ++ * ipsec_alg.h,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ */ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ */ ++#ifndef IPSEC_ALG_H ++#define IPSEC_ALG_H ++ ++/* ++ * gcc >= 3.2 has removed __FUNCTION__, replaced by C99 __func__ ++ * *BUT* its a compiler variable. ++ */ ++#if (__GNUC__ >= 3) ++#ifndef __FUNCTION__ ++#define __FUNCTION__ __func__ ++#endif ++#endif ++ ++/* Version 0.8.1-0 */ ++#define IPSEC_ALG_VERSION 0x00080100 ++ ++#include ++#include ++#include ++/* ++ * The following structs are used via pointers in ipsec_alg object to ++ * avoid ipsec_alg.h coupling with freeswan headers, thus simplifying ++ * module development ++ */ ++struct ipsec_sa; ++struct esp; ++ ++/************************************** ++ * ++ * Main registration object ++ * ++ *************************************/ ++#define IPSEC_ALG_VERSION_QUAD(v) \ ++ (v>>24),((v>>16)&0xff),((v>>8)&0xff),(v&0xff) ++/* ++ * Main ipsec_alg objects: "OOPrograming wannabe" ++ * Hierachy (carefully handled with _minimal_ cast'ing): ++ * ++ * ipsec_alg+ ++ * +->ipsec_alg_enc (ixt_alg_type=SADB_EXT_SUPPORTED_ENCRYPT) ++ * +->ipsec_alg_auth (ixt_alg_type=SADB_EXT_SUPPORTED_AUTH) ++ */ ++ ++/*************************************************************** ++ * ++ * INTERFACE object: struct ipsec_alg ++ * ++ ***************************************************************/ ++ ++/* ++ * common part for every struct ipsec_alg_* ++ * (sortof poor's man OOP) ++ */ ++#define IPSEC_ALG_STRUCT_COMMON \ ++ unsigned ixt_version; /* only allow this version (or 'near')*/ \ ++ struct list_head ixt_list; /* dlinked list */ \ ++ struct module *ixt_module; /* THIS_MODULE */ \ ++ unsigned ixt_state; /* state flags */ \ ++ atomic_t ixt_refcnt; /* ref. count when pointed from ipsec_sa */ \ ++ char ixt_name[16]; /* descriptive short name, eg. "3des" */ \ ++ void *ixt_data; /* private for algo implementation */ \ ++ uint8_t ixt_blocksize; /* blocksize in bytes */ \ ++ \ ++ /* THIS IS A COPY of struct supported (lib/pfkey.h) \ ++ * please keep in sync until we migrate 'supported' stuff \ ++ * to ipsec_alg \ ++ */ \ ++ uint16_t ixt_alg_type; /* correspond to IPSEC_ALG_{ENCRYPT,AUTH} */ \ ++ uint8_t ixt_alg_id; /* enc. alg. number, eg. ESP_3DES */ \ ++ uint8_t ixt_ivlen; /* ivlen in bits, expected to be multiple of 8! */ \ ++ uint16_t ixt_keyminbits;/* min. keybits (of entropy) */ \ ++ uint16_t ixt_keymaxbits;/* max. keybits (of entropy) */ ++ ++#define ixt_support ixt_alg_type ++ ++#define IPSEC_ALG_ST_SUPP 0x01 ++#define IPSEC_ALG_ST_REGISTERED 0x02 ++#define IPSEC_ALG_ST_EXCL 0x04 ++struct ipsec_alg { ++ IPSEC_ALG_STRUCT_COMMON ++}; ++/* ++ * Note the const in cbc_encrypt IV arg: ++ * some ciphers like to toast passed IV (eg. 3DES): make a local IV copy ++ */ ++struct ipsec_alg_enc { ++ IPSEC_ALG_STRUCT_COMMON ++ unsigned ixt_e_keylen; /* raw key length in bytes */ ++ unsigned ixt_e_ctx_size; /* sa_p->key_e_size */ ++ int (*ixt_e_set_key)(struct ipsec_alg_enc *alg, __u8 *key_e, const __u8 *key, size_t keysize); ++ __u8 *(*ixt_e_new_key)(struct ipsec_alg_enc *alg, const __u8 *key, size_t keysize); ++ void (*ixt_e_destroy_key)(struct ipsec_alg_enc *alg, __u8 *key_e); ++ int (*ixt_e_cbc_encrypt)(struct ipsec_alg_enc *alg, __u8 *key_e, __u8 *in, int ilen, const __u8 *iv, int encrypt); ++}; ++struct ipsec_alg_auth { ++ IPSEC_ALG_STRUCT_COMMON ++ unsigned ixt_a_keylen; /* raw key length in bytes */ ++ unsigned ixt_a_ctx_size; /* sa_p->key_a_size */ ++ unsigned ixt_a_authlen; /* 'natural' auth. hash len (bytes) */ ++ int (*ixt_a_hmac_set_key)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *key, int keylen); ++ int (*ixt_a_hmac_hash)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *dat, int len, __u8 *hash, int hashlen); ++}; ++/* ++ * These are _copies_ of SADB_EXT_SUPPORTED_{AUTH,ENCRYPT}, ++ * to avoid header coupling for true constants ++ * about headers ... "cp is your friend" --Linus ++ */ ++#define IPSEC_ALG_TYPE_AUTH 14 ++#define IPSEC_ALG_TYPE_ENCRYPT 15 ++ ++/*************************************************************** ++ * ++ * INTERFACE for module loading,testing, and unloading ++ * ++ ***************************************************************/ ++/* - registration calls */ ++int register_ipsec_alg(struct ipsec_alg *); ++int unregister_ipsec_alg(struct ipsec_alg *); ++/* - optional (simple test) for algos */ ++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int testparm); ++/* inline wrappers (usefull for type validation */ ++static inline int register_ipsec_alg_enc(struct ipsec_alg_enc *ixt) { ++ return register_ipsec_alg((struct ipsec_alg*)ixt); ++} ++static inline int unregister_ipsec_alg_enc(struct ipsec_alg_enc *ixt) { ++ return unregister_ipsec_alg((struct ipsec_alg*)ixt); ++} ++static inline int register_ipsec_alg_auth(struct ipsec_alg_auth *ixt) { ++ return register_ipsec_alg((struct ipsec_alg*)ixt); ++} ++static inline int unregister_ipsec_alg_auth(struct ipsec_alg_auth *ixt) { ++ return unregister_ipsec_alg((struct ipsec_alg*)ixt); ++} ++ ++/***************************************************************** ++ * ++ * INTERFACE for ENC services: key creation, encrypt function ++ * ++ *****************************************************************/ ++ ++#define IPSEC_ALG_ENCRYPT 1 ++#define IPSEC_ALG_DECRYPT 0 ++ ++/* encryption key context creation function */ ++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p); ++/* ++ * ipsec_alg_esp_encrypt(): encrypt ilen bytes in idat returns ++ * 0 or ERR<0 ++ */ ++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 *idat, int ilen, const __u8 *iv, int action); ++ ++/*************************************************************** ++ * ++ * INTERFACE for AUTH services: key creation, hash functions ++ * ++ ***************************************************************/ ++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p); ++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) ; ++#define ipsec_alg_sa_esp_update(c,k,l) ipsec_alg_sa_esp_hash(c,k,l,NULL,0) ++ ++/* only called from ipsec_init.c */ ++int ipsec_alg_init(void); ++ ++/* algo module glue for static algos */ ++void ipsec_alg_static_init(void); ++typedef int (*ipsec_alg_init_func_t) (void); ++ ++/********************************************** ++ * ++ * INTERFACE for ipsec_sa init and wipe ++ * ++ **********************************************/ ++ ++/* returns true if ipsec_sa has ipsec_alg obj attached */ ++/* ++ * Initializes ipsec_sa's ipsec_alg object, using already loaded ++ * proto, authalg, encalg.; links ipsec_alg objects (enc, auth) ++ */ ++int ipsec_alg_sa_init(struct ipsec_sa *sa_p); ++/* ++ * Destroys ipsec_sa's ipsec_alg object ++ * unlinking ipsec_alg objects ++ */ ++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p); ++ ++#define IPSEC_ALG_MODULE_INIT_MOD( func_name ) \ ++ static int func_name(void); \ ++ module_init(func_name); \ ++ static int __init func_name(void) ++#define IPSEC_ALG_MODULE_EXIT_MOD( func_name ) \ ++ static void func_name(void); \ ++ module_exit(func_name); \ ++ static void __exit func_name(void) ++ ++#define IPSEC_ALG_MODULE_INIT_STATIC( func_name ) \ ++ extern int func_name(void); \ ++ int func_name(void) ++#define IPSEC_ALG_MODULE_EXIT_STATIC( func_name ) \ ++ extern void func_name(void); \ ++ void func_name(void) ++ ++/********************************************** ++ * ++ * 2.2 backport for some 2.4 useful module stuff ++ * ++ **********************************************/ ++#ifdef MODULE ++#ifndef THIS_MODULE ++#define THIS_MODULE (&__this_module) ++#endif ++#ifndef module_init ++typedef int (*__init_module_func_t)(void); ++typedef void (*__cleanup_module_func_t)(void); ++ ++#define module_init(x) \ ++ int init_module(void) __attribute__((alias(#x))); \ ++ static inline __init_module_func_t __init_module_inline(void) \ ++ { return x; } ++#define module_exit(x) \ ++ void cleanup_module(void) __attribute__((alias(#x))); \ ++ static inline __cleanup_module_func_t __cleanup_module_inline(void) \ ++ { return x; } ++#endif ++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_MOD( func_name ) ++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_MOD( func_name ) ++ ++#else /* not MODULE */ ++#ifndef THIS_MODULE ++#define THIS_MODULE NULL ++#endif ++/* ++ * I only want module_init() magic ++ * when algo.c file *is THE MODULE*, in all other ++ * cases, initialization is called explicitely from ipsec_alg_init() ++ */ ++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_STATIC(func_name) ++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_STATIC(func_name) ++#endif ++ ++#endif /* IPSEC_ALG_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_auth.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,100 @@ ++/* ++ * Authentication Header declarations ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_auth.h,v 1.3 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#include "ipsec_md5h.h" ++#include "ipsec_sha1.h" ++ ++#ifndef IPSEC_AUTH_H ++#define IPSEC_AUTH_H ++ ++#define AH_FLENGTH 12 /* size of fixed part */ ++#define AHMD5_KMAX 64 /* MD5 max 512 bits key */ ++#define AHMD5_AMAX 12 /* MD5 96 bits of authenticator */ ++ ++#define AHMD596_KLEN 16 /* MD5 128 bits key */ ++#define AHSHA196_KLEN 20 /* SHA1 160 bits key */ ++ ++#define AHMD596_ALEN 16 /* MD5 128 bits authentication length */ ++#define AHSHA196_ALEN 20 /* SHA1 160 bits authentication length */ ++ ++#define AHMD596_BLKLEN 64 /* MD5 block length */ ++#define AHSHA196_BLKLEN 64 /* SHA1 block length */ ++#define AHSHA2_256_BLKLEN 64 /* SHA2-256 block length */ ++#define AHSHA2_384_BLKLEN 128 /* SHA2-384 block length (?) */ ++#define AHSHA2_512_BLKLEN 128 /* SHA2-512 block length */ ++ ++#define AH_BLKLEN_MAX 128 /* keep up to date! */ ++ ++ ++#define AH_AMAX AHSHA196_ALEN /* keep up to date! */ ++#define AHHMAC_HASHLEN 12 /* authenticator length of 96bits */ ++#define AHHMAC_RPLLEN 4 /* 32 bit replay counter */ ++ ++#define DB_AH_PKTRX 0x0001 ++#define DB_AH_PKTRX2 0x0002 ++#define DB_AH_DMP 0x0004 ++#define DB_AH_IPSA 0x0010 ++#define DB_AH_XF 0x0020 ++#define DB_AH_INAU 0x0040 ++#define DB_AH_REPLAY 0x0100 ++ ++#ifdef __KERNEL__ ++ ++/* General HMAC algorithm is described in RFC 2104 */ ++ ++#define HMAC_IPAD 0x36 ++#define HMAC_OPAD 0x5C ++ ++struct md5_ctx { ++ MD5_CTX ictx; /* context after H(K XOR ipad) */ ++ MD5_CTX octx; /* context after H(K XOR opad) */ ++}; ++ ++struct sha1_ctx { ++ SHA1_CTX ictx; /* context after H(K XOR ipad) */ ++ SHA1_CTX octx; /* context after H(K XOR opad) */ ++}; ++ ++struct auth_alg { ++ void (*init)(void *ctx); ++ void (*update)(void *ctx, unsigned char *bytes, __u32 len); ++ void (*final)(unsigned char *hash, void *ctx); ++ int hashlen; ++}; ++ ++struct options; ++ ++#endif /* __KERNEL__ */ ++#endif /* IPSEC_AUTH_H */ ++ ++/* ++ * $Log: ipsec_auth.h,v $ ++ * Revision 1.3 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.2 2004/04/05 19:55:04 mcr ++ * Moved from linux/include/freeswan/ipsec_auth.h,v ++ * ++ * Revision 1.1 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.1 2003/12/06 21:21:19 mcr ++ * split up receive path into per-transform files, for ++ * easier later removal. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_encap.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,149 @@ ++/* ++ * declarations relevant to encapsulation-like operations ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_encap.h,v 1.19 2004/04/05 19:55:04 mcr Exp $ ++ */ ++ ++#ifndef _IPSEC_ENCAP_H_ ++ ++#define SENT_IP4 16 /* data is two struct in_addr + proto + ports*/ ++ /* (2 * sizeof(struct in_addr)) */ ++ /* sizeof(struct sockaddr_encap) ++ - offsetof(struct sockaddr_encap, Sen.Sip4.Src) */ ++ ++struct sockaddr_encap ++{ ++ __u8 sen_len; /* length */ ++ __u8 sen_family; /* AF_ENCAP */ ++ __u16 sen_type; /* see SENT_* */ ++ union ++ { ++ struct /* SENT_IP4 */ ++ { ++ struct in_addr Src; ++ struct in_addr Dst; ++ __u8 Proto; ++ __u16 Sport; ++ __u16 Dport; ++ } Sip4; ++ } Sen; ++}; ++ ++#define sen_ip_src Sen.Sip4.Src ++#define sen_ip_dst Sen.Sip4.Dst ++#define sen_proto Sen.Sip4.Proto ++#define sen_sport Sen.Sip4.Sport ++#define sen_dport Sen.Sip4.Dport ++ ++#ifndef AF_ENCAP ++#define AF_ENCAP 26 ++#endif /* AF_ENCAP */ ++ ++#define _IPSEC_ENCAP_H_ ++#endif /* _IPSEC_ENCAP_H_ */ ++ ++/* ++ * $Log: ipsec_encap.h,v $ ++ * Revision 1.19 2004/04/05 19:55:04 mcr ++ * Moved from linux/include/freeswan/ipsec_encap.h,v ++ * ++ * Revision 1.18 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.17.30.1 2003/09/21 13:59:38 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.17 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_encap.h,v ++ * ++ * Revision 1.16 2001/11/26 09:23:47 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.15.2.1 2001/09/25 02:18:54 mcr ++ * struct eroute moved to ipsec_eroute.h ++ * ++ * Revision 1.15 2001/09/14 16:58:36 rgb ++ * Added support for storing the first and last packets through a HOLD. ++ * ++ * Revision 1.14 2001/09/08 21:13:31 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.13 2001/06/14 19:35:08 rgb ++ * Update copyright date. ++ * ++ * Revision 1.12 2001/05/27 06:12:10 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.11 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.10 2000/03/22 16:15:36 rgb ++ * Fixed renaming of dev_get (MB). ++ * ++ * Revision 1.9 2000/01/21 06:13:26 rgb ++ * Added a macro for AF_ENCAP ++ * ++ * Revision 1.8 1999/12/31 14:56:55 rgb ++ * MB fix for 2.3 dev-use-count. ++ * ++ * Revision 1.7 1999/11/18 04:09:18 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.6 1999/09/24 00:34:13 rgb ++ * Add Marc Boucher's support for 2.3.xx+. ++ * ++ * Revision 1.5 1999/04/11 00:28:57 henry ++ * GPL boilerplate ++ * ++ * Revision 1.4 1999/04/06 04:54:25 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.3 1998/10/19 14:44:28 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.2 1998/07/14 18:19:33 rgb ++ * Added #ifdef __KERNEL__ directives to restrict scope of header. ++ * ++ * Revision 1.1 1998/06/18 21:27:44 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.2 1998/04/21 21:29:10 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.1 1998/04/09 03:05:58 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * Minor cosmetic changes. ++ * ++ * Revision 0.3 1996/11/20 14:35:48 ji ++ * Minor Cleanup. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_eroute.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,112 @@ ++/* ++ * @(#) declarations of eroute structures ++ * ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs ++ * Copyright (C) 2001 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_eroute.h,v 1.5 2004/04/05 19:55:05 mcr Exp $ ++ * ++ * derived from ipsec_encap.h 1.15 on 2001/9/18 by mcr. ++ * ++ */ ++ ++#ifndef _IPSEC_EROUTE_H_ ++ ++#include "radij.h" ++#include "ipsec_encap.h" ++#include "ipsec_radij.h" ++ ++/* ++ * The "type" is really part of the address as far as the routing ++ * system is concerned. By using only one bit in the type field ++ * for each type, we sort-of make sure that different types of ++ * encapsulation addresses won't be matched against the wrong type. ++ */ ++ ++/* ++ * An entry in the radix tree ++ */ ++ ++struct rjtentry ++{ ++ struct radij_node rd_nodes[2]; /* tree glue, and other values */ ++#define rd_key(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_key)) ++#define rd_mask(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_mask)) ++ short rd_flags; ++ short rd_count; ++}; ++ ++struct ident ++{ ++ __u16 type; /* identity type */ ++ __u64 id; /* identity id */ ++ __u8 len; /* identity len */ ++ caddr_t data; /* identity data */ ++}; ++ ++/* ++ * An encapsulation route consists of a pointer to a ++ * radix tree entry and a SAID (a destination_address/SPI/protocol triple). ++ */ ++ ++struct eroute ++{ ++ struct rjtentry er_rjt; ++ ip_said er_said; ++ uint32_t er_pid; ++ uint32_t er_count; ++ uint64_t er_lasttime; ++ struct sockaddr_encap er_eaddr; /* MCR get rid of _encap, it is silly*/ ++ struct sockaddr_encap er_emask; ++ struct ident er_ident_s; ++ struct ident er_ident_d; ++ struct sk_buff* er_first; ++ struct sk_buff* er_last; ++}; ++ ++#define er_dst er_said.dst ++#define er_spi er_said.spi ++ ++#define _IPSEC_EROUTE_H_ ++#endif /* _IPSEC_EROUTE_H_ */ ++ ++/* ++ * $Log: ipsec_eroute.h,v $ ++ * Revision 1.5 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_eroute.h,v ++ * ++ * Revision 1.4 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.3.30.2 2003/10/29 01:10:19 mcr ++ * elimited "struct sa_id" ++ * ++ * Revision 1.3.30.1 2003/09/21 13:59:38 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.3 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_eroute.h,v ++ * ++ * Revision 1.2 2001/11/26 09:16:13 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:18:54 mcr ++ * struct eroute moved to ipsec_eroute.h ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_errs.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,53 @@ ++/* ++ * @(#) definition of ipsec_errs structure ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_errs.h,v 1.4 2004/04/05 19:55:05 mcr Exp $ ++ * ++ */ ++ ++/* ++ * This file describes the errors/statistics that FreeSWAN collects. ++ * ++ */ ++ ++struct ipsec_errs { ++ __u32 ips_alg_errs; /* number of algorithm errors */ ++ __u32 ips_auth_errs; /* # of authentication errors */ ++ __u32 ips_encsize_errs; /* # of encryption size errors*/ ++ __u32 ips_encpad_errs; /* # of encryption pad errors*/ ++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */ ++}; ++ ++/* ++ * $Log: ipsec_errs.h,v $ ++ * Revision 1.4 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_errs.h,v ++ * ++ * Revision 1.3 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_errs.h,v ++ * ++ * Revision 1.2 2001/11/26 09:16:13 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr ++ * lifetime structure created and common functions created. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_esp.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,147 @@ ++/* ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_esp.h,v 1.25 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#include "openswan/ipsec_md5h.h" ++#include "openswan/ipsec_sha1.h" ++ ++#include "crypto/des.h" ++ ++#ifndef IPPROTO_ESP ++#define IPPROTO_ESP 50 ++#endif /* IPPROTO_ESP */ ++ ++#define ESP_HEADER_LEN 8 /* 64 bits header (spi+rpl)*/ ++ ++#define EMT_ESPDESCBC_ULEN 20 /* coming from user mode */ ++#define EMT_ESPDES_KMAX 64 /* 512 bit secret key enough? */ ++#define EMT_ESPDES_KEY_SZ 8 /* 56 bit secret key with parity = 64 bits */ ++#define EMT_ESP3DES_KEY_SZ 24 /* 168 bit secret key with parity = 192 bits */ ++#define EMT_ESPDES_IV_SZ 8 /* IV size */ ++#define ESP_DESCBC_BLKLEN 8 /* DES-CBC block size */ ++ ++#define ESP_IV_MAXSZ 16 /* This is _critical_ */ ++#define ESP_IV_MAXSZ_INT (ESP_IV_MAXSZ/sizeof(int)) ++ ++#define DB_ES_PKTRX 0x0001 ++#define DB_ES_PKTRX2 0x0002 ++#define DB_ES_IPSA 0x0010 ++#define DB_ES_XF 0x0020 ++#define DB_ES_IPAD 0x0040 ++#define DB_ES_INAU 0x0080 ++#define DB_ES_OINFO 0x0100 ++#define DB_ES_OINFO2 0x0200 ++#define DB_ES_OH 0x0400 ++#define DB_ES_REPLAY 0x0800 ++ ++#ifdef __KERNEL__ ++struct des_eks { ++ des_key_schedule ks; ++}; ++ ++extern struct inet_protocol esp_protocol; ++ ++struct options; ++ ++struct esphdr ++{ ++ __u32 esp_spi; /* Security Parameters Index */ ++ __u32 esp_rpl; /* Replay counter */ ++ __u8 esp_iv[8]; /* iv */ ++}; ++ ++extern struct xform_functions esp_xform_funcs[]; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_esp; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* __KERNEL__ */ ++ ++/* ++ * $Log: ipsec_esp.h,v $ ++ * Revision 1.25 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.24 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_esp.h,v ++ * ++ * Revision 1.23 2004/04/05 19:41:05 mcr ++ * merged alg-branch code. ++ * ++ * Revision 1.22 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.23 2003/12/11 20:14:58 mcr ++ * refactored the xmit code, to move all encapsulation ++ * code into protocol functions. Note that all functions ++ * are essentially done by a single function, which is probably ++ * wrong. ++ * the rcv_functions structures are renamed xform_functions. ++ * ++ * Revision 1.22 2003/12/06 21:21:19 mcr ++ * split up receive path into per-transform files, for ++ * easier later removal. ++ * ++ * Revision 1.21.8.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.21 2003/02/06 02:21:34 rgb ++ * ++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . ++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". ++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. ++ * ++ * Revision 1.20 2002/05/14 02:37:02 rgb ++ * Change reference from _TDB to _IPSA. ++ * ++ * Revision 1.19 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.18 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_esp.h,v ++ * ++ * Revision 1.17 2002/02/20 01:27:07 rgb ++ * Ditched a pile of structs only used by the old Netlink interface. ++ * ++ * Revision 1.16 2001/12/11 02:35:57 rgb ++ * Change "struct net_device" to "struct device" for 2.2 compatibility. ++ * ++ * Revision 1.15 2001/11/26 09:23:48 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.14.2.3 2001/10/23 04:16:42 mcr ++ * get definition of des_key_schedule from des.h ++ * ++ * Revision 1.14.2.2 2001/10/22 20:33:13 mcr ++ * use "des_key_schedule" structure instead of cooking our own. ++ * ++ * Revision 1.14.2.1 2001/09/25 02:18:25 mcr ++ * replace "struct device" with "struct netdevice" ++ * ++ * Revision 1.14 2001/06/14 19:35:08 rgb ++ * Update copyright date. ++ * ++ * Revision 1.13 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.12 2000/08/01 14:51:50 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.11 2000/01/10 16:36:20 rgb ++ * Ditch last of EME option flags, including initiator. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_ipcomp.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,91 @@ ++/* ++ * IP compression header declations ++ * ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_ipcomp.h,v 1.3 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#ifndef IPSEC_IPCOMP_H ++#define IPSEC_IPCOMP_H ++ ++#include "openswan/ipsec_auth.h" ++ ++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */ ++#ifndef IPCOMP_PREFIX ++#define IPCOMP_PREFIX ++#endif /* IPCOMP_PREFIX */ ++ ++#ifndef IPPROTO_COMP ++#define IPPROTO_COMP 108 ++#endif /* IPPROTO_COMP */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int sysctl_ipsec_debug_ipcomp; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++struct ipcomphdr { /* IPCOMP header */ ++ __u8 ipcomp_nh; /* Next header (protocol) */ ++ __u8 ipcomp_flags; /* Reserved, must be 0 */ ++ __u16 ipcomp_cpi; /* Compression Parameter Index */ ++}; ++ ++extern struct inet_protocol comp_protocol; ++extern int sysctl_ipsec_debug_ipcomp; ++ ++#define IPCOMP_UNCOMPRESSABLE 0x000000001 ++#define IPCOMP_COMPRESSIONERROR 0x000000002 ++#define IPCOMP_PARMERROR 0x000000004 ++#define IPCOMP_DECOMPRESSIONERROR 0x000000008 ++ ++#define IPCOMP_ADAPT_INITIAL_TRIES 8 ++#define IPCOMP_ADAPT_INITIAL_SKIP 4 ++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2 ++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8 ++ ++/* Function prototypes */ ++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); ++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); ++ ++extern struct xform_functions ipcomp_xform_funcs[]; ++ ++#endif /* IPSEC_IPCOMP_H */ ++ ++/* ++ * $Log: ipsec_ipcomp.h,v $ ++ * Revision 1.3 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.2 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_ipcomp.h,v ++ * ++ * Revision 1.1 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.2 2003/12/11 20:14:58 mcr ++ * refactored the xmit code, to move all encapsulation ++ * code into protocol functions. Note that all functions ++ * are essentially done by a single function, which is probably ++ * wrong. ++ * the rcv_functions structures are renamed xform_functions. ++ * ++ * Revision 1.1 2003/12/06 21:21:19 mcr ++ * split up receive path into per-transform files, for ++ * easier later removal. ++ * ++ * ++ * ++ */ ++ ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_ipe4.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,68 @@ ++/* ++ * IP-in-IP Header declarations ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_ipe4.h,v 1.6 2004/04/05 19:55:05 mcr Exp $ ++ */ ++ ++/* The packet header is an IP header! */ ++ ++struct ipe4_xdata /* transform table data */ ++{ ++ struct in_addr i4_src; ++ struct in_addr i4_dst; ++}; ++ ++#define EMT_IPE4_ULEN 8 /* coming from user mode */ ++ ++ ++/* ++ * $Log: ipsec_ipe4.h,v $ ++ * Revision 1.6 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_ipe4.h,v ++ * ++ * Revision 1.5 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_ipe4.h,v ++ * ++ * Revision 1.4 2001/06/14 19:35:08 rgb ++ * Update copyright date. ++ * ++ * Revision 1.3 1999/04/11 00:28:57 henry ++ * GPL boilerplate ++ * ++ * Revision 1.2 1999/04/06 04:54:25 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.1 1998/06/18 21:27:47 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.1 1998/04/09 03:06:07 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:48:53 ji ++ * Release update only. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_ipip.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_ipip.h,v 1.2 2004/04/05 19:55:05 mcr Exp $ ++ */ ++ ++#ifndef _IPSEC_IPIP_H_ ++ ++#ifndef IPPROTO_IPIP ++#define IPPROTO_IPIP 4 ++#endif /* IPPROTO_ESP */ ++ ++extern struct xform_functions ipip_xform_funcs[]; ++ ++#define _IPSEC_IPIP_H_ ++ ++#endif /* _IPSEC_IPIP_H_ */ ++ ++/* ++ * $Log: ipsec_ipip.h,v $ ++ * Revision 1.2 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_ipip.h,v ++ * ++ * Revision 1.1 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.1 2003/12/11 20:14:58 mcr ++ * refactored the xmit code, to move all encapsulation ++ * code into protocol functions. Note that all functions ++ * are essentially done by a single function, which is probably ++ * wrong. ++ * the rcv_functions structures are renamed xform_functions. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_kversion.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,239 @@ ++#ifndef _FREESWAN_KVERSIONS_H ++/* ++ * header file for FreeS/WAN library functions ++ * Copyright (C) 1998, 1999, 2000 Henry Spencer. ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: ipsec_kversion.h,v 1.9 2004/04/05 19:55:05 mcr Exp $ ++ */ ++#define _FREESWAN_KVERSIONS_H /* seen it, no need to see it again */ ++ ++/* ++ * this file contains a series of atomic defines that depend upon ++ * kernel version numbers. The kernel versions are arranged ++ * in version-order number (which is often not chronological) ++ * and each clause enables or disables a feature. ++ */ ++ ++/* ++ * First, assorted kernel-version-dependent trickery. ++ */ ++#include ++#ifndef KERNEL_VERSION ++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) ++#define HEADER_CACHE_BIND_21 ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) ++#define SPINLOCK ++#define PROC_FS_21 ++#define NETLINK_SOCK ++#define NET_21 ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,19) ++#define net_device_stats enet_statistics ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) ++#define SPINLOCK_23 ++#define NETDEV_23 ++# ifndef CONFIG_IP_ALIAS ++# define CONFIG_IP_ALIAS ++# endif ++#include ++#include ++#include ++# ifdef NETLINK_XFRM ++# define NETDEV_25 ++# endif ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,25) ++#define PROC_FS_2325 ++#undef PROC_FS_21 ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) ++#define PROC_NO_DUMMY ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,35) ++#define SKB_COPY_EXPAND ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,37) ++#define IP_SELECT_IDENT ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,50)) && defined(CONFIG_NETFILTER) ++#define SKB_RESET_NFCT ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,2) ++#define IP_SELECT_IDENT_NEW ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) ++#define IPH_is_SKB_PULLED ++#define SKB_COW_NEW ++#define PROTO_HANDLER_SINGLE_PARM ++#define IP_FRAGMENT_LINEARIZE 1 ++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */ ++# ifdef REDHAT_BOGOSITY ++# define IP_SELECT_IDENT_NEW ++# define IPH_is_SKB_PULLED ++# define SKB_COW_NEW ++# define PROTO_HANDLER_SINGLE_PARM ++# endif /* REDHAT_BOGOSITY */ ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) ++#define MALLOC_SLAB ++#define LINUX_KERNEL_HAS_SNPRINTF ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#define HAVE_NETDEV_PRINTK 1 ++#endif ++ ++#ifdef NET_21 ++# include ++#else ++ /* old kernel in.h has some IPv6 stuff, but not quite enough */ ++# define s6_addr16 s6_addr ++# define AF_INET6 10 ++# define uint8_t __u8 ++# define uint16_t __u16 ++# define uint32_t __u32 ++# define uint64_t __u64 ++#endif ++ ++#ifdef NET_21 ++# define ipsec_kfree_skb(a) kfree_skb(a) ++#else /* NET_21 */ ++# define ipsec_kfree_skb(a) kfree_skb(a, FREE_WRITE) ++#endif /* NET_21 */ ++ ++#ifdef NETDEV_23 ++# define device net_device ++# define ipsec_dev_get dev_get_by_name ++# define __ipsec_dev_get __dev_get_by_name ++# define ipsec_dev_put(x) dev_put(x) ++# define __ipsec_dev_put(x) __dev_put(x) ++# define ipsec_dev_hold(x) dev_hold(x) ++#else /* NETDEV_23 */ ++# define ipsec_dev_get dev_get ++# define __ipsec_dev_put(x) ++# define ipsec_dev_put(x) ++# define ipsec_dev_hold(x) ++#endif /* NETDEV_23 */ ++ ++#ifndef SPINLOCK ++# include ++ /* simulate spin locks and read/write locks */ ++ typedef struct { ++ volatile char lock; ++ } spinlock_t; ++ ++ typedef struct { ++ volatile unsigned int lock; ++ } rwlock_t; ++ ++# define spin_lock_init(x) { (x)->lock = 0;} ++# define rw_lock_init(x) { (x)->lock = 0; } ++ ++# define spin_lock(x) { while ((x)->lock) barrier(); (x)->lock=1;} ++# define spin_lock_irq(x) { cli(); spin_lock(x);} ++# define spin_lock_irqsave(x,flags) { save_flags(flags); spin_lock_irq(x);} ++ ++# define spin_unlock(x) { (x)->lock=0;} ++# define spin_unlock_irq(x) { spin_unlock(x); sti();} ++# define spin_unlock_irqrestore(x,flags) { spin_unlock(x); restore_flags(flags);} ++ ++# define read_lock(x) spin_lock(x) ++# define read_lock_irq(x) spin_lock_irq(x) ++# define read_lock_irqsave(x,flags) spin_lock_irqsave(x,flags) ++ ++# define read_unlock(x) spin_unlock(x) ++# define read_unlock_irq(x) spin_unlock_irq(x) ++# define read_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags) ++ ++# define write_lock(x) spin_lock(x) ++# define write_lock_irq(x) spin_lock_irq(x) ++# define write_lock_irqsave(x,flags) spin_lock_irqsave(x,flags) ++ ++# define write_unlock(x) spin_unlock(x) ++# define write_unlock_irq(x) spin_unlock_irq(x) ++# define write_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags) ++#endif /* !SPINLOCK */ ++ ++#ifndef SPINLOCK_23 ++# define spin_lock_bh(x) spin_lock_irq(x) ++# define spin_unlock_bh(x) spin_unlock_irq(x) ++ ++# define read_lock_bh(x) read_lock_irq(x) ++# define read_unlock_bh(x) read_unlock_irq(x) ++ ++# define write_lock_bh(x) write_lock_irq(x) ++# define write_unlock_bh(x) write_unlock_irq(x) ++#endif /* !SPINLOCK_23 */ ++ ++#ifndef HAVE_NETDEV_PRINTK ++#define netdev_printk(sevlevel, netdev, msglevel, format, arg...) \ ++ printk(sevlevel "%s: " format , netdev->name , ## arg) ++#endif ++ ++#endif /* _FREESWAN_KVERSIONS_H */ ++ ++/* ++ * $Log: ipsec_kversion.h,v $ ++ * Revision 1.9 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_kversion.h,v ++ * ++ * Revision 1.8 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.7 2003/07/31 22:48:08 mcr ++ * derive NET25-ness from presence of NETLINK_XFRM macro. ++ * ++ * Revision 1.6 2003/06/24 20:22:32 mcr ++ * added new global: ipsecdevices[] so that we can keep track of ++ * the ipsecX devices. They will be referenced with dev_hold(), ++ * so 2.2 may need this as well. ++ * ++ * Revision 1.5 2003/04/03 17:38:09 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * ++ * Revision 1.4 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_kversion.h,v ++ * ++ * Revision 1.3 2002/04/12 03:21:17 mcr ++ * three parameter version of ip_select_ident appears first ++ * in 2.4.2 (RH7.1) not 2.4.4. ++ * ++ * Revision 1.2 2002/03/08 21:35:22 rgb ++ * Defined LINUX_KERNEL_HAS_SNPRINTF to shut up compiler warnings after ++ * 2.4.9. (Andreas Piesk). ++ * ++ * Revision 1.1 2002/01/29 02:11:42 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from freeswan.h that also duplicated kversions.h ++ * code. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_life.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,112 @@ ++/* ++ * Definitions relevant to IPSEC lifetimes ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_life.h,v 1.4 2004/04/05 19:55:05 mcr Exp $ ++ * ++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr. ++ * ++ */ ++ ++/* ++ * This file describes the book keeping fields for the ++ * IPsec Security Association Structure. ("ipsec_sa") ++ * ++ * This structure is never allocated directly by kernel code, ++ * (it is always a static/auto or is part of a structure) ++ * so it does not have a reference count. ++ * ++ */ ++ ++#ifndef _IPSEC_LIFE_H_ ++ ++/* ++ * _count is total count. ++ * _hard is hard limit (kill SA after this number) ++ * _soft is soft limit (try to renew SA after this number) ++ * _last is used in some special cases. ++ * ++ */ ++ ++struct ipsec_lifetime64 ++{ ++ __u64 ipl_count; ++ __u64 ipl_soft; ++ __u64 ipl_hard; ++ __u64 ipl_last; ++}; ++ ++struct ipsec_lifetimes ++{ ++ /* number of bytes processed */ ++ struct ipsec_lifetime64 ipl_bytes; ++ ++ /* number of packets processed */ ++ struct ipsec_lifetime64 ipl_packets; ++ ++ /* time since SA was added */ ++ struct ipsec_lifetime64 ipl_addtime; ++ ++ /* time since SA was first used */ ++ struct ipsec_lifetime64 ipl_usetime; ++ ++ /* from rfc2367: ++ * For CURRENT, the number of different connections, ++ * endpoints, or flows that the association has been ++ * allocated towards. For HARD and SOFT, the number of ++ * these the association may be allocated towards ++ * before it expires. The concept of a connection, ++ * flow, or endpoint is system specific. ++ * ++ * mcr(2001-9-18) it is unclear what purpose these serve for FreeSWAN. ++ * They are maintained for PF_KEY compatibility. ++ */ ++ struct ipsec_lifetime64 ipl_allocations; ++}; ++ ++enum ipsec_life_alive { ++ ipsec_life_harddied = -1, ++ ipsec_life_softdied = 0, ++ ipsec_life_okay = 1 ++}; ++ ++enum ipsec_life_type { ++ ipsec_life_timebased = 1, ++ ipsec_life_countbased= 0 ++}; ++ ++#define _IPSEC_LIFE_H_ ++#endif /* _IPSEC_LIFE_H_ */ ++ ++ ++/* ++ * $Log: ipsec_life.h,v $ ++ * Revision 1.4 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_life.h,v ++ * ++ * Revision 1.3 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_life.h,v ++ * ++ * Revision 1.2 2001/11/26 09:16:14 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:25:58 mcr ++ * lifetime structure created and common functions created. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_md5h.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,140 @@ ++/* ++ * RCSID $Id: ipsec_md5h.h,v 1.9 2004/04/05 19:55:05 mcr Exp $ ++ */ ++ ++/* ++ * The rest of this file is Copyright RSA DSI. See the following comments ++ * for the full Copyright notice. ++ */ ++ ++#ifndef _IPSEC_MD5H_H_ ++#define _IPSEC_MD5H_H_ ++ ++/* GLOBAL.H - RSAREF types and constants ++ */ ++ ++/* PROTOTYPES should be set to one if and only if the compiler supports ++ function argument prototyping. ++ The following makes PROTOTYPES default to 0 if it has not already ++ been defined with C compiler flags. ++ */ ++#ifndef PROTOTYPES ++#define PROTOTYPES 1 ++#endif /* !PROTOTYPES */ ++ ++/* POINTER defines a generic pointer type */ ++typedef __u8 *POINTER; ++ ++/* UINT2 defines a two byte word */ ++typedef __u16 UINT2; ++ ++/* UINT4 defines a four byte word */ ++typedef __u32 UINT4; ++ ++/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. ++ If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it ++ returns an empty list. ++ */ ++ ++#if PROTOTYPES ++#define PROTO_LIST(list) list ++#else /* PROTOTYPES */ ++#define PROTO_LIST(list) () ++#endif /* PROTOTYPES */ ++ ++ ++/* MD5.H - header file for MD5C.C ++ */ ++ ++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All ++rights reserved. ++ ++License to copy and use this software is granted provided that it ++is identified as the "RSA Data Security, Inc. MD5 Message-Digest ++Algorithm" in all material mentioning or referencing this software ++or this function. ++ ++License is also granted to make and use derivative works provided ++that such works are identified as "derived from the RSA Data ++Security, Inc. MD5 Message-Digest Algorithm" in all material ++mentioning or referencing the derived work. ++ ++RSA Data Security, Inc. makes no representations concerning either ++the merchantability of this software or the suitability of this ++software for any particular purpose. It is provided "as is" ++without express or implied warranty of any kind. ++ ++These notices must be retained in any copies of any part of this ++documentation and/or software. ++ */ ++ ++/* MD5 context. */ ++typedef struct { ++ UINT4 state[4]; /* state (ABCD) */ ++ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ ++ unsigned char buffer[64]; /* input buffer */ ++} MD5_CTX; ++ ++void MD5Init PROTO_LIST ((void *)); ++void MD5Update PROTO_LIST ++ ((void *, unsigned char *, __u32)); ++void MD5Final PROTO_LIST ((unsigned char [16], void *)); ++ ++#endif /* _IPSEC_MD5H_H_ */ ++ ++/* ++ * $Log: ipsec_md5h.h,v $ ++ * Revision 1.9 2004/04/05 19:55:05 mcr ++ * Moved from linux/include/freeswan/ipsec_md5h.h,v ++ * ++ * Revision 1.8 2002/09/10 01:45:09 mcr ++ * changed type of MD5_CTX and SHA1_CTX to void * so that ++ * the function prototypes would match, and could be placed ++ * into a pointer to a function. ++ * ++ * Revision 1.7 2002/04/24 07:36:46 mcr ++ * Moved from ./klips/net/ipsec/ipsec_md5h.h,v ++ * ++ * Revision 1.6 1999/12/13 13:59:13 rgb ++ * Quick fix to argument size to Update bugs. ++ * ++ * Revision 1.5 1999/12/07 18:16:23 rgb ++ * Fixed comments at end of #endif lines. ++ * ++ * Revision 1.4 1999/04/06 04:54:26 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.3 1999/01/22 06:19:58 rgb ++ * 64-bit clean-up. ++ * ++ * Revision 1.2 1998/11/30 13:22:54 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.1 1998/06/18 21:27:48 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.2 1998/04/23 20:54:03 rgb ++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when ++ * verified. ++ * ++ * Revision 1.1 1998/04/09 03:04:21 henry ++ * sources moved up from linux/net/ipsec ++ * these two include files modified not to include others except in kernel ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:48:53 ji ++ * Release update only. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_param.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,333 @@ ++/* ++ * @(#) FreeSWAN tunable paramaters ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_param.h,v 1.24 2004/04/05 19:55:06 mcr Exp $ ++ * ++ */ ++ ++/* ++ * This file provides a set of #define's which may be tuned by various ++ * people/configurations. It keeps all compile-time tunables in one place. ++ * ++ * This file should be included before all other IPsec kernel-only files. ++ * ++ */ ++ ++#ifndef _IPSEC_PARAM_H_ ++ ++#ifdef __KERNEL__ ++#include "ipsec_kversion.h" ++ ++/* Set number of ipsecX virtual devices here. */ ++/* This must be < exp(field width of IPSEC_DEV_FORMAT) */ ++/* It must also be reasonable so as not to overload the memory and CPU */ ++/* constraints of the host. */ ++#define IPSEC_NUM_IF 4 ++/* The field width must be < IF_NAM_SIZ - strlen("ipsec") - 1. */ ++/* With "ipsec" being 5 characters, that means 10 is the max field width */ ++/* but machine memory and CPU constraints are not likely to tollerate */ ++/* more than 3 digits. The default is one digit. */ ++/* Update: userland scripts get upset if they can't find "ipsec0", so */ ++/* for now, no "0"-padding should be used (which would have been helpful */ ++/* to make text-searches work */ ++#define IPSEC_DEV_FORMAT "ipsec%d" ++/* For, say, 500 virtual ipsec devices, I would recommend: */ ++/* #define IPSEC_NUM_IF 500 */ ++/* #define IPSEC_DEV_FORMAT "ipsec%03d" */ ++/* Note that the "interfaces=" line in /etc/ipsec.conf would be, um, challenging. */ ++ ++/* use dynamic ipsecX device allocation */ ++#ifndef CONFIG_IPSEC_DYNDEV ++#define CONFIG_IPSEC_DYNDEV 1 ++#endif /* CONFIG_IPSEC_DYNDEV */ ++ ++ ++#ifdef CONFIG_IPSEC_BIGGATE ++# define SADB_HASHMOD 8069 ++#else /* CONFIG_IPSEC_BIGGATE */ ++# define SADB_HASHMOD 257 ++#endif /* CONFIG_IPSEC_BIGGATE */ ++#endif /* __KERNEL__ */ ++ ++/* ++ * This is for the SA reference table. This number is related to the ++ * maximum number of SAs that KLIPS can concurrently deal with, plus enough ++ * space for keeping expired SAs around. ++ * ++ * TABLE_MAX_WIDTH is the number of bits that we will use. ++ * MAIN_TABLE_WIDTH is the number of bits used for the primary index table. ++ * ++ */ ++#ifndef IPSEC_SA_REF_TABLE_IDX_WIDTH ++# define IPSEC_SA_REF_TABLE_IDX_WIDTH 16 ++#endif ++ ++#ifndef IPSEC_SA_REF_MAINTABLE_IDX_WIDTH ++# define IPSEC_SA_REF_MAINTABLE_IDX_WIDTH 4 ++#endif ++ ++#ifndef IPSEC_SA_REF_FREELIST_NUM_ENTRIES ++# define IPSEC_SA_REF_FREELIST_NUM_ENTRIES 256 ++#endif ++ ++#ifndef IPSEC_SA_REF_CODE ++# define IPSEC_SA_REF_CODE 1 ++#endif ++ ++#ifdef __KERNEL__ ++/* This is defined for 2.4, but not 2.2.... */ ++#ifndef ARPHRD_VOID ++# define ARPHRD_VOID 0xFFFF ++#endif ++ ++/* ++ * Worry about PROC_FS stuff ++ */ ++#if defined(PROC_FS_2325) ++/* kernel 2.4 */ ++# define IPSEC_PROC_LAST_ARG ,int *eof,void *data ++# define IPSEC_PROCFS_DEBUG_NO_STATIC ++# define IPSEC_PROC_SUBDIRS ++#else ++/* kernel <2.4 */ ++# define IPSEC_PROCFS_DEBUG_NO_STATIC DEBUG_NO_STATIC ++ ++# ifndef PROC_NO_DUMMY ++# define IPSEC_PROC_LAST_ARG , int dummy ++# else ++# define IPSEC_PROC_LAST_ARG ++# endif /* !PROC_NO_DUMMY */ ++#endif /* PROC_FS_2325 */ ++ ++#if !defined(LINUX_KERNEL_HAS_SNPRINTF) ++/* GNU CPP specific! */ ++# define snprintf(buf, len, fmt...) sprintf(buf, ##fmt) ++#endif /* !LINUX_KERNEL_HAS_SNPRINTF */ ++ ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++ ++#ifndef KLIPS_FIXES_DES_PARITY ++# define KLIPS_FIXES_DES_PARITY 1 ++#endif /* !KLIPS_FIXES_DES_PARITY */ ++ ++/* we don't really want to print these unless there are really big problems */ ++#ifndef KLIPS_DIVULGE_CYPHER_KEY ++# define KLIPS_DIVULGE_CYPHER_KEY 0 ++#endif /* !KLIPS_DIVULGE_CYPHER_KEY */ ++ ++#ifndef KLIPS_DIVULGE_HMAC_KEY ++# define KLIPS_DIVULGE_HMAC_KEY 0 ++#endif /* !KLIPS_DIVULGE_HMAC_KEY */ ++ ++#ifndef IPSEC_DISALLOW_IPOPTIONS ++# define IPSEC_DISALLOW_IPOPTIONS 1 ++#endif /* !KLIPS_DIVULGE_HMAC_KEY */ ++ ++/* extra toggles for regression testing */ ++#ifdef CONFIG_IPSEC_REGRESS ++ ++/* ++ * should pfkey_acquire() become 100% lossy? ++ * ++ */ ++extern int sysctl_ipsec_regress_pfkey_lossage; ++#ifndef KLIPS_PFKEY_ACQUIRE_LOSSAGE ++# ifdef CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE ++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 100 ++# else /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */ ++/* not by default! */ ++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 0 ++# endif /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */ ++#endif /* KLIPS_PFKEY_ACQUIRE_LOSSAGE */ ++ ++#endif /* CONFIG_IPSEC_REGRESS */ ++ ++ ++/* ++ * debugging routines. ++ */ ++#ifdef CONFIG_IPSEC_DEBUG ++extern void ipsec_print_ip(struct iphdr *ip); ++ ++ #define KLIPS_PRINT(flag, format, args...) \ ++ ((flag) ? printk(KERN_INFO format , ## args) : 0) ++ #define KLIPS_PRINTMORE(flag, format, args...) \ ++ ((flag) ? printk(format , ## args) : 0) ++ #define KLIPS_IP_PRINT(flag, ip) \ ++ ((flag) ? ipsec_print_ip(ip) : 0) ++#else /* CONFIG_IPSEC_DEBUG */ ++ #define KLIPS_PRINT(flag, format, args...) do ; while(0) ++ #define KLIPS_PRINTMORE(flag, format, args...) do ; while(0) ++ #define KLIPS_IP_PRINT(flag, ip) do ; while(0) ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ++/* ++ * Stupid kernel API differences in APIs. Not only do some ++ * kernels not have ip_select_ident, but some have differing APIs, ++ * and SuSE has one with one parameter, but no way of checking to ++ * see what is really what. ++ */ ++ ++#ifdef SUSE_LINUX_2_4_19_IS_STUPID ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph) ++#else ++ ++/* simplest case, nothing */ ++#if !defined(IP_SELECT_IDENT) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0) ++#endif ++ ++/* kernels > 2.3.37-ish */ ++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst) ++#endif ++ ++/* kernels > 2.4.2 */ ++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL) ++#endif ++ ++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */ ++ ++/* ++ * make klips fail test:east-espiv-01. ++ * exploit is at testing/attacks/espiv ++ * ++ */ ++#define KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK 0 ++ ++ ++/* IP_FRAGMENT_LINEARIZE is set in freeswan.h if Kernel > 2.4.4 */ ++#ifndef IP_FRAGMENT_LINEARIZE ++# define IP_FRAGMENT_LINEARIZE 0 ++#endif /* IP_FRAGMENT_LINEARIZE */ ++#endif /* __KERNEL__ */ ++ ++#define _IPSEC_PARAM_H_ ++#endif /* _IPSEC_PARAM_H_ */ ++ ++/* ++ * $Log: ipsec_param.h,v $ ++ * Revision 1.24 2004/04/05 19:55:06 mcr ++ * Moved from linux/include/freeswan/ipsec_param.h,v ++ * ++ * Revision 1.23 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.22 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.21.4.1 2003/10/29 01:10:19 mcr ++ * elimited "struct sa_id" ++ * ++ * Revision 1.21 2003/04/03 17:38:18 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * Change indentation for readability. ++ * ++ * Revision 1.20 2003/03/14 08:09:26 rgb ++ * Fixed up CONFIG_IPSEC_DYNDEV definitions. ++ * ++ * Revision 1.19 2003/01/30 02:31:43 rgb ++ * ++ * Rename SAref table macro names for clarity. ++ * ++ * Revision 1.18 2002/09/30 19:06:26 rgb ++ * Reduce default table to 16 bits width. ++ * ++ * Revision 1.17 2002/09/20 15:40:29 rgb ++ * Define switch to activate new SAref code. ++ * Prefix macros with "IPSEC_". ++ * Rework saref freelist. ++ * Restrict some bits to kernel context for use to klips utils. ++ * ++ * Revision 1.16 2002/09/20 05:00:31 rgb ++ * Define switch to divulge hmac keys for debugging. ++ * Added IPOPTIONS switch. ++ * ++ * Revision 1.15 2002/09/19 02:34:24 mcr ++ * define IPSEC_PROC_SUBDIRS if we are 2.4, and use that in ipsec_proc.c ++ * to decide if we are to create /proc/net/ipsec/. ++ * ++ * Revision 1.14 2002/08/30 01:20:54 mcr ++ * reorganized 2.0/2.2/2.4 procfs support macro so match ++ * 2.4 values/typedefs. ++ * ++ * Revision 1.13 2002/07/28 22:03:28 mcr ++ * added some documentation to SA_REF_* ++ * turned on fix for ESPIV attack, now that we have the attack code. ++ * ++ * Revision 1.12 2002/07/26 08:48:31 rgb ++ * Added SA ref table code. ++ * ++ * Revision 1.11 2002/07/23 02:57:45 rgb ++ * Define ARPHRD_VOID for < 2.4 kernels. ++ * ++ * Revision 1.10 2002/05/27 21:37:28 rgb ++ * Set the defaults sanely for those adventurous enough to try more than 1 ++ * digit of ipsec devices. ++ * ++ * Revision 1.9 2002/05/27 18:56:07 rgb ++ * Convert to dynamic ipsec device allocation. ++ * ++ * Revision 1.8 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_param.h,v ++ * ++ * Revision 1.7 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.6 2002/01/29 02:11:42 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from freeswan.h that also duplicated kversions.h ++ * code. ++ * ++ * Revision 1.5 2002/01/28 19:22:01 mcr ++ * by default, turn off LINEARIZE option ++ * (let kversions.h turn it on) ++ * ++ * Revision 1.4 2002/01/20 20:19:36 mcr ++ * renamed option to IP_FRAGMENT_LINEARIZE. ++ * ++ * Revision 1.3 2002/01/12 02:57:25 mcr ++ * first regression test causes acquire messages to be lost ++ * 100% of the time. This is to help testing of pluto. ++ * ++ * Revision 1.2 2001/11/26 09:16:14 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.3 2001/10/23 04:40:16 mcr ++ * added #define for DIVULGING session keys in debug output. ++ * ++ * Revision 1.1.2.2 2001/10/22 20:53:25 mcr ++ * added a define to control forcing of DES parity. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:20:19 mcr ++ * many common kernel configuration questions centralized. ++ * more things remain that should be moved from freeswan.h. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_policy.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,216 @@ ++#ifndef _IPSEC_POLICY_H ++/* ++ * policy interface file between pluto and applications ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: ipsec_policy.h,v 1.5 2004/06/17 04:23:33 mcr Exp $ ++ */ ++#define _IPSEC_POLICY_H /* seen it, no need to see it again */ ++ ++ ++/* ++ * this file defines an interface between an application (or rather an ++ * application library) and a key/policy daemon. It provides for inquiries ++ * as to the current state of a connected socket, as well as for general ++ * questions. ++ * ++ * In general, the interface is defined as a series of functional interfaces, ++ * and the policy messages should be internal. However, because this is in ++ * fact an ABI between pieces of the system that may get compiled and revised ++ * seperately, this ABI must be public and revision controlled. ++ * ++ * It is expected that the daemon will always support previous versions. ++ */ ++ ++#define IPSEC_POLICY_MSG_REVISION (unsigned)200305061 ++ ++enum ipsec_policy_command { ++ IPSEC_CMD_QUERY_FD = 1, ++ IPSEC_CMD_QUERY_HOSTPAIR = 2, ++ IPSEC_CMD_QUERY_DSTONLY = 3, ++}; ++ ++struct ipsec_policy_msg_head { ++ u_int32_t ipm_version; ++ u_int32_t ipm_msg_len; ++ u_int32_t ipm_msg_type; ++ u_int32_t ipm_msg_seq; ++}; ++ ++enum ipsec_privacy_quality { ++ IPSEC_PRIVACY_NONE = 0, ++ IPSEC_PRIVACY_INTEGRAL = 4, /* not private at all. AH-like */ ++ IPSEC_PRIVACY_UNKNOWN = 8, /* something is claimed, but details unavail */ ++ IPSEC_PRIVACY_ROT13 = 12, /* trivially breakable, i.e. 1DES */ ++ IPSEC_PRIVACY_GAK = 16, /* known eavesdroppers */ ++ IPSEC_PRIVACY_PRIVATE = 32, /* secure for at least a decade */ ++ IPSEC_PRIVACY_STRONG = 64, /* ridiculously secure */ ++ IPSEC_PRIVACY_TORTOISE = 192, /* even stronger, but very slow */ ++ IPSEC_PRIVACY_OTP = 224, /* some kind of *true* one time pad */ ++}; ++ ++enum ipsec_bandwidth_quality { ++ IPSEC_QOS_UNKNOWN = 0, /* unknown bandwidth */ ++ IPSEC_QOS_INTERACTIVE = 16, /* reasonably moderate jitter, moderate fast. ++ Good enough for telnet/ssh. */ ++ IPSEC_QOS_VOIP = 32, /* faster crypto, predicable jitter */ ++ IPSEC_QOS_FTP = 64, /* higher throughput crypto, perhaps hardware ++ offloaded, but latency/jitter may be bad */ ++ IPSEC_QOS_WIRESPEED = 128, /* expect to be able to fill your pipe */ ++}; ++ ++/* moved from programs/pluto/constants.h */ ++/* IPsec AH transform values ++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.3 ++ * and in http://www.iana.org/assignments/isakmp-registry ++ */ ++enum ipsec_authentication_algo { ++ AH_MD5=2, ++ AH_SHA=3, ++ AH_DES=4, ++ AH_SHA2_256=5, ++ AH_SHA2_384=6, ++ AH_SHA2_512=7 ++}; ++ ++/* IPsec ESP transform values ++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.4 ++ * and from http://www.iana.org/assignments/isakmp-registry ++ */ ++ ++enum ipsec_cipher_algo { ++ ESP_reserved=0, ++ ESP_DES_IV64=1, ++ ESP_DES=2, ++ ESP_3DES=3, ++ ESP_RC5=4, ++ ESP_IDEA=5, ++ ESP_CAST=6, ++ ESP_BLOWFISH=7, ++ ESP_3IDEA=8, ++ ESP_DES_IV32=9, ++ ESP_RC4=10, ++ ESP_NULL=11, ++ ESP_AES=12, ++}; ++ ++/* IPCOMP transform values ++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.5 ++ */ ++ ++enum ipsec_comp_algo { ++ IPCOMP_OUI= 1, ++ IPCOMP_DEFLATE= 2, ++ IPCOMP_LZS= 3, ++ IPCOMP_V42BIS= 4 ++}; ++ ++/* Identification type values ++ * RFC 2407 The Internet IP security Domain of Interpretation for ISAKMP 4.6.2.1 ++ */ ++ ++enum ipsec_id_type { ++ ID_IMPOSSIBLE= (-2), /* private to Pluto */ ++ ID_MYID= (-1), /* private to Pluto */ ++ ID_NONE= 0, /* private to Pluto */ ++ ID_IPV4_ADDR= 1, ++ ID_FQDN= 2, ++ ID_USER_FQDN= 3, ++ ID_IPV4_ADDR_SUBNET= 4, ++ ID_IPV6_ADDR= 5, ++ ID_IPV6_ADDR_SUBNET= 6, ++ ID_IPV4_ADDR_RANGE= 7, ++ ID_IPV6_ADDR_RANGE= 8, ++ ID_DER_ASN1_DN= 9, ++ ID_DER_ASN1_GN= 10, ++ ID_KEY_ID= 11 ++}; ++ ++/* Certificate type values ++ * RFC 2408 ISAKMP, chapter 3.9 ++ */ ++enum ipsec_cert_type { ++ CERT_NONE= 0, /* none, or guess from file contents */ ++ CERT_PKCS7_WRAPPED_X509= 1, /* self-signed certificate from disk */ ++ CERT_PGP= 2, ++ CERT_DNS_SIGNED_KEY= 3, /* KEY RR from DNS */ ++ CERT_X509_SIGNATURE= 4, ++ CERT_X509_KEY_EXCHANGE= 5, ++ CERT_KERBEROS_TOKENS= 6, ++ CERT_CRL= 7, ++ CERT_ARL= 8, ++ CERT_SPKI= 9, ++ CERT_X509_ATTRIBUTE= 10, ++ CERT_RAW_RSA= 11, /* raw RSA from config file */ ++}; ++ ++/* a SIG record in ASCII */ ++struct ipsec_dns_sig { ++ char fqdn[256]; ++ char dns_sig[768]; /* empty string if not signed */ ++}; ++ ++struct ipsec_raw_key { ++ char id_name[256]; ++ char fs_keyid[8]; ++}; ++ ++struct ipsec_identity { ++ enum ipsec_id_type ii_type; ++ enum ipsec_cert_type ii_format; ++ union { ++ struct ipsec_dns_sig ipsec_dns_signed; ++ /* some thing for PGP */ ++ /* some thing for PKIX */ ++ struct ipsec_raw_key ipsec_raw_key; ++ } ii_credential; ++}; ++ ++#define IPSEC_MAX_CREDENTIALS 32 ++ ++struct ipsec_policy_cmd_query { ++ struct ipsec_policy_msg_head head; ++ ++ /* Query section */ ++ ip_address query_local; /* us */ ++ ip_address query_remote; /* them */ ++ u_short src_port, dst_port; ++ ++ /* Answer section */ ++ enum ipsec_privacy_quality strength; ++ enum ipsec_bandwidth_quality bandwidth; ++ enum ipsec_authentication_algo auth_detail; ++ enum ipsec_cipher_algo esp_detail; ++ enum ipsec_comp_algo comp_detail; ++ ++ int credential_count; ++ ++ struct ipsec_identity credentials[IPSEC_MAX_CREDENTIALS]; ++}; ++ ++#define IPSEC_POLICY_SOCKET "/var/run/pluto.info" ++ ++/* prototypes */ ++extern err_t ipsec_policy_lookup(int fd, struct ipsec_policy_cmd_query *result); ++extern err_t ipsec_policy_init(void); ++extern err_t ipsec_policy_final(void); ++extern err_t ipsec_policy_readmsg(int policysock, ++ unsigned char *buf, size_t buflen); ++extern err_t ipsec_policy_sendrecv(unsigned char *buf, size_t buflen); ++extern err_t ipsec_policy_cgilookup(struct ipsec_policy_cmd_query *result); ++ ++ ++extern const char *ipsec_policy_version_code(void); ++extern const char *ipsec_policy_version_string(void); ++ ++#endif /* _IPSEC_POLICY_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_proto.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,151 @@ ++/* ++ * @(#) prototypes for FreeSWAN functions ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_proto.h,v 1.8 2004/04/05 19:55:06 mcr Exp $ ++ * ++ */ ++ ++#ifndef _IPSEC_PROTO_H_ ++ ++#include "ipsec_param.h" ++ ++/* ++ * This file is a kernel only file that declares prototypes for ++ * all intra-module function calls and global data structures. ++ * ++ * Include this file last. ++ * ++ */ ++ ++/* ipsec_init.c */ ++extern struct prng ipsec_prng; ++ ++/* ipsec_sa.c */ ++extern struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD]; ++extern spinlock_t tdb_lock; ++extern int ipsec_sadb_init(void); ++ ++extern struct ipsec_sa *ipsec_sa_getbyid(ip_said *); ++extern int ipsec_sa_put(struct ipsec_sa *); ++extern /* void */ int ipsec_sa_del(struct ipsec_sa *); ++extern /* void */ int ipsec_sa_delchain(struct ipsec_sa *); ++extern /* void */ int ipsec_sa_add(struct ipsec_sa *); ++ ++extern int ipsec_sadb_cleanup(__u8); ++extern int ipsec_sa_wipe(struct ipsec_sa *); ++ ++/* debug declarations */ ++ ++/* ipsec_proc.c */ ++extern int ipsec_proc_init(void); ++extern void ipsec_proc_cleanup(void); ++ ++/* ipsec_radij.c */ ++extern int ipsec_makeroute(struct sockaddr_encap *ea, ++ struct sockaddr_encap *em, ++ ip_said said, ++ uint32_t pid, ++ struct sk_buff *skb, ++ struct ident *ident_s, ++ struct ident *ident_d); ++ ++extern int ipsec_breakroute(struct sockaddr_encap *ea, ++ struct sockaddr_encap *em, ++ struct sk_buff **first, ++ struct sk_buff **last); ++ ++int ipsec_radijinit(void); ++int ipsec_cleareroutes(void); ++int ipsec_radijcleanup(void); ++ ++/* ipsec_life.c */ ++extern enum ipsec_life_alive ipsec_lifetime_check(struct ipsec_lifetime64 *il64, ++ const char *lifename, ++ const char *saname, ++ enum ipsec_life_type ilt, ++ enum ipsec_direction idir, ++ struct ipsec_sa *ips); ++ ++ ++extern int ipsec_lifetime_format(char *buffer, ++ int buflen, ++ char *lifename, ++ enum ipsec_life_type timebaselife, ++ struct ipsec_lifetime64 *lifetime); ++ ++extern void ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime, ++ __u64 newvalue); ++ ++extern void ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime, ++ __u64 newvalue); ++ ++ ++ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ ++extern int debug_xform; ++extern int debug_eroute; ++extern int debug_spi; ++extern int debug_netlink; ++ ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ++ ++ ++#define _IPSEC_PROTO_H ++#endif /* _IPSEC_PROTO_H_ */ ++ ++/* ++ * $Log: ipsec_proto.h,v $ ++ * Revision 1.8 2004/04/05 19:55:06 mcr ++ * Moved from linux/include/freeswan/ipsec_proto.h,v ++ * ++ * Revision 1.7 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.6.30.1 2003/10/29 01:10:19 mcr ++ * elimited "struct sa_id" ++ * ++ * Revision 1.6 2002/05/23 07:13:48 rgb ++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount. ++ * ++ * Revision 1.5 2002/05/14 02:36:40 rgb ++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion ++ * with "put" usage in the kernel. ++ * ++ * Revision 1.4 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_proto.h,v ++ * ++ * Revision 1.3 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.2 2001/11/26 09:16:15 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:21:01 mcr ++ * ipsec_proto.h created to keep prototypes rather than deal with ++ * cyclic dependancies of structures and prototypes in .h files. ++ * ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_radij.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,176 @@ ++/* ++ * @(#) Definitions relevant to the IPSEC <> radij tree interfacing ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_radij.h,v 1.21 2004/04/29 11:06:42 ken Exp $ ++ */ ++ ++#ifndef _IPSEC_RADIJ_H ++ ++#include ++ ++int ipsec_walk(char *); ++ ++int ipsec_rj_walker_procprint(struct radij_node *, void *); ++int ipsec_rj_walker_delete(struct radij_node *, void *); ++ ++/* This structure is used to pass information between ++ * ipsec_eroute_get_info and ipsec_rj_walker_procprint ++ * (through rj_walktree) and between calls of ipsec_rj_walker_procprint. ++ */ ++struct wsbuf ++{ ++ /* from caller of ipsec_eroute_get_info: */ ++ char *const buffer; /* start of buffer provided */ ++ const int length; /* length of buffer provided */ ++ const off_t offset; /* file position of first character of interest */ ++ /* accumulated by ipsec_rj_walker_procprint: */ ++ int len; /* number of character filled into buffer */ ++ off_t begin; /* file position contained in buffer[0] (<=offset) */ ++}; ++ ++extern struct radij_node_head *rnh; ++extern spinlock_t eroute_lock; ++ ++struct eroute * ipsec_findroute(struct sockaddr_encap *); ++ ++#define O1(x) (int)(((x)>>24)&0xff) ++#define O2(x) (int)(((x)>>16)&0xff) ++#define O3(x) (int)(((x)>>8)&0xff) ++#define O4(x) (int)(((x))&0xff) ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_radij; ++void rj_dumptrees(void); ++ ++#define DB_RJ_DUMPTREES 0x0001 ++#define DB_RJ_FINDROUTE 0x0002 ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#define _IPSEC_RADIJ_H ++#endif ++ ++/* ++ * $Log: ipsec_radij.h,v $ ++ * Revision 1.21 2004/04/29 11:06:42 ken ++ * Last bits from 2.06 procfs updates ++ * ++ * Revision 1.20 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.19 2004/04/05 19:55:06 mcr ++ * Moved from linux/include/freeswan/ipsec_radij.h,v ++ * ++ * Revision 1.18 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_radij.h,v ++ * ++ * Revision 1.17 2001/11/26 09:23:49 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.16.2.1 2001/09/25 02:21:17 mcr ++ * ipsec_proto.h created to keep prototypes rather than deal with ++ * cyclic dependancies of structures and prototypes in .h files. ++ * ++ * Revision 1.16 2001/09/15 16:24:04 rgb ++ * Re-inject first and last HOLD packet when an eroute REPLACE is done. ++ * ++ * Revision 1.15 2001/09/14 16:58:37 rgb ++ * Added support for storing the first and last packets through a HOLD. ++ * ++ * Revision 1.14 2001/09/08 21:13:32 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.13 2001/06/14 19:35:09 rgb ++ * Update copyright date. ++ * ++ * Revision 1.12 2001/05/27 06:12:11 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.11 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.10 1999/11/17 15:53:39 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.9 1999/10/01 00:01:23 rgb ++ * Added eroute structure locking. ++ * ++ * Revision 1.8 1999/04/11 00:28:59 henry ++ * GPL boilerplate ++ * ++ * Revision 1.7 1999/04/06 04:54:26 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.6 1999/01/22 06:23:26 rgb ++ * Cruft clean-out. ++ * ++ * Revision 1.5 1998/10/25 02:42:08 rgb ++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an ++ * argument to be able to transmit more infomation about errors. ++ * ++ * Revision 1.4 1998/10/19 14:44:29 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.3 1998/07/28 00:03:31 rgb ++ * Comment out temporary inet_nto4u() kluge. ++ * ++ * Revision 1.2 1998/07/14 18:22:00 rgb ++ * Add function to clear the eroute table. ++ * ++ * Revision 1.1 1998/06/18 21:27:49 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.5 1998/05/25 20:30:38 rgb ++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions. ++ * ++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and ++ * add ipsec_rj_walker_delete. ++ * ++ * Revision 1.4 1998/05/21 13:02:56 rgb ++ * Imported definitions from ipsec_radij.c and radij.c to support /proc 3k ++ * limit fix. ++ * ++ * Revision 1.3 1998/04/21 21:29:09 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.2 1998/04/14 17:30:39 rgb ++ * Fix up compiling errors for radij tree memory reclamation. ++ * ++ * Revision 1.1 1998/04/09 03:06:10 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_rcv.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,279 @@ ++/* ++ * ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_rcv.h,v 1.21 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#ifndef IPSEC_RCV_H ++#define IPSEC_RCV_H ++ ++#include "openswan/ipsec_auth.h" ++ ++#define DB_RX_PKTRX 0x0001 ++#define DB_RX_PKTRX2 0x0002 ++#define DB_RX_DMP 0x0004 ++#define DB_RX_IPSA 0x0010 ++#define DB_RX_XF 0x0020 ++#define DB_RX_IPAD 0x0040 ++#define DB_RX_INAU 0x0080 ++#define DB_RX_OINFO 0x0100 ++#define DB_RX_OINFO2 0x0200 ++#define DB_RX_OH 0x0400 ++#define DB_RX_REPLAY 0x0800 ++ ++#ifdef __KERNEL__ ++/* struct options; */ ++ ++#define __NO_VERSION__ ++#include ++#include /* for CONFIG_IP_FORWARD */ ++#include ++#include ++ ++#define IPSEC_BIRTH_TEMPLATE_MAXLEN 256 ++ ++struct ipsec_birth_reply { ++ int packet_template_len; ++ unsigned char packet_template[IPSEC_BIRTH_TEMPLATE_MAXLEN]; ++}; ++ ++extern struct ipsec_birth_reply ipsec_ipv4_birth_packet; ++extern struct ipsec_birth_reply ipsec_ipv6_birth_packet; ++ ++enum ipsec_rcv_value { ++ IPSEC_RCV_LASTPROTO=1, ++ IPSEC_RCV_OK=0, ++ IPSEC_RCV_BADPROTO=-1, ++ IPSEC_RCV_BADLEN=-2, ++ IPSEC_RCV_ESP_BADALG=-3, ++ IPSEC_RCV_3DES_BADBLOCKING=-4, ++ IPSEC_RCV_ESP_DECAPFAIL=-5, ++ IPSEC_RCV_DECAPFAIL=-6, ++ IPSEC_RCV_SAIDNOTFOUND=-7, ++ IPSEC_RCV_IPCOMPALONE=-8, ++ IPSEC_RCV_IPCOMPFAILED=-10, ++ IPSEC_RCV_SAIDNOTLIVE=-11, ++ IPSEC_RCV_FAILEDINBOUND=-12, ++ IPSEC_RCV_LIFETIMEFAILED=-13, ++ IPSEC_RCV_BADAUTH=-14, ++ IPSEC_RCV_REPLAYFAILED=-15, ++ IPSEC_RCV_AUTHFAILED=-16, ++ IPSEC_RCV_REPLAYROLLED=-17, ++ IPSEC_RCV_BAD_DECRYPT=-18 ++}; ++ ++struct ipsec_rcv_state { ++ struct sk_buff *skb; ++ struct net_device_stats *stats; ++ struct iphdr *ipp; ++ struct ipsec_sa *ipsp; ++ int len; ++ int ilen; ++ int authlen; ++ int hard_header_len; ++ int iphlen; ++ struct auth_alg *authfuncs; ++ ip_said said; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ __u8 next_header; ++ __u8 hash[AH_AMAX]; ++ char ipsaddr_txt[ADDRTOA_BUF]; ++ char ipdaddr_txt[ADDRTOA_BUF]; ++ __u8 *octx; ++ __u8 *ictx; ++ int ictx_len; ++ int octx_len; ++ union { ++ struct { ++ struct esphdr *espp; ++ } espstuff; ++ struct { ++ struct ahhdr *ahp; ++ } ahstuff; ++ struct { ++ struct ipcomphdr *compp; ++ } ipcompstuff; ++ } protostuff; ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ __u8 natt_type; ++ __u16 natt_sport; ++ __u16 natt_dport; ++ int natt_len; ++#endif ++}; ++ ++extern int ++#ifdef PROTO_HANDLER_SINGLE_PARM ++ipsec_rcv(struct sk_buff *skb); ++#else /* PROTO_HANDLER_SINGLE_PARM */ ++ipsec_rcv(struct sk_buff *skb, ++#ifdef NET_21 ++ unsigned short xlen); ++#else /* NET_21 */ ++ struct device *dev, ++ struct options *opt, ++ __u32 daddr, ++ unsigned short len, ++ __u32 saddr, ++ int redo, ++ struct inet_protocol *protocol); ++#endif /* NET_21 */ ++#endif /* PROTO_HANDLER_SINGLE_PARM */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_rcv; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#define ipsec_rcv_dmp(_x,_y, _z) if (debug_rcv && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z) ++ ++extern int sysctl_ipsec_inbound_policy_check; ++#endif /* __KERNEL__ */ ++ ++#endif /* IPSEC_RCV_H */ ++ ++/* ++ * $Log: ipsec_rcv.h,v $ ++ * Revision 1.21 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.20 2004/04/05 19:55:06 mcr ++ * Moved from linux/include/freeswan/ipsec_rcv.h,v ++ * ++ * Revision 1.19 2003/12/15 18:13:09 mcr ++ * when compiling with NAT traversal, don't assume that the ++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP ++ * is set. ++ * ++ * Revision 1.18 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.17 2002/09/03 16:32:32 mcr ++ * definitions of ipsec_birth_reply. ++ * ++ * Revision 1.16 2002/05/14 02:36:00 rgb ++ * Change references to _TDB to _IPSA. ++ * ++ * Revision 1.15 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_rcv.h,v ++ * ++ * Revision 1.14 2001/09/07 22:15:48 rgb ++ * Fix for removal of transport layer protocol handler arg in 2.4.4. ++ * ++ * Revision 1.13 2001/06/14 19:35:09 rgb ++ * Update copyright date. ++ * ++ * Revision 1.12 2001/03/16 07:36:44 rgb ++ * Fixed #endif comment to sate compiler. ++ * ++ * Revision 1.11 2000/09/21 04:34:21 rgb ++ * Moved declaration of sysctl_ipsec_inbound_policy_check outside ++ * CONFIG_IPSEC_DEBUG. (MB) ++ * ++ * Revision 1.10 2000/09/18 02:36:10 rgb ++ * Exported sysctl_ipsec_inbound_policy_check for skb_decompress(). ++ * ++ * Revision 1.9 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.8 1999/11/18 04:09:19 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.7 1999/05/25 01:45:37 rgb ++ * Fix version macros for 2.0.x as a module. ++ * ++ * Revision 1.6 1999/05/08 21:24:27 rgb ++ * Add includes for 2.2.x include into net/ipv4/protocol.c ++ * ++ * Revision 1.5 1999/05/05 22:02:32 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.4 1999/04/11 00:28:59 henry ++ * GPL boilerplate ++ * ++ * Revision 1.3 1999/04/06 04:54:27 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.2 1999/01/22 20:06:59 rgb ++ * Fixed cut-and-paste error from ipsec_esp.h. ++ * ++ * Revision 1.1 1999/01/21 20:29:12 rgb ++ * Converted from transform switching to algorithm switching. ++ * ++ * Log: ipsec_esp.h,v ++ * Revision 1.4 1998/08/12 00:07:32 rgb ++ * Added data structures for new xforms: null, {,3}dessha1. ++ * ++ * Revision 1.3 1998/07/14 15:57:01 rgb ++ * Add #ifdef __KERNEL__ to protect kernel-only structures. ++ * ++ * Revision 1.2 1998/06/25 19:33:46 rgb ++ * Add prototype for protocol receive function. ++ * Rearrange for more logical layout. ++ * ++ * Revision 1.1 1998/06/18 21:27:45 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.6 1998/06/05 02:28:08 rgb ++ * Minor comment fix. ++ * ++ * Revision 1.5 1998/05/27 22:34:00 rgb ++ * Changed structures to accomodate key separation. ++ * ++ * Revision 1.4 1998/05/18 22:28:43 rgb ++ * Disable key printing facilities from /proc/net/ipsec_*. ++ * ++ * Revision 1.3 1998/04/21 21:29:07 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.2 1998/04/12 22:03:20 rgb ++ * Updated ESP-3DES-HMAC-MD5-96, ++ * ESP-DES-HMAC-MD5-96, ++ * AH-HMAC-MD5-96, ++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository ++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts. ++ * ++ * Fixed eroute references in /proc/net/ipsec*. ++ * ++ * Started to patch module unloading memory leaks in ipsec_netlink and ++ * radij tree unloading. ++ * ++ * Revision 1.1 1998/04/09 03:06:00 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.5 1997/06/03 04:24:48 ji ++ * Added ESP-3DES-MD5-96 transform. ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * Added definitions for new ESP transforms. ++ * ++ * Revision 0.3 1996/11/20 14:35:48 ji ++ * Minor Cleanup. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_sa.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,341 @@ ++/* ++ * @(#) Definitions of IPsec Security Association (ipsec_sa) ++ * ++ * Copyright (C) 2001, 2002, 2003 ++ * Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_sa.h,v 1.19 2004/04/05 19:55:06 mcr Exp $ ++ * ++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr. ++ * ++ */ ++ ++/* ++ * This file describes the IPsec Security Association Structure. ++ * ++ * This structure keeps track of a single transform that may be done ++ * to a set of packets. It can describe applying the transform or ++ * apply the reverse. (e.g. compression vs expansion). However, it ++ * only describes one at a time. To describe both, two structures would ++ * be used, but since the sides of the transform are performed ++ * on different machines typically it is usual to have only one side ++ * of each association. ++ * ++ */ ++ ++#ifndef _IPSEC_SA_H_ ++ ++#ifdef __KERNEL__ ++#include "ipsec_stats.h" ++#include "ipsec_life.h" ++#include "ipsec_eroute.h" ++#endif /* __KERNEL__ */ ++#include "ipsec_param.h" ++ ++ ++/* SAs are held in a table. ++ * Entries in this table are referenced by IPsecSAref_t values. ++ * IPsecSAref_t values are conceptually subscripts. Because ++ * we want to allocate the table piece-meal, the subscripting ++ * is implemented with two levels, a bit like paged virtual memory. ++ * This representation mechanism is known as an Iliffe Vector. ++ * ++ * The Main table (AKA the refTable) consists of 2^IPSEC_SA_REF_MAINTABLE_IDX_WIDTH ++ * pointers to subtables. ++ * Each subtable has 2^IPSEC_SA_REF_SUBTABLE_IDX_WIDTH entries, each of which ++ * is a pointer to an SA. ++ * ++ * An IPsecSAref_t contains either an exceptional value (signified by the ++ * high-order bit being on) or a reference to a table entry. A table entry ++ * reference has the subtable subscript in the low-order ++ * IPSEC_SA_REF_SUBTABLE_IDX_WIDTH bits and the Main table subscript ++ * in the next lowest IPSEC_SA_REF_MAINTABLE_IDX_WIDTH bits. ++ * ++ * The Maintable entry for an IPsecSAref_t x, a pointer to its subtable, is ++ * IPsecSAref2table(x). It is of type struct IPsecSArefSubTable *. ++ * ++ * The pointer to the SA for x is IPsecSAref2SA(x). It is of type ++ * struct ipsec_sa*. The macro definition clearly shows the two-level ++ * access needed to find the SA pointer. ++ * ++ * The Maintable is allocated when IPsec is initialized. ++ * Each subtable is allocated when needed, but the first is allocated ++ * when IPsec is initialized. ++ * ++ * IPsecSAref_t is designed to be smaller than an NFmark so that ++ * they can be stored in NFmarks and still leave a few bits for other ++ * purposes. The spare bits are in the low order of the NFmark ++ * but in the high order of the IPsecSAref_t, so conversion is required. ++ * We pick the upper bits of NFmark on the theory that they are less likely to ++ * interfere with more pedestrian uses of nfmark. ++ */ ++ ++ ++typedef unsigned short int IPsecRefTableUnusedCount; ++ ++#define IPSEC_SA_REF_TABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH) ++ ++#ifdef __KERNEL__ ++#if ((IPSEC_SA_REF_TABLE_IDX_WIDTH - (1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) < 0) ++#error "IPSEC_SA_REF_TABLE_IDX_WIDTH("IPSEC_SA_REF_TABLE_IDX_WIDTH") MUST be < 1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH("IPSEC_SA_REF_MAINTABLE_IDX_WIDTH")" ++#endif ++ ++#define IPSEC_SA_REF_SUBTABLE_IDX_WIDTH (IPSEC_SA_REF_TABLE_IDX_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH) ++ ++#define IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_MAINTABLE_IDX_WIDTH) ++#define IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) ++ ++#ifdef CONFIG_NETFILTER ++#define IPSEC_SA_REF_HOST_FIELD(x) ((struct sk_buff*)(x))->nfmark ++#define IPSEC_SA_REF_HOST_FIELD_TYPE typeof(IPSEC_SA_REF_HOST_FIELD(NULL)) ++#else /* CONFIG_NETFILTER */ ++/* just make it work for now, it doesn't matter, since there is no nfmark */ ++#define IPSEC_SA_REF_HOST_FIELD_TYPE unsigned long ++#endif /* CONFIG_NETFILTER */ ++#define IPSEC_SA_REF_HOST_FIELD_WIDTH (8 * sizeof(IPSEC_SA_REF_HOST_FIELD_TYPE)) ++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t)) ++ ++#define IPSEC_SA_REF_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) ++#define IPSEC_SA_REF_TABLE_MASK ((IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) ++#define IPSEC_SA_REF_ENTRY_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)) ++ ++#define IPsecSAref2table(x) (((x) & IPSEC_SA_REF_TABLE_MASK) >> IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) ++#define IPsecSAref2entry(x) ((x) & IPSEC_SA_REF_ENTRY_MASK) ++#define IPsecSArefBuild(x,y) (((x) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) + (y)) ++ ++#define IPsecSAref2SA(x) (ipsec_sadb.refTable[IPsecSAref2table(x)]->entry[IPsecSAref2entry(x)]) ++#define IPsecSA2SAref(x) ((x)->ips_ref) ++ ++#define EMT_INBOUND 0x01 /* SA direction, 1=inbound */ ++ ++/* 'struct ipsec_sa' should be 64bit aligned when allocated. */ ++struct ipsec_sa ++{ ++ IPsecSAref_t ips_ref; /* reference table entry number */ ++ atomic_t ips_refcount; /* reference count for this struct */ ++ struct ipsec_sa *ips_hnext; /* next in hash chain */ ++ struct ipsec_sa *ips_inext; /* pointer to next xform */ ++ struct ipsec_sa *ips_onext; /* pointer to prev xform */ ++ ++ struct ifnet *ips_rcvif; /* related rcv encap interface */ ++ ++ ip_said ips_said; /* SA ID */ ++ ++ __u32 ips_seq; /* seq num of msg that initiated this SA */ ++ __u32 ips_pid; /* PID of process that initiated this SA */ ++ __u8 ips_authalg; /* auth algorithm for this SA */ ++ __u8 ips_encalg; /* enc algorithm for this SA */ ++ ++ struct ipsec_stats ips_errs; ++ ++ __u8 ips_replaywin; /* replay window size */ ++ __u8 ips_state; /* state of SA */ ++ __u32 ips_replaywin_lastseq; /* last pkt sequence num */ ++ __u64 ips_replaywin_bitmap; /* bitmap of received pkts */ ++ __u32 ips_replaywin_maxdiff; /* max pkt sequence difference */ ++ ++ __u32 ips_flags; /* generic xform flags */ ++ ++ ++ struct ipsec_lifetimes ips_life; /* lifetime records */ ++ ++ /* selector information */ ++ __u8 ips_transport_protocol; /* protocol for this SA, if ports are involved */ ++ struct sockaddr*ips_addr_s; /* src sockaddr */ ++ struct sockaddr*ips_addr_d; /* dst sockaddr */ ++ struct sockaddr*ips_addr_p; /* proxy sockaddr */ ++ __u16 ips_addr_s_size; ++ __u16 ips_addr_d_size; ++ __u16 ips_addr_p_size; ++ ip_address ips_flow_s; ++ ip_address ips_flow_d; ++ ip_address ips_mask_s; ++ ip_address ips_mask_d; ++ ++ __u16 ips_key_bits_a; /* size of authkey in bits */ ++ __u16 ips_auth_bits; /* size of authenticator in bits */ ++ __u16 ips_key_bits_e; /* size of enckey in bits */ ++ __u16 ips_iv_bits; /* size of IV in bits */ ++ __u8 ips_iv_size; ++ __u16 ips_key_a_size; ++ __u16 ips_key_e_size; ++ ++ caddr_t ips_key_a; /* authentication key */ ++ caddr_t ips_key_e; /* encryption key */ ++ caddr_t ips_iv; /* Initialisation Vector */ ++ ++ struct ident ips_ident_s; /* identity src */ ++ struct ident ips_ident_d; /* identity dst */ ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++ __u16 ips_comp_adapt_tries; /* ipcomp self-adaption tries */ ++ __u16 ips_comp_adapt_skip; /* ipcomp self-adaption to-skip */ ++ __u64 ips_comp_ratio_cbytes; /* compressed bytes */ ++ __u64 ips_comp_ratio_dbytes; /* decompressed (or uncompressed) bytes */ ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ __u8 ips_natt_type; ++ __u8 ips_natt_reserved[3]; ++ __u16 ips_natt_sport; ++ __u16 ips_natt_dport; ++ ++ struct sockaddr *ips_natt_oa; ++ __u16 ips_natt_oa_size; ++ __u16 ips_natt_reserved2; ++#endif ++ ++#if 0 ++ __u32 ips_sens_dpd; ++ __u8 ips_sens_sens_level; ++ __u8 ips_sens_sens_len; ++ __u64* ips_sens_sens_bitmap; ++ __u8 ips_sens_integ_level; ++ __u8 ips_sens_integ_len; ++ __u64* ips_sens_integ_bitmap; ++#endif ++ struct ipsec_alg_enc *ips_alg_enc; ++ struct ipsec_alg_auth *ips_alg_auth; ++ IPsecSAref_t ips_ref_rel; ++}; ++ ++struct IPsecSArefSubTable ++{ ++ struct ipsec_sa* entry[IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES]; ++}; ++ ++struct ipsec_sadb { ++ struct IPsecSArefSubTable* refTable[IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES]; ++ IPsecSAref_t refFreeList[IPSEC_SA_REF_FREELIST_NUM_ENTRIES]; ++ int refFreeListHead; ++ int refFreeListTail; ++ IPsecSAref_t refFreeListCont; ++ IPsecSAref_t said_hash[SADB_HASHMOD]; ++ spinlock_t sadb_lock; ++}; ++ ++extern struct ipsec_sadb ipsec_sadb; ++ ++extern int ipsec_SAref_recycle(void); ++extern int ipsec_SArefSubTable_alloc(unsigned table); ++extern int ipsec_saref_freelist_init(void); ++extern int ipsec_sadb_init(void); ++extern struct ipsec_sa *ipsec_sa_alloc(int*error); /* pass in error var by pointer */ ++extern IPsecSAref_t ipsec_SAref_alloc(int*erorr); /* pass in error var by pointer */ ++extern int ipsec_sa_free(struct ipsec_sa* ips); ++extern int ipsec_sa_put(struct ipsec_sa *ips); ++extern int ipsec_sa_add(struct ipsec_sa *ips); ++extern int ipsec_sa_del(struct ipsec_sa *ips); ++extern int ipsec_sa_delchain(struct ipsec_sa *ips); ++extern int ipsec_sadb_cleanup(__u8 proto); ++extern int ipsec_sadb_free(void); ++extern int ipsec_sa_wipe(struct ipsec_sa *ips); ++#endif /* __KERNEL__ */ ++ ++enum ipsec_direction { ++ ipsec_incoming = 1, ++ ipsec_outgoing = 2 ++}; ++ ++#define _IPSEC_SA_H_ ++#endif /* _IPSEC_SA_H_ */ ++ ++/* ++ * $Log: ipsec_sa.h,v $ ++ * Revision 1.19 2004/04/05 19:55:06 mcr ++ * Moved from linux/include/freeswan/ipsec_sa.h,v ++ * ++ * Revision 1.18 2004/04/05 19:41:05 mcr ++ * merged alg-branch code. ++ * ++ * Revision 1.17.2.1 2003/12/22 15:25:52 jjo ++ * . Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.17 2003/12/10 01:20:06 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.16 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.15.4.1 2003/10/29 01:10:19 mcr ++ * elimited "struct sa_id" ++ * ++ * Revision 1.15 2003/05/11 00:53:09 mcr ++ * IPsecSAref_t and macros were moved to freeswan.h. ++ * ++ * Revision 1.14 2003/02/12 19:31:55 rgb ++ * Fixed bug in "file seen" machinery. ++ * Updated copyright year. ++ * ++ * Revision 1.13 2003/01/30 02:31:52 rgb ++ * ++ * Re-wrote comments describing SAref system for accuracy. ++ * Rename SAref table macro names for clarity. ++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. ++ * Transmit error code through to caller from callee for better diagnosis of problems. ++ * Enclose all macro arguments in parens to avoid any possible obscrure bugs. ++ * ++ * Revision 1.12 2002/10/07 18:31:19 rgb ++ * Change comment to reflect the flexible nature of the main and sub-table widths. ++ * Added a counter for the number of unused entries in each subtable. ++ * Further break up host field type macro to host field. ++ * Move field width sanity checks to ipsec_sa.c ++ * Define a mask for an entire saref. ++ * ++ * Revision 1.11 2002/09/20 15:40:33 rgb ++ * Re-write most of the SAref macros and types to eliminate any pointer references to Entrys. ++ * Fixed SAref/nfmark macros. ++ * Rework saref freeslist. ++ * Place all ipsec sadb globals into one struct. ++ * Restrict some bits to kernel context for use to klips utils. ++ * ++ * Revision 1.10 2002/09/20 05:00:34 rgb ++ * Update copyright date. ++ * ++ * Revision 1.9 2002/09/17 17:19:29 mcr ++ * make it compile even if there is no netfilter - we lost ++ * functionality, but it works, especially on 2.2. ++ * ++ * Revision 1.8 2002/07/28 22:59:53 mcr ++ * clarified/expanded one comment. ++ * ++ * Revision 1.7 2002/07/26 08:48:31 rgb ++ * Added SA ref table code. ++ * ++ * Revision 1.6 2002/05/31 17:27:48 rgb ++ * Comment fix. ++ * ++ * Revision 1.5 2002/05/27 18:55:03 rgb ++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. ++ * ++ * Revision 1.4 2002/05/23 07:13:36 rgb ++ * Convert "usecount" to "refcount" to remove ambiguity. ++ * ++ * Revision 1.3 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_sa.h,v ++ * ++ * Revision 1.2 2001/11/26 09:16:15 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:24:58 mcr ++ * struct tdb -> struct ipsec_sa. ++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c ++ * ipsec_xform.c removed. header file still contains useful things. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_sha1.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,79 @@ ++/* ++ * RCSID $Id: ipsec_sha1.h,v 1.8 2004/04/05 19:55:07 mcr Exp $ ++ */ ++ ++/* ++ * Here is the original comment from the distribution: ++ ++SHA-1 in C ++By Steve Reid ++100% Public Domain ++ ++ * Adapted for use by the IPSEC code by John Ioannidis ++ */ ++ ++ ++#ifndef _IPSEC_SHA1_H_ ++#define _IPSEC_SHA1_H_ ++ ++typedef struct ++{ ++ __u32 state[5]; ++ __u32 count[2]; ++ __u8 buffer[64]; ++} SHA1_CTX; ++ ++void SHA1Transform(__u32 state[5], __u8 buffer[64]); ++void SHA1Init(void *context); ++void SHA1Update(void *context, unsigned char *data, __u32 len); ++void SHA1Final(unsigned char digest[20], void *context); ++ ++ ++#endif /* _IPSEC_SHA1_H_ */ ++ ++/* ++ * $Log: ipsec_sha1.h,v $ ++ * Revision 1.8 2004/04/05 19:55:07 mcr ++ * Moved from linux/include/freeswan/ipsec_sha1.h,v ++ * ++ * Revision 1.7 2002/09/10 01:45:09 mcr ++ * changed type of MD5_CTX and SHA1_CTX to void * so that ++ * the function prototypes would match, and could be placed ++ * into a pointer to a function. ++ * ++ * Revision 1.6 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_sha1.h,v ++ * ++ * Revision 1.5 1999/12/13 13:59:13 rgb ++ * Quick fix to argument size to Update bugs. ++ * ++ * Revision 1.4 1999/12/07 18:16:23 rgb ++ * Fixed comments at end of #endif lines. ++ * ++ * Revision 1.3 1999/04/06 04:54:27 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.2 1998/11/30 13:22:54 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.1 1998/06/18 21:27:50 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.2 1998/04/23 20:54:05 rgb ++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when ++ * verified. ++ * ++ * Revision 1.1 1998/04/09 03:04:21 henry ++ * sources moved up from linux/net/ipsec ++ * these two include files modified not to include others except in kernel ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * New transform ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_stats.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,75 @@ ++/* ++ * @(#) definition of ipsec_stats structure ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_stats.h,v 1.6 2004/04/05 19:55:07 mcr Exp $ ++ * ++ */ ++ ++/* ++ * This file describes the errors/statistics that FreeSWAN collects. ++ */ ++ ++#ifndef _IPSEC_STATS_H_ ++ ++struct ipsec_stats { ++ __u32 ips_alg_errs; /* number of algorithm errors */ ++ __u32 ips_auth_errs; /* # of authentication errors */ ++ __u32 ips_encsize_errs; /* # of encryption size errors*/ ++ __u32 ips_encpad_errs; /* # of encryption pad errors*/ ++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */ ++}; ++ ++extern int ipsec_snprintf(char * buf, ssize_t size, const char *fmt, ...); ++ ++#define _IPSEC_STATS_H_ ++#endif /* _IPSEC_STATS_H_ */ ++ ++/* ++ * $Log: ipsec_stats.h,v $ ++ * Revision 1.6 2004/04/05 19:55:07 mcr ++ * Moved from linux/include/freeswan/ipsec_stats.h,v ++ * ++ * Revision 1.5 2004/04/05 19:41:05 mcr ++ * merged alg-branch code. ++ * ++ * Revision 1.4 2004/03/28 20:27:19 paul ++ * Included tested and confirmed fixes mcr made and dhr verified for ++ * snprint statements. Changed one other snprintf to use ipsec_snprintf ++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with ++ * dhr. (thanks dhr!) ++ * ++ * Revision 1.4 2004/03/24 01:58:31 mcr ++ * sprintf->snprintf for formatting into proc buffer. ++ * ++ * Revision 1.3.34.1 2004/04/05 04:30:46 mcr ++ * patches for alg-branch to compile/work with 2.x openswan ++ * ++ * Revision 1.3 2002/04/24 07:36:47 mcr ++ * Moved from ./klips/net/ipsec/ipsec_stats.h,v ++ * ++ * Revision 1.2 2001/11/26 09:16:16 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:27:00 mcr ++ * statistics moved to seperate structure. ++ * ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_tunnel.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,265 @@ ++/* ++ * IPSEC tunneling code ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_tunnel.h,v 1.29 2004/04/05 19:55:07 mcr Exp $ ++ */ ++ ++ ++#ifdef NET_21 ++# define DEV_QUEUE_XMIT(skb, device, pri) {\ ++ skb->dev = device; \ ++ neigh_compat_output(skb); \ ++ /* skb->dst->output(skb); */ \ ++ } ++# define ICMP_SEND(skb_in, type, code, info, dev) \ ++ icmp_send(skb_in, type, code, htonl(info)) ++# define IP_SEND(skb, dev) \ ++ ip_send(skb); ++#else /* NET_21 */ ++# define DEV_QUEUE_XMIT(skb, device, pri) {\ ++ dev_queue_xmit(skb, device, pri); \ ++ } ++# define ICMP_SEND(skb_in, type, code, info, dev) \ ++ icmp_send(skb_in, type, code, info, dev) ++# define IP_SEND(skb, dev) \ ++ if(ntohs(iph->tot_len) > physmtu) { \ ++ ip_fragment(NULL, skb, dev, 0); \ ++ ipsec_kfree_skb(skb); \ ++ } else { \ ++ dev_queue_xmit(skb, dev, SOPRI_NORMAL); \ ++ } ++#endif /* NET_21 */ ++ ++ ++/* ++ * Heavily based on drivers/net/new_tunnel.c. Lots ++ * of ideas also taken from the 2.1.x version of drivers/net/shaper.c ++ */ ++ ++struct ipsectunnelconf ++{ ++ __u32 cf_cmd; ++ union ++ { ++ char cfu_name[12]; ++ } cf_u; ++#define cf_name cf_u.cfu_name ++}; ++ ++#define IPSEC_SET_DEV (SIOCDEVPRIVATE) ++#define IPSEC_DEL_DEV (SIOCDEVPRIVATE + 1) ++#define IPSEC_CLR_DEV (SIOCDEVPRIVATE + 2) ++ ++#ifdef __KERNEL__ ++#include ++#ifndef KERNEL_VERSION ++# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) ++#endif ++struct ipsecpriv ++{ ++ struct sk_buff_head sendq; ++ struct device *dev; ++ struct wait_queue *wait_queue; ++ char locked; ++ int (*hard_start_xmit) (struct sk_buff *skb, ++ struct device *dev); ++ int (*hard_header) (struct sk_buff *skb, ++ struct device *dev, ++ unsigned short type, ++ void *daddr, ++ void *saddr, ++ unsigned len); ++#ifdef NET_21 ++ int (*rebuild_header)(struct sk_buff *skb); ++#else /* NET_21 */ ++ int (*rebuild_header)(void *buff, struct device *dev, ++ unsigned long raddr, struct sk_buff *skb); ++#endif /* NET_21 */ ++ int (*set_mac_address)(struct device *dev, void *addr); ++#ifndef NET_21 ++ void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, ++ unsigned short htype, __u32 daddr); ++#endif /* !NET_21 */ ++ void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr); ++ struct net_device_stats *(*get_stats)(struct device *dev); ++ struct net_device_stats mystats; ++ int mtu; /* What is the desired MTU? */ ++}; ++ ++extern char ipsec_tunnel_c_version[]; ++ ++extern struct device *ipsecdevices[IPSEC_NUM_IF]; ++ ++int ipsec_tunnel_init_devices(void); ++ ++/* void */ int ipsec_tunnel_cleanup_devices(void); ++ ++extern /* void */ int ipsec_init(void); ++ ++extern int ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev); ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_tunnel; ++extern int sysctl_ipsec_debug_verbose; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* __KERNEL__ */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++#define DB_TN_INIT 0x0001 ++#define DB_TN_PROCFS 0x0002 ++#define DB_TN_XMIT 0x0010 ++#define DB_TN_OHDR 0x0020 ++#define DB_TN_CROUT 0x0040 ++#define DB_TN_OXFS 0x0080 ++#define DB_TN_REVEC 0x0100 ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++/* ++ * $Log: ipsec_tunnel.h,v $ ++ * Revision 1.29 2004/04/05 19:55:07 mcr ++ * Moved from linux/include/freeswan/ipsec_tunnel.h,v ++ * ++ * Revision 1.28 2003/06/24 20:22:32 mcr ++ * added new global: ipsecdevices[] so that we can keep track of ++ * the ipsecX devices. They will be referenced with dev_hold(), ++ * so 2.2 may need this as well. ++ * ++ * Revision 1.27 2003/04/03 17:38:09 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * ++ * Revision 1.26 2003/02/12 19:32:20 rgb ++ * Updated copyright year. ++ * ++ * Revision 1.25 2002/05/27 18:56:07 rgb ++ * Convert to dynamic ipsec device allocation. ++ * ++ * Revision 1.24 2002/04/24 07:36:48 mcr ++ * Moved from ./klips/net/ipsec/ipsec_tunnel.h,v ++ * ++ * Revision 1.23 2001/11/06 19:50:44 rgb ++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for ++ * use also by pfkey_v2_parser.c ++ * ++ * Revision 1.22 2001/09/15 16:24:05 rgb ++ * Re-inject first and last HOLD packet when an eroute REPLACE is done. ++ * ++ * Revision 1.21 2001/06/14 19:35:10 rgb ++ * Update copyright date. ++ * ++ * Revision 1.20 2000/09/15 11:37:02 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.19 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.18 2000/07/28 13:50:54 rgb ++ * Changed enet_statistics to net_device_stats and added back compatibility ++ * for pre-2.1.19. ++ * ++ * Revision 1.17 1999/11/19 01:12:15 rgb ++ * Purge unneeded proc_info prototypes, now that static linking uses ++ * dynamic proc_info registration. ++ * ++ * Revision 1.16 1999/11/18 18:51:00 rgb ++ * Changed all device registrations for static linking to ++ * dynamic to reduce the number and size of patches. ++ * ++ * Revision 1.15 1999/11/18 04:14:21 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * Added CONFIG_PROC_FS compiler directives in case it is shut off. ++ * Added Marc Boucher's 2.3.25 proc patches. ++ * ++ * Revision 1.14 1999/05/25 02:50:10 rgb ++ * Fix kernel version macros for 2.0.x static linking. ++ * ++ * Revision 1.13 1999/05/25 02:41:06 rgb ++ * Add ipsec_klipsdebug support for static linking. ++ * ++ * Revision 1.12 1999/05/05 22:02:32 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.11 1999/04/29 15:19:50 rgb ++ * Add return values to init and cleanup functions. ++ * ++ * Revision 1.10 1999/04/16 16:02:39 rgb ++ * Bump up macro to 4 ipsec I/Fs. ++ * ++ * Revision 1.9 1999/04/15 15:37:25 rgb ++ * Forward check changes from POST1_00 branch. ++ * ++ * Revision 1.5.2.1 1999/04/02 04:26:14 rgb ++ * Backcheck from HEAD, pre1.0. ++ * ++ * Revision 1.8 1999/04/11 00:29:01 henry ++ * GPL boilerplate ++ * ++ * Revision 1.7 1999/04/06 04:54:28 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.6 1999/03/31 05:44:48 rgb ++ * Keep PMTU reduction private. ++ * ++ * Revision 1.5 1999/02/10 22:31:20 rgb ++ * Change rebuild_header member to reflect generality of link layer. ++ * ++ * Revision 1.4 1998/12/01 13:22:04 rgb ++ * Added support for debug printing of version info. ++ * ++ * Revision 1.3 1998/07/29 20:42:46 rgb ++ * Add a macro for clearing all tunnel devices. ++ * Rearrange structures and declarations for sharing with userspace. ++ * ++ * Revision 1.2 1998/06/25 20:01:45 rgb ++ * Make prototypes available for ipsec_init and ipsec proc_dir_entries ++ * for static linking. ++ * ++ * Revision 1.1 1998/06/18 21:27:50 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.3 1998/05/18 21:51:50 rgb ++ * Added macros for num of I/F's and a procfs debug switch. ++ * ++ * Revision 1.2 1998/04/21 21:29:09 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.1 1998/04/09 03:06:13 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.5 1997/06/03 04:24:48 ji ++ * Added transport mode. ++ * Changed the way routing is done. ++ * Lots of bug fixes. ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_xform.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,254 @@ ++/* ++ * Definitions relevant to IPSEC transformations ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * COpyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_xform.h,v 1.40 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#ifndef _IPSEC_XFORM_H_ ++ ++#include ++ ++#define XF_NONE 0 /* No transform set */ ++#define XF_IP4 1 /* IPv4 inside IPv4 */ ++#define XF_AHMD5 2 /* AH MD5 */ ++#define XF_AHSHA 3 /* AH SHA */ ++#define XF_ESP3DES 5 /* ESP DES3-CBC */ ++#define XF_AHHMACMD5 6 /* AH-HMAC-MD5 with opt replay prot */ ++#define XF_AHHMACSHA1 7 /* AH-HMAC-SHA1 with opt replay prot */ ++#define XF_ESP3DESMD5 9 /* triple DES, HMAC-MD-5, 128-bits of authentication */ ++#define XF_ESP3DESMD596 10 /* triple DES, HMAC-MD-5, 96-bits of authentication */ ++#define XF_ESPNULLMD596 12 /* NULL, HMAC-MD-5 with 96-bits of authentication */ ++#define XF_ESPNULLSHA196 13 /* NULL, HMAC-SHA-1 with 96-bits of authentication */ ++#define XF_ESP3DESSHA196 14 /* triple DES, HMAC-SHA-1, 96-bits of authentication */ ++#define XF_IP6 15 /* IPv6 inside IPv6 */ ++#define XF_COMPDEFLATE 16 /* IPCOMP deflate */ ++ ++#define XF_CLR 126 /* Clear SA table */ ++#define XF_DEL 127 /* Delete SA */ ++ ++/* IPsec AH transform values ++ * RFC 2407 ++ * draft-ietf-ipsec-doi-tc-mib-02.txt ++ */ ++ ++#define AH_NONE 0 ++#define AH_MD5 2 ++#define AH_SHA 3 ++/* draft-ietf-ipsec-ciph-aes-cbc-03.txt */ ++#define AH_SHA2_256 5 ++#define AH_SHA2_384 6 ++#define AH_SHA2_512 7 ++#define AH_RIPEMD 8 ++#define AH_MAX 15 ++ ++/* IPsec ESP transform values */ ++ ++#define ESP_NONE 0 ++#define ESP_DES 2 ++#define ESP_3DES 3 ++#define ESP_RC5 4 ++#define ESP_IDEA 5 ++#define ESP_CAST 6 ++#define ESP_BLOWFISH 7 ++#define ESP_3IDEA 8 ++#define ESP_RC4 10 ++#define ESP_NULL 11 ++#define ESP_AES 12 ++ ++/* as draft-ietf-ipsec-ciph-aes-cbc-02.txt */ ++#define ESP_MARS 249 ++#define ESP_RC6 250 ++#define ESP_SERPENT 252 ++#define ESP_TWOFISH 253 ++ ++/* IPCOMP transform values */ ++ ++#define IPCOMP_NONE 0 ++#define IPCOMP_OUI 1 ++#define IPCOMP_DEFLAT 2 ++#define IPCOMP_LZS 3 ++#define IPCOMP_V42BIS 4 ++ ++#define XFT_AUTH 0x0001 ++#define XFT_CONF 0x0100 ++ ++/* available if CONFIG_IPSEC_DEBUG is defined */ ++#define DB_XF_INIT 0x0001 ++ ++#define PROTO2TXT(x) \ ++ (x) == IPPROTO_AH ? "AH" : \ ++ (x) == IPPROTO_ESP ? "ESP" : \ ++ (x) == IPPROTO_IPIP ? "IPIP" : \ ++ (x) == IPPROTO_COMP ? "COMP" : \ ++ "UNKNOWN_proto" ++static inline const char *enc_name_id (unsigned id) { ++ static char buf[16]; ++ snprintf(buf, sizeof(buf), "_ID%d", id); ++ return buf; ++} ++static inline const char *auth_name_id (unsigned id) { ++ static char buf[16]; ++ snprintf(buf, sizeof(buf), "_ID%d", id); ++ return buf; ++} ++#define IPS_XFORM_NAME(x) \ ++ PROTO2TXT((x)->ips_said.proto), \ ++ (x)->ips_said.proto == IPPROTO_COMP ? \ ++ ((x)->ips_encalg == SADB_X_CALG_DEFLATE ? \ ++ "_DEFLATE" : "_UNKNOWN_comp") : \ ++ (x)->ips_encalg == ESP_NONE ? "" : \ ++ (x)->ips_encalg == ESP_3DES ? "_3DES" : \ ++ (x)->ips_encalg == ESP_AES ? "_AES" : \ ++ (x)->ips_encalg == ESP_SERPENT ? "_SERPENT" : \ ++ (x)->ips_encalg == ESP_TWOFISH ? "_TWOFISH" : \ ++ enc_name_id(x->ips_encalg)/* "_UNKNOWN_encr" */, \ ++ (x)->ips_authalg == AH_NONE ? "" : \ ++ (x)->ips_authalg == AH_MD5 ? "_HMAC_MD5" : \ ++ (x)->ips_authalg == AH_SHA ? "_HMAC_SHA1" : \ ++ (x)->ips_authalg == AH_SHA2_256 ? "_HMAC_SHA2_256" : \ ++ (x)->ips_authalg == AH_SHA2_384 ? "_HMAC_SHA2_384" : \ ++ (x)->ips_authalg == AH_SHA2_512 ? "_HMAC_SHA2_512" : \ ++ auth_name_id(x->ips_authalg) /* "_UNKNOWN_auth" */ \ ++ ++#ifdef __KERNEL__ ++struct ipsec_rcv_state; ++struct ipsec_xmit_state; ++ ++struct xform_functions { ++ enum ipsec_rcv_value (*rcv_checks)(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb); ++ enum ipsec_rcv_value (*rcv_decrypt)(struct ipsec_rcv_state *irs); ++ ++ enum ipsec_rcv_value (*rcv_setup_auth)(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb, ++ __u32 *replay, ++ unsigned char **authenticator); ++ enum ipsec_rcv_value (*rcv_calc_auth)(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb); ++ ++ enum ipsec_xmit_value (*xmit_setup)(struct ipsec_xmit_state *ixs); ++ enum ipsec_xmit_value (*xmit_encrypt)(struct ipsec_xmit_state *ixs); ++ ++ enum ipsec_xmit_value (*xmit_setup_auth)(struct ipsec_xmit_state *ixs, ++ struct sk_buff *skb, ++ __u32 *replay, ++ unsigned char **authenticator); ++ enum ipsec_xmit_value (*xmit_calc_auth)(struct ipsec_xmit_state *ixs, ++ struct sk_buff *skb); ++ int xmit_headroom; ++ int xmit_needtailroom; ++}; ++ ++#endif /* __KERNEL__ */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern void ipsec_dmp(char *s, caddr_t bb, int len); ++#else /* CONFIG_IPSEC_DEBUG */ ++#define ipsec_dmp(_x, _y, _z) ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ++#define _IPSEC_XFORM_H_ ++#endif /* _IPSEC_XFORM_H_ */ ++ ++/* ++ * $Log: ipsec_xform.h,v $ ++ * Revision 1.40 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.39 2004/04/05 19:55:07 mcr ++ * Moved from linux/include/freeswan/ipsec_xform.h,v ++ * ++ * Revision 1.38 2004/04/05 19:41:05 mcr ++ * merged alg-branch code. ++ * ++ * Revision 1.37 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.36.34.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.36 2002/04/24 07:36:48 mcr ++ * Moved from ./klips/net/ipsec/ipsec_xform.h,v ++ * ++ * Revision 1.35 2001/11/26 09:23:51 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.33.2.1 2001/09/25 02:24:58 mcr ++ * struct tdb -> struct ipsec_sa. ++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c ++ * ipsec_xform.c removed. header file still contains useful things. ++ * ++ * Revision 1.34 2001/11/06 19:47:17 rgb ++ * Changed lifetime_packets to uint32 from uint64. ++ * ++ * Revision 1.33 2001/09/08 21:13:34 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.32 2001/07/06 07:40:01 rgb ++ * Reformatted for readability. ++ * Added inbound policy checking fields for use with IPIP SAs. ++ * ++ * Revision 1.31 2001/06/14 19:35:11 rgb ++ * Update copyright date. ++ * ++ * Revision 1.30 2001/05/30 08:14:03 rgb ++ * Removed vestiges of esp-null transforms. ++ * ++ * Revision 1.29 2001/01/30 23:42:47 rgb ++ * Allow pfkey msgs from pid other than user context required for ACQUIRE ++ * and subsequent ADD or UDATE. ++ * ++ * Revision 1.28 2000/11/06 04:30:40 rgb ++ * Add Svenning's adaptive content compression. ++ * ++ * Revision 1.27 2000/09/19 00:38:25 rgb ++ * Fixed algorithm name bugs introduced for ipcomp. ++ * ++ * Revision 1.26 2000/09/17 21:36:48 rgb ++ * Added proto2txt macro. ++ * ++ * Revision 1.25 2000/09/17 18:56:47 rgb ++ * Added IPCOMP support. ++ * ++ * Revision 1.24 2000/09/12 19:34:12 rgb ++ * Defined XF_IP6 from Gerhard for ipv6 tunnel support. ++ * ++ * Revision 1.23 2000/09/12 03:23:14 rgb ++ * Cleaned out now unused tdb_xform and tdb_xdata members of struct tdb. ++ * ++ * Revision 1.22 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.21 2000/09/01 18:32:43 rgb ++ * Added (disabled) sensitivity members to tdb struct. ++ * ++ * Revision 1.20 2000/08/30 05:31:01 rgb ++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst. ++ * Kill remainder of tdb_xform, tdb_xdata, xformsw. ++ * ++ * Revision 1.19 2000/08/01 14:51:52 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.18 2000/01/21 06:17:45 rgb ++ * Tidied up spacing. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/ipsec_xmit.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,176 @@ ++/* ++ * IPSEC tunneling code ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_xmit.h,v 1.9 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#include "openswan/ipsec_sa.h" ++ ++enum ipsec_xmit_value ++{ ++ IPSEC_XMIT_STOLEN=2, ++ IPSEC_XMIT_PASS=1, ++ IPSEC_XMIT_OK=0, ++ IPSEC_XMIT_ERRMEMALLOC=-1, ++ IPSEC_XMIT_ESP_BADALG=-2, ++ IPSEC_XMIT_BADPROTO=-3, ++ IPSEC_XMIT_ESP_PUSHPULLERR=-4, ++ IPSEC_XMIT_BADLEN=-5, ++ IPSEC_XMIT_AH_BADALG=-6, ++ IPSEC_XMIT_SAIDNOTFOUND=-7, ++ IPSEC_XMIT_SAIDNOTLIVE=-8, ++ IPSEC_XMIT_REPLAYROLLED=-9, ++ IPSEC_XMIT_LIFETIMEFAILED=-10, ++ IPSEC_XMIT_CANNOTFRAG=-11, ++ IPSEC_XMIT_MSSERR=-12, ++ IPSEC_XMIT_ERRSKBALLOC=-13, ++ IPSEC_XMIT_ENCAPFAIL=-14, ++ IPSEC_XMIT_NODEV=-15, ++ IPSEC_XMIT_NOPRIVDEV=-16, ++ IPSEC_XMIT_NOPHYSDEV=-17, ++ IPSEC_XMIT_NOSKB=-18, ++ IPSEC_XMIT_NOIPV6=-19, ++ IPSEC_XMIT_NOIPOPTIONS=-20, ++ IPSEC_XMIT_TTLEXPIRED=-21, ++ IPSEC_XMIT_BADHHLEN=-22, ++ IPSEC_XMIT_PUSHPULLERR=-23, ++ IPSEC_XMIT_ROUTEERR=-24, ++ IPSEC_XMIT_RECURSDETECT=-25, ++ IPSEC_XMIT_IPSENDFAILURE=-26, ++ IPSEC_XMIT_ESPUDP=-27, ++ IPSEC_XMIT_ESPUDP_BADTYPE=-28, ++}; ++ ++struct ipsec_xmit_state ++{ ++ struct sk_buff *skb; /* working skb pointer */ ++ struct device *dev; /* working dev pointer */ ++ struct ipsecpriv *prv; /* Our device' private space */ ++ struct sk_buff *oskb; /* Original skb pointer */ ++ struct net_device_stats *stats; /* This device's statistics */ ++ struct iphdr *iph; /* Our new IP header */ ++ __u32 newdst; /* The other SG's IP address */ ++ __u32 orgdst; /* Original IP destination address */ ++ __u32 orgedst; /* 1st SG's IP address */ ++ __u32 newsrc; /* The new source SG's IP address */ ++ __u32 orgsrc; /* Original IP source address */ ++ __u32 innersrc; /* Innermost IP source address */ ++ int iphlen; /* IP header length */ ++ int pyldsz; /* upper protocol payload size */ ++ int headroom; ++ int tailroom; ++ int authlen; ++ int max_headroom; /* The extra header space needed */ ++ int max_tailroom; /* The extra stuffing needed */ ++ int ll_headroom; /* The extra link layer hard_header space needed */ ++ int tot_headroom; /* The total header space needed */ ++ int tot_tailroom; /* The totalstuffing needed */ ++ __u8 *saved_header; /* saved copy of the hard header */ ++ unsigned short sport, dport; ++ ++ struct sockaddr_encap matcher; /* eroute search key */ ++ struct eroute *eroute; ++ struct ipsec_sa *ipsp, *ipsq; /* ipsec_sa pointers */ ++ char sa_txt[SATOT_BUF]; ++ size_t sa_len; ++ int hard_header_stripped; /* has the hard header been removed yet? */ ++ int hard_header_len; ++ struct device *physdev; ++/* struct device *virtdev; */ ++ short physmtu; ++ short mtudiff; ++#ifdef NET_21 ++ struct rtable *route; ++#endif /* NET_21 */ ++ ip_said outgoing_said; ++#ifdef NET_21 ++ int pass; ++#endif /* NET_21 */ ++ int error; ++ uint32_t eroute_pid; ++ struct ipsec_sa ips; ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ uint8_t natt_type; ++ uint8_t natt_head; ++ uint16_t natt_sport; ++ uint16_t natt_dport; ++#endif ++}; ++ ++enum ipsec_xmit_value ++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs); ++ ++enum ipsec_xmit_value ++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs); ++ ++enum ipsec_xmit_value ++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs); ++ ++extern void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er); ++ ++ ++extern int ipsec_xmit_trap_count; ++extern int ipsec_xmit_trap_sendcount; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_tunnel; ++extern int sysctl_ipsec_debug_verbose; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#define debug_xmit debug_tunnel ++ ++#define ipsec_xmit_dmp(_x,_y, _z) if (debug_xmit && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z) ++ ++extern int sysctl_ipsec_icmp; ++extern int sysctl_ipsec_tos; ++ ++ ++/* ++ * $Log: ipsec_xmit.h,v $ ++ * Revision 1.9 2004/04/06 02:49:08 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.8 2004/04/05 19:55:07 mcr ++ * Moved from linux/include/freeswan/ipsec_xmit.h,v ++ * ++ * Revision 1.7 2004/02/03 03:11:40 mcr ++ * new xmit type if the UDP encapsulation is wrong. ++ * ++ * Revision 1.6 2003/12/13 19:10:16 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.5 2003/12/10 01:20:06 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.4 2003/12/06 16:37:04 mcr ++ * 1.4.7a X.509 patch applied. ++ * ++ * Revision 1.3 2003/10/31 02:27:05 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.2.4.2 2003/10/29 01:10:19 mcr ++ * elimited "struct sa_id" ++ * ++ * Revision 1.2.4.1 2003/09/21 13:59:38 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.2 2003/06/20 01:42:13 mcr ++ * added counters to measure how many ACQUIREs we send to pluto, ++ * and how many are successfully sent. ++ * ++ * Revision 1.1 2003/02/12 19:31:03 rgb ++ * Refactored from ipsec_tunnel.c ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/passert.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,64 @@ ++/* ++ * sanitize a string into a printable format. ++ * ++ * Copyright (C) 1998-2002 D. Hugh Redelmeier. ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: passert.h,v 1.4 2004/04/06 02:49:08 mcr Exp $ ++ */ ++ ++#include "openswan.h" ++ ++/* our versions of assert: log result */ ++ ++#ifdef DEBUG ++ ++extern void passert_fail(const char *pred_str ++ , const char *file_str, unsigned long line_no) NEVER_RETURNS; ++ ++extern void pexpect_log(const char *pred_str ++ , const char *file_str, unsigned long line_no); ++ ++# define impossible() passert_fail("impossible", __FILE__, __LINE__) ++ ++extern void switch_fail(int n ++ , const char *file_str, unsigned long line_no) NEVER_RETURNS; ++ ++# define bad_case(n) switch_fail((int) n, __FILE__, __LINE__) ++ ++# define passert(pred) { \ ++ if (!(pred)) \ ++ passert_fail(#pred, __FILE__, __LINE__); \ ++ } ++ ++# define pexpect(pred) { \ ++ if (!(pred)) \ ++ pexpect_log(#pred, __FILE__, __LINE__); \ ++ } ++ ++/* assert that an err_t is NULL; evaluate exactly once */ ++# define happy(x) { \ ++ err_t ugh = x; \ ++ if (ugh != NULL) \ ++ passert_fail(ugh, __FILE__, __LINE__); \ ++ } ++ ++#else /*!DEBUG*/ ++ ++# define impossible() abort() ++# define bad_case(n) abort() ++# define passert(pred) { } /* do nothing */ ++# define happy(x) { (void) x; } /* evaluate non-judgementally */ ++ ++#endif /*!DEBUG*/ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/pfkey_debug.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,54 @@ ++/* ++ * sanitize a string into a printable format. ++ * ++ * Copyright (C) 1998-2002 D. Hugh Redelmeier. ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: pfkey_debug.h,v 1.3 2004/04/05 19:55:07 mcr Exp $ ++ */ ++ ++#ifndef _FREESWAN_PFKEY_DEBUG_H ++#define _FREESWAN_PFKEY_DEBUG_H ++ ++#ifdef __KERNEL__ ++ ++/* note, kernel version ignores pfkey levels */ ++# define DEBUGGING(level,args...) \ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:" args) ++ ++# define ERROR(args...) printk(KERN_ERR "klips:" args) ++ ++#else ++ ++extern unsigned int pfkey_lib_debug; ++ ++extern void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1); ++extern void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1); ++ ++#define DEBUGGING(level,args...) if(pfkey_lib_debug & level) { \ ++ if(pfkey_debug_func != NULL) { \ ++ (*pfkey_debug_func)("pfkey_lib_debug:" args); \ ++ } else { \ ++ printf("pfkey_lib_debug:" args); \ ++ } } ++ ++#define ERROR(args...) if(pfkey_error_func != NULL) { \ ++ (*pfkey_error_func)("pfkey_lib_debug:" args); \ ++ } ++ ++# define MALLOC(size) malloc(size) ++# define FREE(obj) free(obj) ++ ++#endif ++ ++#endif +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/openswan/radij.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,280 @@ ++/* ++ * RCSID $Id: radij.h,v 1.13 2004/04/05 19:55:08 mcr Exp $ ++ */ ++ ++/* ++ * This file is defived from ${SRC}/sys/net/radix.h of BSD 4.4lite ++ * ++ * Variable and procedure names have been modified so that they don't ++ * conflict with the original BSD code, as a small number of modifications ++ * have been introduced and we may want to reuse this code in BSD. ++ * ++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek ++ * chi or a German ch sound (as `doch', not as in `milch'), or even a ++ * spanish j as in Juan. It is not as far back in the throat like ++ * the corresponding Hebrew sound, nor is it a soft breath like the English h. ++ * It has nothing to do with the Dutch ij sound. ++ * ++ * Here is the appropriate copyright notice: ++ */ ++ ++/* ++ * Copyright (c) 1988, 1989, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * This product includes software developed by the University of ++ * California, Berkeley and its contributors. ++ * 4. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)radix.h 8.1 (Berkeley) 6/10/93 ++ */ ++ ++#ifndef _RADIJ_H_ ++#define _RADIJ_H_ ++ ++/* ++#define RJ_DEBUG ++*/ ++ ++#ifdef __KERNEL__ ++ ++#ifndef __P ++#ifdef __STDC__ ++#define __P(x) x ++#else ++#define __P(x) () ++#endif ++#endif ++ ++/* ++ * Radix search tree node layout. ++ */ ++ ++struct radij_node ++{ ++ struct radij_mask *rj_mklist; /* list of masks contained in subtree */ ++ struct radij_node *rj_p; /* parent */ ++ short rj_b; /* bit offset; -1-index(netmask) */ ++ char rj_bmask; /* node: mask for bit test*/ ++ u_char rj_flags; /* enumerated next */ ++#define RJF_NORMAL 1 /* leaf contains normal route */ ++#define RJF_ROOT 2 /* leaf is root leaf for tree */ ++#define RJF_ACTIVE 4 /* This node is alive (for rtfree) */ ++ union { ++ struct { /* leaf only data: */ ++ caddr_t rj_Key; /* object of search */ ++ caddr_t rj_Mask; /* netmask, if present */ ++ struct radij_node *rj_Dupedkey; ++ } rj_leaf; ++ struct { /* node only data: */ ++ int rj_Off; /* where to start compare */ ++ struct radij_node *rj_L;/* progeny */ ++ struct radij_node *rj_R;/* progeny */ ++ }rj_node; ++ } rj_u; ++#ifdef RJ_DEBUG ++ int rj_info; ++ struct radij_node *rj_twin; ++ struct radij_node *rj_ybro; ++#endif ++}; ++ ++#define rj_dupedkey rj_u.rj_leaf.rj_Dupedkey ++#define rj_key rj_u.rj_leaf.rj_Key ++#define rj_mask rj_u.rj_leaf.rj_Mask ++#define rj_off rj_u.rj_node.rj_Off ++#define rj_l rj_u.rj_node.rj_L ++#define rj_r rj_u.rj_node.rj_R ++ ++/* ++ * Annotations to tree concerning potential routes applying to subtrees. ++ */ ++ ++extern struct radij_mask { ++ short rm_b; /* bit offset; -1-index(netmask) */ ++ char rm_unused; /* cf. rj_bmask */ ++ u_char rm_flags; /* cf. rj_flags */ ++ struct radij_mask *rm_mklist; /* more masks to try */ ++ caddr_t rm_mask; /* the mask */ ++ int rm_refs; /* # of references to this struct */ ++} *rj_mkfreelist; ++ ++#define MKGet(m) {\ ++ if (rj_mkfreelist) {\ ++ m = rj_mkfreelist; \ ++ rj_mkfreelist = (m)->rm_mklist; \ ++ } else \ ++ R_Malloc(m, struct radij_mask *, sizeof (*(m))); }\ ++ ++#define MKFree(m) { (m)->rm_mklist = rj_mkfreelist; rj_mkfreelist = (m);} ++ ++struct radij_node_head { ++ struct radij_node *rnh_treetop; ++ int rnh_addrsize; /* permit, but not require fixed keys */ ++ int rnh_pktsize; /* permit, but not require fixed keys */ ++#if 0 ++ struct radij_node *(*rnh_addaddr) /* add based on sockaddr */ ++ __P((void *v, void *mask, ++ struct radij_node_head *head, struct radij_node nodes[])); ++#endif ++ int (*rnh_addaddr) /* add based on sockaddr */ ++ __P((void *v, void *mask, ++ struct radij_node_head *head, struct radij_node nodes[])); ++ struct radij_node *(*rnh_addpkt) /* add based on packet hdr */ ++ __P((void *v, void *mask, ++ struct radij_node_head *head, struct radij_node nodes[])); ++#if 0 ++ struct radij_node *(*rnh_deladdr) /* remove based on sockaddr */ ++ __P((void *v, void *mask, struct radij_node_head *head)); ++#endif ++ int (*rnh_deladdr) /* remove based on sockaddr */ ++ __P((void *v, void *mask, struct radij_node_head *head, struct radij_node **node)); ++ struct radij_node *(*rnh_delpkt) /* remove based on packet hdr */ ++ __P((void *v, void *mask, struct radij_node_head *head)); ++ struct radij_node *(*rnh_matchaddr) /* locate based on sockaddr */ ++ __P((void *v, struct radij_node_head *head)); ++ struct radij_node *(*rnh_matchpkt) /* locate based on packet hdr */ ++ __P((void *v, struct radij_node_head *head)); ++ int (*rnh_walktree) /* traverse tree */ ++ __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w)); ++ struct radij_node rnh_nodes[3]; /* empty tree for common case */ ++}; ++ ++ ++#define Bcmp(a, b, n) memcmp(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n)) ++#define Bcopy(a, b, n) memmove(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n)) ++#define Bzero(p, n) memset((caddr_t)(p), 0, (unsigned)(n)) ++#define R_Malloc(p, t, n) ((p = (t) kmalloc((size_t)(n), GFP_ATOMIC)), Bzero((p),(n))) ++#define Free(p) kfree((caddr_t)p); ++ ++void rj_init __P((void)); ++int rj_inithead __P((void **, int)); ++int rj_refines __P((void *, void *)); ++int rj_walktree __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w)); ++struct radij_node ++ *rj_addmask __P((void *, int, int)) /* , rgb */ ; ++int /* * */ rj_addroute __P((void *, void *, struct radij_node_head *, ++ struct radij_node [2])) /* , rgb */ ; ++int /* * */ rj_delete __P((void *, void *, struct radij_node_head *, struct radij_node **)) /* , rgb */ ; ++struct radij_node /* rgb */ ++ *rj_insert __P((void *, struct radij_node_head *, int *, ++ struct radij_node [2])), ++ *rj_match __P((void *, struct radij_node_head *)), ++ *rj_newpair __P((void *, int, struct radij_node[2])), ++ *rj_search __P((void *, struct radij_node *)), ++ *rj_search_m __P((void *, struct radij_node *, void *)); ++ ++void rj_deltree(struct radij_node_head *); ++void rj_delnodes(struct radij_node *); ++void rj_free_mkfreelist(void); ++int radijcleartree(void); ++int radijcleanup(void); ++ ++extern struct radij_node_head *mask_rjhead; ++extern int maj_keylen; ++#endif /* __KERNEL__ */ ++ ++#endif /* _RADIJ_H_ */ ++ ++ ++/* ++ * $Log: radij.h,v $ ++ * Revision 1.13 2004/04/05 19:55:08 mcr ++ * Moved from linux/include/freeswan/radij.h,v ++ * ++ * Revision 1.12 2002/04/24 07:36:48 mcr ++ * Moved from ./klips/net/ipsec/radij.h,v ++ * ++ * Revision 1.11 2001/09/20 15:33:00 rgb ++ * Min/max cleanup. ++ * ++ * Revision 1.10 1999/11/18 04:09:20 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.9 1999/05/05 22:02:33 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.8 1999/04/29 15:24:58 rgb ++ * Add check for existence of macros min/max. ++ * ++ * Revision 1.7 1999/04/11 00:29:02 henry ++ * GPL boilerplate ++ * ++ * Revision 1.6 1999/04/06 04:54:29 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.5 1999/01/22 06:30:32 rgb ++ * 64-bit clean-up. ++ * ++ * Revision 1.4 1998/11/30 13:22:55 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.3 1998/10/25 02:43:27 rgb ++ * Change return type on rj_addroute and rj_delete and add and argument ++ * to the latter to be able to transmit more infomation about errors. ++ * ++ * Revision 1.2 1998/07/14 18:09:51 rgb ++ * Add a routine to clear eroute table. ++ * Added #ifdef __KERNEL__ directives to restrict scope of header. ++ * ++ * Revision 1.1 1998/06/18 21:30:22 henry ++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel ++ * build scripts happier about symlinks ++ * ++ * Revision 1.4 1998/05/25 20:34:16 rgb ++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions. ++ * ++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and ++ * add ipsec_rj_walker_delete. ++ * ++ * Recover memory for eroute table on unload of module. ++ * ++ * Revision 1.3 1998/04/22 16:51:37 rgb ++ * Tidy up radij debug code from recent rash of modifications to debug code. ++ * ++ * Revision 1.2 1998/04/14 17:30:38 rgb ++ * Fix up compiling errors for radij tree memory reclamation. ++ * ++ * Revision 1.1 1998/04/09 03:06:16 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:44:45 ji ++ * Release update only. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/pfkey.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,509 @@ ++/* ++ * FreeS/WAN specific PF_KEY headers ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey.h,v 1.45 2004/04/06 02:49:00 mcr Exp $ ++ */ ++ ++#ifndef __NET_IPSEC_PF_KEY_H ++#define __NET_IPSEC_PF_KEY_H ++#ifdef __KERNEL__ ++extern struct proto_ops pfkey_proto_ops; ++typedef struct sock pfkey_sock; ++extern int debug_pfkey; ++ ++extern /* void */ int pfkey_init(void); ++extern /* void */ int pfkey_cleanup(void); ++ ++extern struct sock *pfkey_sock_list; ++struct socket_list ++{ ++ struct socket *socketp; ++ struct socket_list *next; ++}; ++extern int pfkey_list_insert_socket(struct socket*, struct socket_list**); ++extern int pfkey_list_remove_socket(struct socket*, struct socket_list**); ++extern struct socket_list *pfkey_open_sockets; ++extern struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1]; ++ ++struct supported ++{ ++ uint16_t supported_alg_exttype; ++ uint8_t supported_alg_id; ++ uint8_t supported_alg_ivlen; ++ uint16_t supported_alg_minbits; ++ uint16_t supported_alg_maxbits; ++}; ++ ++extern struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1]; ++struct supported_list ++{ ++ struct supported *supportedp; ++ struct supported_list *next; ++}; ++extern int pfkey_list_insert_supported(struct supported*, struct supported_list**); ++extern int pfkey_list_remove_supported(struct supported*, struct supported_list**); ++ ++struct sockaddr_key ++{ ++ uint16_t key_family; /* PF_KEY */ ++ uint16_t key_pad; /* not used */ ++ uint32_t key_pid; /* process ID */ ++}; ++ ++struct pfkey_extracted_data ++{ ++ struct ipsec_sa* ips; ++ struct ipsec_sa* ips2; ++ struct eroute *eroute; ++}; ++ ++extern int ++pfkey_alloc_eroute(struct eroute** eroute); ++ ++extern int ++pfkey_sa_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_lifetime_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_address_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_key_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_ident_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_sens_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_prop_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_supported_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_spirange_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_x_satype_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int ++pfkey_x_debug_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data* extr); ++ ++extern int pfkey_upmsg(struct socket *, struct sadb_msg *); ++extern int pfkey_expire(struct ipsec_sa *, int); ++extern int pfkey_acquire(struct ipsec_sa *); ++#else /* ! __KERNEL__ */ ++ ++extern void (*pfkey_debug_func)(const char *message, ...); ++extern void (*pfkey_error_func)(const char *message, ...); ++extern void pfkey_print(struct sadb_msg *msg, FILE *out); ++ ++ ++#endif /* __KERNEL__ */ ++ ++extern uint8_t satype2proto(uint8_t satype); ++extern uint8_t proto2satype(uint8_t proto); ++extern char* satype2name(uint8_t satype); ++extern char* proto2name(uint8_t proto); ++ ++struct key_opt ++{ ++ uint32_t key_pid; /* process ID */ ++ struct sock *sk; ++}; ++ ++#define key_pid(sk) ((struct key_opt*)&((sk)->protinfo))->key_pid ++ ++/* XXX-mcr this is not an alignment, this is because the count is in 64-bit ++ * words. ++ */ ++#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t)) ++#define BITS_PER_OCTET 8 ++#define OCTETBITS 8 ++#define PFKEYBITS 64 ++#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */ ++#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */ ++ ++#define IPSEC_PFKEYv2_LEN(x) ((x) * IPSEC_PFKEYv2_ALIGN) ++#define IPSEC_PFKEYv2_WORDS(x) ((x) / IPSEC_PFKEYv2_ALIGN) ++ ++ ++#define PFKEYv2_MAX_MSGSIZE 4096 ++ ++/* ++ * PF_KEYv2 permitted and required extensions in and out bitmaps ++ */ ++struct pf_key_ext_parsers_def { ++ int (*parser)(struct sadb_ext*); ++ char *parser_name; ++}; ++ ++ ++extern unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/]; ++#define EXT_BITS_IN 0 ++#define EXT_BITS_OUT 1 ++#define EXT_BITS_PERM 0 ++#define EXT_BITS_REQ 1 ++ ++extern void pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]); ++extern void pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]); ++extern void pfkey_msg_free(struct sadb_msg **pfkey_msg); ++ ++extern int pfkey_msg_parse(struct sadb_msg *pfkey_msg, ++ struct pf_key_ext_parsers_def *ext_parsers[], ++ struct sadb_ext **extensions, ++ int dir); ++ ++extern int pfkey_register_reply(int satype, struct sadb_msg *sadb_msg); ++ ++/* ++ * PF_KEYv2 build function prototypes ++ */ ++ ++int ++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext, ++ uint8_t msg_type, ++ uint8_t satype, ++ uint8_t msg_errno, ++ uint32_t seq, ++ uint32_t pid); ++ ++int ++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t spi, /* in network order */ ++ uint8_t replay_window, ++ uint8_t sa_state, ++ uint8_t auth, ++ uint8_t encrypt, ++ uint32_t flags, ++ uint32_t/*IPsecSAref_t*/ ref); ++ ++int ++pfkey_sa_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t spi, /* in network order */ ++ uint8_t replay_window, ++ uint8_t sa_state, ++ uint8_t auth, ++ uint8_t encrypt, ++ uint32_t flags); ++ ++int ++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t allocations, ++ uint64_t bytes, ++ uint64_t addtime, ++ uint64_t usetime, ++ uint32_t packets); ++ ++int ++pfkey_address_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint8_t proto, ++ uint8_t prefixlen, ++ struct sockaddr* address); ++ ++int ++pfkey_key_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t key_bits, ++ char* key); ++ ++int ++pfkey_ident_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t ident_type, ++ uint64_t ident_id, ++ uint8_t ident_len, ++ char* ident_string); ++ ++#ifdef NAT_TRAVERSAL ++#ifdef __KERNEL__ ++extern int pfkey_nat_t_new_mapping(struct ipsec_sa *, struct sockaddr *, __u16); ++extern int pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr); ++extern int pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr); ++#endif /* __KERNEL__ */ ++int ++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext, ++ uint8_t type); ++int ++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t port); ++#endif ++ ++int ++pfkey_sens_build(struct sadb_ext** pfkey_ext, ++ uint32_t dpd, ++ uint8_t sens_level, ++ uint8_t sens_len, ++ uint64_t* sens_bitmap, ++ uint8_t integ_level, ++ uint8_t integ_len, ++ uint64_t* integ_bitmap); ++ ++int pfkey_x_protocol_build(struct sadb_ext **, uint8_t); ++ ++ ++int ++pfkey_prop_build(struct sadb_ext** pfkey_ext, ++ uint8_t replay, ++ unsigned int comb_num, ++ struct sadb_comb* comb); ++ ++int ++pfkey_supported_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ unsigned int alg_num, ++ struct sadb_alg* alg); ++ ++int ++pfkey_spirange_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint32_t min, ++ uint32_t max); ++ ++int ++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext); ++ ++int ++pfkey_x_satype_build(struct sadb_ext** pfkey_ext, ++ uint8_t satype); ++ ++int ++pfkey_x_debug_build(struct sadb_ext** pfkey_ext, ++ uint32_t tunnel, ++ uint32_t netlink, ++ uint32_t xform, ++ uint32_t eroute, ++ uint32_t spi, ++ uint32_t radij, ++ uint32_t esp, ++ uint32_t ah, ++ uint32_t rcv, ++ uint32_t pfkey, ++ uint32_t ipcomp, ++ uint32_t verbose); ++ ++int ++pfkey_msg_build(struct sadb_msg** pfkey_msg, ++ struct sadb_ext* extensions[], ++ int dir); ++ ++/* in pfkey_v2_debug.c - routines to decode numbers -> strings */ ++const char * ++pfkey_v2_sadb_ext_string(int extnum); ++ ++const char * ++pfkey_v2_sadb_type_string(int sadb_type); ++ ++ ++#endif /* __NET_IPSEC_PF_KEY_H */ ++ ++/* ++ * $Log: pfkey.h,v $ ++ * Revision 1.45 2004/04/06 02:49:00 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.44 2003/12/10 01:20:01 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.43 2003/10/31 02:26:44 mcr ++ * pulled up port-selector patches. ++ * ++ * Revision 1.42.2.2 2003/10/29 01:09:32 mcr ++ * added debugging for pfkey library. ++ * ++ * Revision 1.42.2.1 2003/09/21 13:59:34 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.42 2003/08/25 22:08:19 mcr ++ * removed pfkey_proto_init() from pfkey.h for 2.6 support. ++ * ++ * Revision 1.41 2003/05/07 17:28:57 mcr ++ * new function pfkey_debug_func added for us in debugging from ++ ++ * pfkey library. ++ * ++ * Revision 1.40 2003/01/30 02:31:34 rgb ++ * ++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. ++ * ++ * Revision 1.39 2002/09/20 15:40:21 rgb ++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc(). ++ * Added ref parameter to pfkey_sa_build(). ++ * Cleaned out unused cruft. ++ * ++ * Revision 1.38 2002/05/14 02:37:24 rgb ++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, ++ * ipsec_sa or ipsec_sa. ++ * Added function prototypes for the functions moved to ++ * pfkey_v2_ext_process.c. ++ * ++ * Revision 1.37 2002/04/24 07:36:49 mcr ++ * Moved from ./lib/pfkey.h,v ++ * ++ * Revision 1.36 2002/01/20 20:34:49 mcr ++ * added pfkey_v2_sadb_type_string to decode sadb_type to string. ++ * ++ * Revision 1.35 2001/11/27 05:27:47 mcr ++ * pfkey parses are now maintained by a structure ++ * that includes their name for debug purposes. ++ * ++ * Revision 1.34 2001/11/26 09:23:53 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.33 2001/11/06 19:47:47 rgb ++ * Added packet parameter to lifetime and comb structures. ++ * ++ * Revision 1.32 2001/09/08 21:13:34 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.31 2001/06/14 19:35:16 rgb ++ * Update copyright date. ++ * ++ * Revision 1.30 2001/02/27 07:04:52 rgb ++ * Added satype2name prototype. ++ * ++ * Revision 1.29 2001/02/26 19:59:33 rgb ++ * Ditch unused sadb_satype2proto[], replaced by satype2proto(). ++ * ++ * Revision 1.28 2000/10/10 20:10:19 rgb ++ * Added support for debug_ipcomp and debug_verbose to klipsdebug. ++ * ++ * Revision 1.27 2000/09/21 04:20:45 rgb ++ * Fixed array size off-by-one error. (Thanks Svenning!) ++ * ++ * Revision 1.26 2000/09/12 03:26:05 rgb ++ * Added pfkey_acquire prototype. ++ * ++ * Revision 1.25 2000/09/08 19:21:28 rgb ++ * Fix pfkey_prop_build() parameter to be only single indirection. ++ * ++ * Revision 1.24 2000/09/01 18:46:42 rgb ++ * Added a supported algorithms array lists, one per satype and registered ++ * existing algorithms. ++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to ++ * list. ++ * ++ * Revision 1.23 2000/08/27 01:55:26 rgb ++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. ++ * ++ * Revision 1.22 2000/08/20 21:39:23 rgb ++ * Added kernel prototypes for kernel funcitions pfkey_upmsg() and ++ * pfkey_expire(). ++ * ++ * Revision 1.21 2000/08/15 17:29:23 rgb ++ * Fixes from SZI to untested pfkey_prop_build(). ++ * ++ * Revision 1.20 2000/05/10 20:14:19 rgb ++ * Fleshed out sensitivity, proposal and supported extensions. ++ * ++ * Revision 1.19 2000/03/16 14:07:23 rgb ++ * Renamed ALIGN macro to avoid fighting with others in kernel. ++ * ++ * Revision 1.18 2000/01/22 23:24:06 rgb ++ * Added prototypes for proto2satype(), satype2proto() and proto2name(). ++ * ++ * Revision 1.17 2000/01/21 06:26:59 rgb ++ * Converted from double tdb arguments to one structure (extr) ++ * containing pointers to all temporary information structures. ++ * Added klipsdebug switching capability. ++ * Dropped unused argument to pfkey_x_satype_build(). ++ * ++ * Revision 1.16 1999/12/29 21:17:41 rgb ++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg** ++ * parameter for cleaner manipulation of extensions[] and to guard ++ * against potential memory leaks. ++ * Changed the I/F to pfkey_msg_free() for the same reason. ++ * ++ * Revision 1.15 1999/12/09 23:12:54 rgb ++ * Added macro for BITS_PER_OCTET. ++ * Added argument to pfkey_sa_build() to do eroutes. ++ * ++ * Revision 1.14 1999/12/08 20:33:25 rgb ++ * Changed sa_family_t to uint16_t for 2.0.xx compatibility. ++ * ++ * Revision 1.13 1999/12/07 19:53:40 rgb ++ * Removed unused first argument from extension parsers. ++ * Changed __u* types to uint* to avoid use of asm/types.h and ++ * sys/types.h in userspace code. ++ * Added function prototypes for pfkey message and extensions ++ * initialisation and cleanup. ++ * ++ * Revision 1.12 1999/12/01 22:19:38 rgb ++ * Change pfkey_sa_build to accept an SPI in network byte order. ++ * ++ * Revision 1.11 1999/11/27 11:55:26 rgb ++ * Added extern sadb_satype2proto to enable moving protocol lookup table ++ * to lib/pfkey_v2_parse.c. ++ * Delete unused, moved typedefs. ++ * Add argument to pfkey_msg_parse() for direction. ++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. ++ * ++ * Revision 1.10 1999/11/23 22:29:21 rgb ++ * This file has been moved in the distribution from klips/net/ipsec to ++ * lib. ++ * Add macros for dealing with alignment and rounding up more opaquely. ++ * The uint_t type defines have been moved to freeswan.h to avoid ++ * chicken-and-egg problems. ++ * Add macros for dealing with alignment and rounding up more opaque. ++ * Added prototypes for using extention header bitmaps. ++ * Added prototypes of all the build functions. ++ * ++ * Revision 1.9 1999/11/20 21:59:48 rgb ++ * Moved socketlist type declarations and prototypes for shared use. ++ * Slightly modified scope of sockaddr_key declaration. ++ * ++ * Revision 1.8 1999/11/17 14:34:25 rgb ++ * Protect sa_family_t from being used in userspace with GLIBC<2. ++ * ++ * Revision 1.7 1999/10/27 19:40:35 rgb ++ * Add a maximum PFKEY packet size macro. ++ * ++ * Revision 1.6 1999/10/26 16:58:58 rgb ++ * Created a sockaddr_key and key_opt socket extension structures. ++ * ++ * Revision 1.5 1999/06/10 05:24:41 rgb ++ * Renamed variables to reduce confusion. ++ * ++ * Revision 1.4 1999/04/29 15:21:11 rgb ++ * Add pfkey support to debugging. ++ * Add return values to init and cleanup functions. ++ * ++ * Revision 1.3 1999/04/15 17:58:07 rgb ++ * Add RCSID labels. ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/pfkeyv2.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,467 @@ ++/* ++ * RCSID $Id: pfkeyv2.h,v 1.30 2004/04/06 02:49:00 mcr Exp $ ++ */ ++ ++/* ++RFC 2367 PF_KEY Key Management API July 1998 ++ ++ ++Appendix D: Sample Header File ++ ++This file defines structures and symbols for the PF_KEY Version 2 ++key management interface. It was written at the U.S. Naval Research ++Laboratory. This file is in the public domain. The authors ask that ++you leave this credit intact on any copies of this file. ++*/ ++#ifndef __PFKEY_V2_H ++#define __PFKEY_V2_H 1 ++ ++#define PF_KEY_V2 2 ++#define PFKEYV2_REVISION 199806L ++ ++#define SADB_RESERVED 0 ++#define SADB_GETSPI 1 ++#define SADB_UPDATE 2 ++#define SADB_ADD 3 ++#define SADB_DELETE 4 ++#define SADB_GET 5 ++#define SADB_ACQUIRE 6 ++#define SADB_REGISTER 7 ++#define SADB_EXPIRE 8 ++#define SADB_FLUSH 9 ++#define SADB_DUMP 10 ++#define SADB_X_PROMISC 11 ++#define SADB_X_PCHANGE 12 ++#define SADB_X_GRPSA 13 ++#define SADB_X_ADDFLOW 14 ++#define SADB_X_DELFLOW 15 ++#define SADB_X_DEBUG 16 ++#define SADB_X_NAT_T_NEW_MAPPING 17 ++#define SADB_MAX 17 ++ ++struct sadb_msg { ++ uint8_t sadb_msg_version; ++ uint8_t sadb_msg_type; ++ uint8_t sadb_msg_errno; ++ uint8_t sadb_msg_satype; ++ uint16_t sadb_msg_len; ++ uint16_t sadb_msg_reserved; ++ uint32_t sadb_msg_seq; ++ uint32_t sadb_msg_pid; ++}; ++ ++struct sadb_ext { ++ uint16_t sadb_ext_len; ++ uint16_t sadb_ext_type; ++}; ++ ++struct sadb_sa { ++ uint16_t sadb_sa_len; ++ uint16_t sadb_sa_exttype; ++ uint32_t sadb_sa_spi; ++ uint8_t sadb_sa_replay; ++ uint8_t sadb_sa_state; ++ uint8_t sadb_sa_auth; ++ uint8_t sadb_sa_encrypt; ++ uint32_t sadb_sa_flags; ++ uint32_t /*IPsecSAref_t*/ sadb_x_sa_ref; /* 32 bits */ ++ uint8_t sadb_x_reserved[4]; ++}; ++ ++struct sadb_sa_v1 { ++ uint16_t sadb_sa_len; ++ uint16_t sadb_sa_exttype; ++ uint32_t sadb_sa_spi; ++ uint8_t sadb_sa_replay; ++ uint8_t sadb_sa_state; ++ uint8_t sadb_sa_auth; ++ uint8_t sadb_sa_encrypt; ++ uint32_t sadb_sa_flags; ++}; ++ ++struct sadb_lifetime { ++ uint16_t sadb_lifetime_len; ++ uint16_t sadb_lifetime_exttype; ++ uint32_t sadb_lifetime_allocations; ++ uint64_t sadb_lifetime_bytes; ++ uint64_t sadb_lifetime_addtime; ++ uint64_t sadb_lifetime_usetime; ++ uint32_t sadb_x_lifetime_packets; ++ uint32_t sadb_x_lifetime_reserved; ++}; ++ ++struct sadb_address { ++ uint16_t sadb_address_len; ++ uint16_t sadb_address_exttype; ++ uint8_t sadb_address_proto; ++ uint8_t sadb_address_prefixlen; ++ uint16_t sadb_address_reserved; ++}; ++ ++struct sadb_key { ++ uint16_t sadb_key_len; ++ uint16_t sadb_key_exttype; ++ uint16_t sadb_key_bits; ++ uint16_t sadb_key_reserved; ++}; ++ ++struct sadb_ident { ++ uint16_t sadb_ident_len; ++ uint16_t sadb_ident_exttype; ++ uint16_t sadb_ident_type; ++ uint16_t sadb_ident_reserved; ++ uint64_t sadb_ident_id; ++}; ++ ++struct sadb_sens { ++ uint16_t sadb_sens_len; ++ uint16_t sadb_sens_exttype; ++ uint32_t sadb_sens_dpd; ++ uint8_t sadb_sens_sens_level; ++ uint8_t sadb_sens_sens_len; ++ uint8_t sadb_sens_integ_level; ++ uint8_t sadb_sens_integ_len; ++ uint32_t sadb_sens_reserved; ++}; ++ ++struct sadb_prop { ++ uint16_t sadb_prop_len; ++ uint16_t sadb_prop_exttype; ++ uint8_t sadb_prop_replay; ++ uint8_t sadb_prop_reserved[3]; ++}; ++ ++struct sadb_comb { ++ uint8_t sadb_comb_auth; ++ uint8_t sadb_comb_encrypt; ++ uint16_t sadb_comb_flags; ++ uint16_t sadb_comb_auth_minbits; ++ uint16_t sadb_comb_auth_maxbits; ++ uint16_t sadb_comb_encrypt_minbits; ++ uint16_t sadb_comb_encrypt_maxbits; ++ uint32_t sadb_comb_reserved; ++ uint32_t sadb_comb_soft_allocations; ++ uint32_t sadb_comb_hard_allocations; ++ uint64_t sadb_comb_soft_bytes; ++ uint64_t sadb_comb_hard_bytes; ++ uint64_t sadb_comb_soft_addtime; ++ uint64_t sadb_comb_hard_addtime; ++ uint64_t sadb_comb_soft_usetime; ++ uint64_t sadb_comb_hard_usetime; ++ uint32_t sadb_x_comb_soft_packets; ++ uint32_t sadb_x_comb_hard_packets; ++}; ++ ++struct sadb_supported { ++ uint16_t sadb_supported_len; ++ uint16_t sadb_supported_exttype; ++ uint32_t sadb_supported_reserved; ++}; ++ ++struct sadb_alg { ++ uint8_t sadb_alg_id; ++ uint8_t sadb_alg_ivlen; ++ uint16_t sadb_alg_minbits; ++ uint16_t sadb_alg_maxbits; ++ uint16_t sadb_alg_reserved; ++}; ++ ++struct sadb_spirange { ++ uint16_t sadb_spirange_len; ++ uint16_t sadb_spirange_exttype; ++ uint32_t sadb_spirange_min; ++ uint32_t sadb_spirange_max; ++ uint32_t sadb_spirange_reserved; ++}; ++ ++struct sadb_x_kmprivate { ++ uint16_t sadb_x_kmprivate_len; ++ uint16_t sadb_x_kmprivate_exttype; ++ uint32_t sadb_x_kmprivate_reserved; ++}; ++ ++struct sadb_x_satype { ++ uint16_t sadb_x_satype_len; ++ uint16_t sadb_x_satype_exttype; ++ uint8_t sadb_x_satype_satype; ++ uint8_t sadb_x_satype_reserved[3]; ++}; ++ ++struct sadb_x_policy { ++ uint16_t sadb_x_policy_len; ++ uint16_t sadb_x_policy_exttype; ++ uint16_t sadb_x_policy_type; ++ uint8_t sadb_x_policy_dir; ++ uint8_t sadb_x_policy_reserved; ++ uint32_t sadb_x_policy_id; ++ uint32_t sadb_x_policy_reserved2; ++}; ++ ++struct sadb_x_debug { ++ uint16_t sadb_x_debug_len; ++ uint16_t sadb_x_debug_exttype; ++ uint32_t sadb_x_debug_tunnel; ++ uint32_t sadb_x_debug_netlink; ++ uint32_t sadb_x_debug_xform; ++ uint32_t sadb_x_debug_eroute; ++ uint32_t sadb_x_debug_spi; ++ uint32_t sadb_x_debug_radij; ++ uint32_t sadb_x_debug_esp; ++ uint32_t sadb_x_debug_ah; ++ uint32_t sadb_x_debug_rcv; ++ uint32_t sadb_x_debug_pfkey; ++ uint32_t sadb_x_debug_ipcomp; ++ uint32_t sadb_x_debug_verbose; ++ uint8_t sadb_x_debug_reserved[4]; ++}; ++ ++struct sadb_x_nat_t_type { ++ uint16_t sadb_x_nat_t_type_len; ++ uint16_t sadb_x_nat_t_type_exttype; ++ uint8_t sadb_x_nat_t_type_type; ++ uint8_t sadb_x_nat_t_type_reserved[3]; ++}; ++struct sadb_x_nat_t_port { ++ uint16_t sadb_x_nat_t_port_len; ++ uint16_t sadb_x_nat_t_port_exttype; ++ uint16_t sadb_x_nat_t_port_port; ++ uint16_t sadb_x_nat_t_port_reserved; ++}; ++ ++/* ++ * A protocol structure for passing through the transport level ++ * protocol. It contains more fields than are actually used/needed ++ * but it is this way to be compatible with the structure used in ++ * OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h) ++ */ ++struct sadb_protocol { ++ uint16_t sadb_protocol_len; ++ uint16_t sadb_protocol_exttype; ++ uint8_t sadb_protocol_proto; ++ uint8_t sadb_protocol_direction; ++ uint8_t sadb_protocol_flags; ++ uint8_t sadb_protocol_reserved2; ++}; ++ ++#define SADB_EXT_RESERVED 0 ++#define SADB_EXT_SA 1 ++#define SADB_EXT_LIFETIME_CURRENT 2 ++#define SADB_EXT_LIFETIME_HARD 3 ++#define SADB_EXT_LIFETIME_SOFT 4 ++#define SADB_EXT_ADDRESS_SRC 5 ++#define SADB_EXT_ADDRESS_DST 6 ++#define SADB_EXT_ADDRESS_PROXY 7 ++#define SADB_EXT_KEY_AUTH 8 ++#define SADB_EXT_KEY_ENCRYPT 9 ++#define SADB_EXT_IDENTITY_SRC 10 ++#define SADB_EXT_IDENTITY_DST 11 ++#define SADB_EXT_SENSITIVITY 12 ++#define SADB_EXT_PROPOSAL 13 ++#define SADB_EXT_SUPPORTED_AUTH 14 ++#define SADB_EXT_SUPPORTED_ENCRYPT 15 ++#define SADB_EXT_SPIRANGE 16 ++#define SADB_X_EXT_KMPRIVATE 17 ++#define SADB_X_EXT_SATYPE2 18 ++#ifdef KERNEL26_HAS_KAME_DUPLICATES ++#define SADB_X_EXT_POLICY 18 ++#endif ++#define SADB_X_EXT_SA2 19 ++#define SADB_X_EXT_ADDRESS_DST2 20 ++#define SADB_X_EXT_ADDRESS_SRC_FLOW 21 ++#define SADB_X_EXT_ADDRESS_DST_FLOW 22 ++#define SADB_X_EXT_ADDRESS_SRC_MASK 23 ++#define SADB_X_EXT_ADDRESS_DST_MASK 24 ++#define SADB_X_EXT_DEBUG 25 ++#define SADB_X_EXT_PROTOCOL 26 ++#define SADB_X_EXT_NAT_T_TYPE 27 ++#define SADB_X_EXT_NAT_T_SPORT 28 ++#define SADB_X_EXT_NAT_T_DPORT 29 ++#define SADB_X_EXT_NAT_T_OA 30 ++#define SADB_EXT_MAX 30 ++ ++/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */ ++#define SADB_X_EXT_ADDRESS_DELFLOW \ ++ ( (1<adler to the adler32 checksum of all input read ++ so far (that is, total_in bytes). ++ ++ deflate() may update data_type if it can make a good guess about ++ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered ++ binary. This field is only for information purposes and does not affect ++ the compression algorithm in any manner. ++ ++ deflate() returns Z_OK if some progress has been made (more input ++ processed or more output produced), Z_STREAM_END if all input has been ++ consumed and all output has been produced (only when flush is set to ++ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example ++ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible ++ (for example avail_in or avail_out was zero). ++*/ ++ ++ ++ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); ++/* ++ All dynamically allocated data structures for this stream are freed. ++ This function discards any unprocessed input and does not flush any ++ pending output. ++ ++ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the ++ stream state was inconsistent, Z_DATA_ERROR if the stream was freed ++ prematurely (some input or output was discarded). In the error case, ++ msg may be set but then points to a static string (which must not be ++ deallocated). ++*/ ++ ++ ++/* ++ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); ++ ++ Initializes the internal stream state for decompression. The fields ++ next_in, avail_in, zalloc, zfree and opaque must be initialized before by ++ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact ++ value depends on the compression method), inflateInit determines the ++ compression method from the zlib header and allocates all data structures ++ accordingly; otherwise the allocation will be deferred to the first call of ++ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to ++ use default allocation functions. ++ ++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the ++ version assumed by the caller. msg is set to null if there is no error ++ message. inflateInit does not perform any decompression apart from reading ++ the zlib header if present: this will be done by inflate(). (So next_in and ++ avail_in may be modified, but next_out and avail_out are unchanged.) ++*/ ++ ++ ++ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); ++/* ++ inflate decompresses as much data as possible, and stops when the input ++ buffer becomes empty or the output buffer becomes full. It may some ++ introduce some output latency (reading input without producing any output) ++ except when forced to flush. ++ ++ The detailed semantics are as follows. inflate performs one or both of the ++ following actions: ++ ++ - Decompress more input starting at next_in and update next_in and avail_in ++ accordingly. If not all input can be processed (because there is not ++ enough room in the output buffer), next_in is updated and processing ++ will resume at this point for the next call of inflate(). ++ ++ - Provide more output starting at next_out and update next_out and avail_out ++ accordingly. inflate() provides as much output as possible, until there ++ is no more input data or no more space in the output buffer (see below ++ about the flush parameter). ++ ++ Before the call of inflate(), the application should ensure that at least ++ one of the actions is possible, by providing more input and/or consuming ++ more output, and updating the next_* and avail_* values accordingly. ++ The application can consume the uncompressed output when it wants, for ++ example when the output buffer is full (avail_out == 0), or after each ++ call of inflate(). If inflate returns Z_OK and with zero avail_out, it ++ must be called again after making room in the output buffer because there ++ might be more output pending. ++ ++ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much ++ output as possible to the output buffer. The flushing behavior of inflate is ++ not specified for values of the flush parameter other than Z_SYNC_FLUSH ++ and Z_FINISH, but the current implementation actually flushes as much output ++ as possible anyway. ++ ++ inflate() should normally be called until it returns Z_STREAM_END or an ++ error. However if all decompression is to be performed in a single step ++ (a single call of inflate), the parameter flush should be set to ++ Z_FINISH. In this case all pending input is processed and all pending ++ output is flushed; avail_out must be large enough to hold all the ++ uncompressed data. (The size of the uncompressed data may have been saved ++ by the compressor for this purpose.) The next operation on this stream must ++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH ++ is never required, but can be used to inform inflate that a faster routine ++ may be used for the single inflate() call. ++ ++ If a preset dictionary is needed at this point (see inflateSetDictionary ++ below), inflate sets strm-adler to the adler32 checksum of the ++ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise ++ it sets strm->adler to the adler32 checksum of all output produced ++ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or ++ an error code as described below. At the end of the stream, inflate() ++ checks that its computed adler32 checksum is equal to that saved by the ++ compressor and returns Z_STREAM_END only if the checksum is correct. ++ ++ inflate() returns Z_OK if some progress has been made (more input processed ++ or more output produced), Z_STREAM_END if the end of the compressed data has ++ been reached and all uncompressed output has been produced, Z_NEED_DICT if a ++ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was ++ corrupted (input stream not conforming to the zlib format or incorrect ++ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent ++ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not ++ enough memory, Z_BUF_ERROR if no progress is possible or if there was not ++ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR ++ case, the application may then call inflateSync to look for a good ++ compression block. ++*/ ++ ++ ++ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); ++/* ++ All dynamically allocated data structures for this stream are freed. ++ This function discards any unprocessed input and does not flush any ++ pending output. ++ ++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state ++ was inconsistent. In the error case, msg may be set but then points to a ++ static string (which must not be deallocated). ++*/ ++ ++ /* Advanced functions */ ++ ++/* ++ The following functions are needed only in some special applications. ++*/ ++ ++/* ++ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, ++ int level, ++ int method, ++ int windowBits, ++ int memLevel, ++ int strategy)); ++ ++ This is another version of deflateInit with more compression options. The ++ fields next_in, zalloc, zfree and opaque must be initialized before by ++ the caller. ++ ++ The method parameter is the compression method. It must be Z_DEFLATED in ++ this version of the library. ++ ++ The windowBits parameter is the base two logarithm of the window size ++ (the size of the history buffer). It should be in the range 8..15 for this ++ version of the library. Larger values of this parameter result in better ++ compression at the expense of memory usage. The default value is 15 if ++ deflateInit is used instead. ++ ++ The memLevel parameter specifies how much memory should be allocated ++ for the internal compression state. memLevel=1 uses minimum memory but ++ is slow and reduces compression ratio; memLevel=9 uses maximum memory ++ for optimal speed. The default value is 8. See zconf.h for total memory ++ usage as a function of windowBits and memLevel. ++ ++ The strategy parameter is used to tune the compression algorithm. Use the ++ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a ++ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no ++ string match). Filtered data consists mostly of small values with a ++ somewhat random distribution. In this case, the compression algorithm is ++ tuned to compress them better. The effect of Z_FILTERED is to force more ++ Huffman coding and less string matching; it is somewhat intermediate ++ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects ++ the compression ratio but not the correctness of the compressed output even ++ if it is not set appropriately. ++ ++ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid ++ method). msg is set to null if there is no error message. deflateInit2 does ++ not perform any compression: this will be done by deflate(). ++*/ ++ ++ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, ++ const Bytef *dictionary, ++ uInt dictLength)); ++/* ++ Initializes the compression dictionary from the given byte sequence ++ without producing any compressed output. This function must be called ++ immediately after deflateInit, deflateInit2 or deflateReset, before any ++ call of deflate. The compressor and decompressor must use exactly the same ++ dictionary (see inflateSetDictionary). ++ ++ The dictionary should consist of strings (byte sequences) that are likely ++ to be encountered later in the data to be compressed, with the most commonly ++ used strings preferably put towards the end of the dictionary. Using a ++ dictionary is most useful when the data to be compressed is short and can be ++ predicted with good accuracy; the data can then be compressed better than ++ with the default empty dictionary. ++ ++ Depending on the size of the compression data structures selected by ++ deflateInit or deflateInit2, a part of the dictionary may in effect be ++ discarded, for example if the dictionary is larger than the window size in ++ deflate or deflate2. Thus the strings most likely to be useful should be ++ put at the end of the dictionary, not at the front. ++ ++ Upon return of this function, strm->adler is set to the Adler32 value ++ of the dictionary; the decompressor may later use this value to determine ++ which dictionary has been used by the compressor. (The Adler32 value ++ applies to the whole dictionary even if only a subset of the dictionary is ++ actually used by the compressor.) ++ ++ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a ++ parameter is invalid (such as NULL dictionary) or the stream state is ++ inconsistent (for example if deflate has already been called for this stream ++ or if the compression method is bsort). deflateSetDictionary does not ++ perform any compression: this will be done by deflate(). ++*/ ++ ++ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, ++ z_streamp source)); ++/* ++ Sets the destination stream as a complete copy of the source stream. ++ ++ This function can be useful when several compression strategies will be ++ tried, for example when there are several ways of pre-processing the input ++ data with a filter. The streams that will be discarded should then be freed ++ by calling deflateEnd. Note that deflateCopy duplicates the internal ++ compression state which can be quite large, so this strategy is slow and ++ can consume lots of memory. ++ ++ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent ++ (such as zalloc being NULL). msg is left unchanged in both source and ++ destination. ++*/ ++ ++ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); ++/* ++ This function is equivalent to deflateEnd followed by deflateInit, ++ but does not free and reallocate all the internal compression state. ++ The stream will keep the same compression level and any other attributes ++ that may have been set by deflateInit2. ++ ++ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent (such as zalloc or state being NULL). ++*/ ++ ++ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, ++ int level, ++ int strategy)); ++/* ++ Dynamically update the compression level and compression strategy. The ++ interpretation of level and strategy is as in deflateInit2. This can be ++ used to switch between compression and straight copy of the input data, or ++ to switch to a different kind of input data requiring a different ++ strategy. If the compression level is changed, the input available so far ++ is compressed with the old level (and may be flushed); the new level will ++ take effect only at the next call of deflate(). ++ ++ Before the call of deflateParams, the stream state must be set as for ++ a call of deflate(), since the currently available input may have to ++ be compressed and flushed. In particular, strm->avail_out must be non-zero. ++ ++ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source ++ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR ++ if strm->avail_out was zero. ++*/ ++ ++/* ++ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, ++ int windowBits)); ++ ++ This is another version of inflateInit with an extra parameter. The ++ fields next_in, avail_in, zalloc, zfree and opaque must be initialized ++ before by the caller. ++ ++ The windowBits parameter is the base two logarithm of the maximum window ++ size (the size of the history buffer). It should be in the range 8..15 for ++ this version of the library. The default value is 15 if inflateInit is used ++ instead. If a compressed stream with a larger window size is given as ++ input, inflate() will return with the error code Z_DATA_ERROR instead of ++ trying to allocate a larger window. ++ ++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative ++ memLevel). msg is set to null if there is no error message. inflateInit2 ++ does not perform any decompression apart from reading the zlib header if ++ present: this will be done by inflate(). (So next_in and avail_in may be ++ modified, but next_out and avail_out are unchanged.) ++*/ ++ ++ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, ++ const Bytef *dictionary, ++ uInt dictLength)); ++/* ++ Initializes the decompression dictionary from the given uncompressed byte ++ sequence. This function must be called immediately after a call of inflate ++ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor ++ can be determined from the Adler32 value returned by this call of ++ inflate. The compressor and decompressor must use exactly the same ++ dictionary (see deflateSetDictionary). ++ ++ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a ++ parameter is invalid (such as NULL dictionary) or the stream state is ++ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the ++ expected one (incorrect Adler32 value). inflateSetDictionary does not ++ perform any decompression: this will be done by subsequent calls of ++ inflate(). ++*/ ++ ++ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); ++/* ++ Skips invalid compressed data until a full flush point (see above the ++ description of deflate with Z_FULL_FLUSH) can be found, or until all ++ available input is skipped. No output is provided. ++ ++ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR ++ if no more input was provided, Z_DATA_ERROR if no flush point has been found, ++ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success ++ case, the application may save the current current value of total_in which ++ indicates where valid compressed data was found. In the error case, the ++ application may repeatedly call inflateSync, providing more input each time, ++ until success or end of the input data. ++*/ ++ ++ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); ++/* ++ This function is equivalent to inflateEnd followed by inflateInit, ++ but does not free and reallocate all the internal decompression state. ++ The stream will keep attributes that may have been set by inflateInit2. ++ ++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source ++ stream state was inconsistent (such as zalloc or state being NULL). ++*/ ++ ++ ++ /* utility functions */ ++ ++/* ++ The following utility functions are implemented on top of the ++ basic stream-oriented functions. To simplify the interface, some ++ default options are assumed (compression level and memory usage, ++ standard memory allocation functions). The source code of these ++ utility functions can easily be modified if you need special options. ++*/ ++ ++ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen)); ++/* ++ Compresses the source buffer into the destination buffer. sourceLen is ++ the byte length of the source buffer. Upon entry, destLen is the total ++ size of the destination buffer, which must be at least 0.1% larger than ++ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the ++ compressed buffer. ++ This function can be used to compress a whole file at once if the ++ input file is mmap'ed. ++ compress returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_BUF_ERROR if there was not enough room in the output ++ buffer. ++*/ ++ ++ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen, ++ int level)); ++/* ++ Compresses the source buffer into the destination buffer. The level ++ parameter has the same meaning as in deflateInit. sourceLen is the byte ++ length of the source buffer. Upon entry, destLen is the total size of the ++ destination buffer, which must be at least 0.1% larger than sourceLen plus ++ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. ++ ++ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough ++ memory, Z_BUF_ERROR if there was not enough room in the output buffer, ++ Z_STREAM_ERROR if the level parameter is invalid. ++*/ ++ ++ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, ++ const Bytef *source, uLong sourceLen)); ++/* ++ Decompresses the source buffer into the destination buffer. sourceLen is ++ the byte length of the source buffer. Upon entry, destLen is the total ++ size of the destination buffer, which must be large enough to hold the ++ entire uncompressed data. (The size of the uncompressed data must have ++ been saved previously by the compressor and transmitted to the decompressor ++ by some mechanism outside the scope of this compression library.) ++ Upon exit, destLen is the actual size of the compressed buffer. ++ This function can be used to decompress a whole file at once if the ++ input file is mmap'ed. ++ ++ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not ++ enough memory, Z_BUF_ERROR if there was not enough room in the output ++ buffer, or Z_DATA_ERROR if the input data was corrupted. ++*/ ++ ++ ++typedef voidp gzFile; ++ ++ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); ++/* ++ Opens a gzip (.gz) file for reading or writing. The mode parameter ++ is as in fopen ("rb" or "wb") but can also include a compression level ++ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for ++ Huffman only compression as in "wb1h". (See the description ++ of deflateInit2 for more information about the strategy parameter.) ++ ++ gzopen can be used to read a file which is not in gzip format; in this ++ case gzread will directly read from the file without decompression. ++ ++ gzopen returns NULL if the file could not be opened or if there was ++ insufficient memory to allocate the (de)compression state; errno ++ can be checked to distinguish the two cases (if errno is zero, the ++ zlib error is Z_MEM_ERROR). */ ++ ++ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); ++/* ++ gzdopen() associates a gzFile with the file descriptor fd. File ++ descriptors are obtained from calls like open, dup, creat, pipe or ++ fileno (in the file has been previously opened with fopen). ++ The mode parameter is as in gzopen. ++ The next call of gzclose on the returned gzFile will also close the ++ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file ++ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). ++ gzdopen returns NULL if there was insufficient memory to allocate ++ the (de)compression state. ++*/ ++ ++ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); ++/* ++ Dynamically update the compression level or strategy. See the description ++ of deflateInit2 for the meaning of these parameters. ++ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not ++ opened for writing. ++*/ ++ ++ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); ++/* ++ Reads the given number of uncompressed bytes from the compressed file. ++ If the input file was not in gzip format, gzread copies the given number ++ of bytes into the buffer. ++ gzread returns the number of uncompressed bytes actually read (0 for ++ end of file, -1 for error). */ ++ ++ZEXTERN int ZEXPORT gzwrite OF((gzFile file, ++ const voidp buf, unsigned len)); ++/* ++ Writes the given number of uncompressed bytes into the compressed file. ++ gzwrite returns the number of uncompressed bytes actually written ++ (0 in case of error). ++*/ ++ ++ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); ++/* ++ Converts, formats, and writes the args to the compressed file under ++ control of the format string, as in fprintf. gzprintf returns the number of ++ uncompressed bytes actually written (0 in case of error). ++*/ ++ ++ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); ++/* ++ Writes the given null-terminated string to the compressed file, excluding ++ the terminating null character. ++ gzputs returns the number of characters written, or -1 in case of error. ++*/ ++ ++ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); ++/* ++ Reads bytes from the compressed file until len-1 characters are read, or ++ a newline character is read and transferred to buf, or an end-of-file ++ condition is encountered. The string is then terminated with a null ++ character. ++ gzgets returns buf, or Z_NULL in case of error. ++*/ ++ ++ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); ++/* ++ Writes c, converted to an unsigned char, into the compressed file. ++ gzputc returns the value that was written, or -1 in case of error. ++*/ ++ ++ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); ++/* ++ Reads one byte from the compressed file. gzgetc returns this byte ++ or -1 in case of end of file or error. ++*/ ++ ++ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); ++/* ++ Flushes all pending output into the compressed file. The parameter ++ flush is as in the deflate() function. The return value is the zlib ++ error number (see function gzerror below). gzflush returns Z_OK if ++ the flush parameter is Z_FINISH and all output could be flushed. ++ gzflush should be called only when strictly necessary because it can ++ degrade compression. ++*/ ++ ++ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, ++ z_off_t offset, int whence)); ++/* ++ Sets the starting position for the next gzread or gzwrite on the ++ given compressed file. The offset represents a number of bytes in the ++ uncompressed data stream. The whence parameter is defined as in lseek(2); ++ the value SEEK_END is not supported. ++ If the file is opened for reading, this function is emulated but can be ++ extremely slow. If the file is opened for writing, only forward seeks are ++ supported; gzseek then compresses a sequence of zeroes up to the new ++ starting position. ++ ++ gzseek returns the resulting offset location as measured in bytes from ++ the beginning of the uncompressed stream, or -1 in case of error, in ++ particular if the file is opened for writing and the new starting position ++ would be before the current position. ++*/ ++ ++ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); ++/* ++ Rewinds the given file. This function is supported only for reading. ++ ++ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) ++*/ ++ ++ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); ++/* ++ Returns the starting position for the next gzread or gzwrite on the ++ given compressed file. This position represents a number of bytes in the ++ uncompressed data stream. ++ ++ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) ++*/ ++ ++ZEXTERN int ZEXPORT gzeof OF((gzFile file)); ++/* ++ Returns 1 when EOF has previously been detected reading the given ++ input stream, otherwise zero. ++*/ ++ ++ZEXTERN int ZEXPORT gzclose OF((gzFile file)); ++/* ++ Flushes all pending output if necessary, closes the compressed file ++ and deallocates all the (de)compression state. The return value is the zlib ++ error number (see function gzerror below). ++*/ ++ ++ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); ++/* ++ Returns the error message for the last error which occurred on the ++ given compressed file. errnum is set to zlib error number. If an ++ error occurred in the file system and not in the compression library, ++ errnum is set to Z_ERRNO and the application may consult errno ++ to get the exact error code. ++*/ ++ ++ /* checksum functions */ ++ ++/* ++ These functions are not related to compression but are exported ++ anyway because they might be useful in applications using the ++ compression library. ++*/ ++ ++ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); ++ ++/* ++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and ++ return the updated checksum. If buf is NULL, this function returns ++ the required initial value for the checksum. ++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed ++ much faster. Usage example: ++ ++ uLong adler = adler32(0L, Z_NULL, 0); ++ ++ while (read_buffer(buffer, length) != EOF) { ++ adler = adler32(adler, buffer, length); ++ } ++ if (adler != original_adler) error(); ++*/ ++ ++ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); ++/* ++ Update a running crc with the bytes buf[0..len-1] and return the updated ++ crc. If buf is NULL, this function returns the required initial value ++ for the crc. Pre- and post-conditioning (one's complement) is performed ++ within this function so it shouldn't be done by the application. ++ Usage example: ++ ++ uLong crc = crc32(0L, Z_NULL, 0); ++ ++ while (read_buffer(buffer, length) != EOF) { ++ crc = crc32(crc, buffer, length); ++ } ++ if (crc != original_crc) error(); ++*/ ++ ++ ++ /* various hacks, don't look :) */ ++ ++/* deflateInit and inflateInit are macros to allow checking the zlib version ++ * and the compiler's view of z_stream: ++ */ ++ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, ++ const char *version, int stream_size)); ++ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, ++ const char *version, int stream_size)); ++ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, ++ int windowBits, int memLevel, ++ int strategy, const char *version, ++ int stream_size)); ++ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, ++ const char *version, int stream_size)); ++#define deflateInit(strm, level) \ ++ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) ++#define inflateInit(strm) \ ++ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) ++#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ ++ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ ++ (strategy), ZLIB_VERSION, sizeof(z_stream)) ++#define inflateInit2(strm, windowBits) \ ++ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) ++ ++ ++#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) ++ struct internal_state {int dummy;}; /* hack for buggy compilers */ ++#endif ++ ++ZEXTERN const char * ZEXPORT zError OF((int err)); ++ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); ++ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _ZLIB_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/include/zlib/zutil.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,225 @@ ++/* zutil.h -- internal interface and configuration of the compression library ++ * Copyright (C) 1995-2002 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/* @(#) $Id: zutil.h,v 1.4 2002/04/24 07:36:48 mcr Exp $ */ ++ ++#ifndef _Z_UTIL_H ++#define _Z_UTIL_H ++ ++#include "zlib.h" ++ ++#include ++#define HAVE_MEMCPY ++ ++#if 0 // #ifdef STDC ++# include ++# include ++# include ++#endif ++#ifndef __KERNEL__ ++#ifdef NO_ERRNO_H ++ extern int errno; ++#else ++# include ++#endif ++#endif ++ ++#ifndef local ++# define local static ++#endif ++/* compile with -Dlocal if your debugger can't find static symbols */ ++ ++typedef unsigned char uch; ++typedef uch FAR uchf; ++typedef unsigned short ush; ++typedef ush FAR ushf; ++typedef unsigned long ulg; ++ ++extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ ++/* (size given to avoid silly warnings with Visual C++) */ ++ ++#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] ++ ++#define ERR_RETURN(strm,err) \ ++ return (strm->msg = ERR_MSG(err), (err)) ++/* To be used only when the state is known to be valid */ ++ ++ /* common constants */ ++ ++#ifndef DEF_WBITS ++# define DEF_WBITS MAX_WBITS ++#endif ++/* default windowBits for decompression. MAX_WBITS is for compression only */ ++ ++#if MAX_MEM_LEVEL >= 8 ++# define DEF_MEM_LEVEL 8 ++#else ++# define DEF_MEM_LEVEL MAX_MEM_LEVEL ++#endif ++/* default memLevel */ ++ ++#define STORED_BLOCK 0 ++#define STATIC_TREES 1 ++#define DYN_TREES 2 ++/* The three kinds of block type */ ++ ++#define MIN_MATCH 3 ++#define MAX_MATCH 258 ++/* The minimum and maximum match lengths */ ++ ++#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ ++ ++ /* target dependencies */ ++ ++#ifdef MSDOS ++# define OS_CODE 0x00 ++# if defined(__TURBOC__) || defined(__BORLANDC__) ++# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) ++ /* Allow compilation with ANSI keywords only enabled */ ++ void _Cdecl farfree( void *block ); ++ void *_Cdecl farmalloc( unsigned long nbytes ); ++# else ++# include ++# endif ++# else /* MSC or DJGPP */ ++# include ++# endif ++#endif ++ ++#ifdef OS2 ++# define OS_CODE 0x06 ++#endif ++ ++#ifdef WIN32 /* Window 95 & Windows NT */ ++# define OS_CODE 0x0b ++#endif ++ ++#if defined(VAXC) || defined(VMS) ++# define OS_CODE 0x02 ++# define F_OPEN(name, mode) \ ++ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") ++#endif ++ ++#ifdef AMIGA ++# define OS_CODE 0x01 ++#endif ++ ++#if defined(ATARI) || defined(atarist) ++# define OS_CODE 0x05 ++#endif ++ ++#if defined(MACOS) || defined(TARGET_OS_MAC) ++# define OS_CODE 0x07 ++# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os ++# include /* for fdopen */ ++# else ++# ifndef fdopen ++# define fdopen(fd,mode) NULL /* No fdopen() */ ++# endif ++# endif ++#endif ++ ++#ifdef __50SERIES /* Prime/PRIMOS */ ++# define OS_CODE 0x0F ++#endif ++ ++#ifdef TOPS20 ++# define OS_CODE 0x0a ++#endif ++ ++#if defined(_BEOS_) || defined(RISCOS) ++# define fdopen(fd,mode) NULL /* No fdopen() */ ++#endif ++ ++#if (defined(_MSC_VER) && (_MSC_VER > 600)) ++# define fdopen(fd,type) _fdopen(fd,type) ++#endif ++ ++ ++ /* Common defaults */ ++ ++#ifndef OS_CODE ++# define OS_CODE 0x03 /* assume Unix */ ++#endif ++ ++#ifndef F_OPEN ++# define F_OPEN(name, mode) fopen((name), (mode)) ++#endif ++ ++ /* functions */ ++ ++#ifdef HAVE_STRERROR ++ extern char *strerror OF((int)); ++# define zstrerror(errnum) strerror(errnum) ++#else ++# define zstrerror(errnum) "" ++#endif ++ ++#if defined(pyr) ++# define NO_MEMCPY ++#endif ++#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) ++ /* Use our own functions for small and medium model with MSC <= 5.0. ++ * You may have to use the same strategy for Borland C (untested). ++ * The __SC__ check is for Symantec. ++ */ ++# define NO_MEMCPY ++#endif ++#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) ++# define HAVE_MEMCPY ++#endif ++#ifdef HAVE_MEMCPY ++# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ ++# define zmemcpy _fmemcpy ++# define zmemcmp _fmemcmp ++# define zmemzero(dest, len) _fmemset(dest, 0, len) ++# else ++# define zmemcpy memcpy ++# define zmemcmp memcmp ++# define zmemzero(dest, len) memset(dest, 0, len) ++# endif ++#else ++ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); ++ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); ++ extern void zmemzero OF((Bytef* dest, uInt len)); ++#endif ++ ++/* Diagnostic functions */ ++#ifdef DEBUG ++# include ++ extern int z_verbose; ++ extern void z_error OF((char *m)); ++# define Assert(cond,msg) {if(!(cond)) z_error(msg);} ++# define Trace(x) {if (z_verbose>=0) fprintf x ;} ++# define Tracev(x) {if (z_verbose>0) fprintf x ;} ++# define Tracevv(x) {if (z_verbose>1) fprintf x ;} ++# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} ++# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} ++#else ++# define Assert(cond,msg) ++# define Trace(x) ++# define Tracev(x) ++# define Tracevv(x) ++# define Tracec(c,x) ++# define Tracecv(c,x) ++#endif ++ ++ ++typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf, ++ uInt len)); ++voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); ++void zcfree OF((voidpf opaque, voidpf ptr)); ++ ++#define ZALLOC(strm, items, size) \ ++ (*((strm)->zalloc))((strm)->opaque, (items), (size)) ++#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) ++#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} ++ ++#endif /* _Z_UTIL_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/Makefile.objs Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,21 @@ ++obj-y += satot.o ++obj-y += addrtot.o ++obj-y += ultot.o ++obj-y += addrtypeof.o ++obj-y += anyaddr.o ++obj-y += initaddr.o ++obj-y += ultoa.o ++obj-y += addrtoa.o ++obj-y += subnettoa.o ++obj-y += subnetof.o ++obj-y += goodmask.o ++obj-y += datatot.o ++obj-y += rangetoa.o ++obj-y += prng.o ++obj-y += pfkey_v2_parse.o ++obj-y += pfkey_v2_build.o ++obj-y += pfkey_v2_debug.o ++obj-y += pfkey_v2_ext_bits.o ++ ++#version.c: ${LIBFREESWANDIR}/version.in.c ${OPENSWANSRCDIR}/Makefile.ver ++# sed '/"/s/xxx/$(IPSECVERSION)/' ${LIBFREESWANDIR}/version.in.c >$@ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/addrtoa.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,67 @@ ++/* ++ * addresses to ASCII ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: addrtoa.c,v 1.9 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++#define NBYTES 4 /* bytes in an address */ ++#define PERBYTE 4 /* three digits plus a dot or NUL */ ++#define BUFLEN (NBYTES*PERBYTE) ++ ++#if BUFLEN != ADDRTOA_BUF ++#error "ADDRTOA_BUF in openswan.h inconsistent with addrtoa() code" ++#endif ++ ++/* ++ - addrtoa - convert binary address to ASCII dotted decimal ++ */ ++size_t /* space needed for full conversion */ ++addrtoa(addr, format, dst, dstlen) ++struct in_addr addr; ++int format; /* character */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ unsigned long a = ntohl(addr.s_addr); ++ int i; ++ size_t n; ++ unsigned long byte; ++ char buf[BUFLEN]; ++ char *p; ++ ++ switch (format) { ++ case 0: ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ p = buf; ++ for (i = NBYTES-1; i >= 0; i--) { ++ byte = (a >> (i*8)) & 0xff; ++ p += ultoa(byte, 10, p, PERBYTE); ++ if (i != 0) ++ *(p-1) = '.'; ++ } ++ n = p - buf; ++ ++ if (dstlen > 0) { ++ if (n > dstlen) ++ buf[dstlen - 1] = '\0'; ++ strcpy(dst, buf); ++ } ++ return n; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/addrtot.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,334 @@ ++/* ++ * addresses to text ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: addrtot.c,v 1.15 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++#define IP4BYTES 4 /* bytes in an IPv4 address */ ++#define PERBYTE 4 /* three digits plus a dot or NUL */ ++#define IP6BYTES 16 /* bytes in an IPv6 address */ ++ ++/* forwards */ ++static size_t normal4(const unsigned char *s, size_t len, char *b, char **dp); ++static size_t normal6(const unsigned char *s, size_t len, char *b, char **dp, int squish); ++static size_t reverse4(const unsigned char *s, size_t len, char *b, char **dp); ++static size_t reverse6(const unsigned char *s, size_t len, char *b, char **dp); ++ ++/* ++ - addrtot - convert binary address to text (dotted decimal or IPv6 string) ++ */ ++size_t /* space needed for full conversion */ ++addrtot(src, format, dst, dstlen) ++const ip_address *src; ++int format; /* character */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ const unsigned char *b; ++ size_t n; ++ char buf[1+ADDRTOT_BUF+1]; /* :address: */ ++ char *p; ++ int t = addrtypeof(src); ++# define TF(t, f) (((t)<<8) | (f)) ++ ++ n = addrbytesptr(src, &b); ++ if (n == 0) { ++ bad: ++ dst[0]='\0'; ++ strncat(dst, "", dstlen); ++ return sizeof(""); ++ } ++ ++ switch (TF(t, format)) { ++ case TF(AF_INET, 0): ++ n = normal4(b, n, buf, &p); ++ break; ++ case TF(AF_INET6, 0): ++ n = normal6(b, n, buf, &p, 1); ++ break; ++ case TF(AF_INET, 'Q'): ++ n = normal4(b, n, buf, &p); ++ break; ++ case TF(AF_INET6, 'Q'): ++ n = normal6(b, n, buf, &p, 0); ++ break; ++ case TF(AF_INET, 'r'): ++ n = reverse4(b, n, buf, &p); ++ break; ++ case TF(AF_INET6, 'r'): ++ n = reverse6(b, n, buf, &p); ++ break; ++ default: /* including (AF_INET, 'R') */ ++ goto bad; ++ break; ++ } ++ ++ if (dstlen > 0) { ++ if (dstlen < n) ++ p[dstlen - 1] = '\0'; ++ strcpy(dst, p); ++ } ++ return n; ++} ++ ++/* ++ - normal4 - normal IPv4 address-text conversion ++ */ ++static size_t /* size of text, including NUL */ ++normal4(srcp, srclen, buf, dstp) ++const unsigned char *srcp; ++size_t srclen; ++char *buf; /* guaranteed large enough */ ++char **dstp; /* where to put result pointer */ ++{ ++ int i; ++ char *p; ++ ++ if (srclen != IP4BYTES) /* "can't happen" */ ++ return 0; ++ p = buf; ++ for (i = 0; i < IP4BYTES; i++) { ++ p += ultot(srcp[i], 10, p, PERBYTE); ++ if (i != IP4BYTES - 1) ++ *(p-1) = '.'; /* overwrites the NUL */ ++ } ++ *dstp = buf; ++ return p - buf; ++} ++ ++/* ++ - normal6 - normal IPv6 address-text conversion ++ */ ++static size_t /* size of text, including NUL */ ++normal6(srcp, srclen, buf, dstp, squish) ++const unsigned char *srcp; ++size_t srclen; ++char *buf; /* guaranteed large enough, plus 2 */ ++char **dstp; /* where to put result pointer */ ++int squish; /* whether to squish out 0:0 */ ++{ ++ int i; ++ unsigned long piece; ++ char *p; ++ char *q; ++ ++ if (srclen != IP6BYTES) /* "can't happen" */ ++ return 0; ++ p = buf; ++ *p++ = ':'; ++ for (i = 0; i < IP6BYTES/2; i++) { ++ piece = (srcp[2*i] << 8) + srcp[2*i + 1]; ++ p += ultot(piece, 16, p, 5); /* 5 = abcd + NUL */ ++ *(p-1) = ':'; /* overwrites the NUL */ ++ } ++ *p = '\0'; ++ q = strstr(buf, ":0:0:"); ++ if (squish && q != NULL) { /* zero squishing is possible */ ++ p = q + 1; ++ while (*p == '0' && *(p+1) == ':') ++ p += 2; ++ q++; ++ *q++ = ':'; /* overwrite first 0 */ ++ while (*p != '\0') ++ *q++ = *p++; ++ *q = '\0'; ++ if (!(*(q-1) == ':' && *(q-2) == ':')) ++ *--q = '\0'; /* strip final : unless :: */ ++ p = buf; ++ if (!(*p == ':' && *(p+1) == ':')) ++ p++; /* skip initial : unless :: */ ++ } else { ++ q = p; ++ *--q = '\0'; /* strip final : */ ++ p = buf + 1; /* skip initial : */ ++ } ++ *dstp = p; ++ return q - p + 1; ++} ++ ++/* ++ - reverse4 - IPv4 reverse-lookup conversion ++ */ ++static size_t /* size of text, including NUL */ ++reverse4(srcp, srclen, buf, dstp) ++const unsigned char *srcp; ++size_t srclen; ++char *buf; /* guaranteed large enough */ ++char **dstp; /* where to put result pointer */ ++{ ++ int i; ++ char *p; ++ ++ if (srclen != IP4BYTES) /* "can't happen" */ ++ return 0; ++ p = buf; ++ for (i = IP4BYTES-1; i >= 0; i--) { ++ p += ultot(srcp[i], 10, p, PERBYTE); ++ *(p-1) = '.'; /* overwrites the NUL */ ++ } ++ strcpy(p, "IN-ADDR.ARPA."); ++ *dstp = buf; ++ return strlen(buf) + 1; ++} ++ ++/* ++ - reverse6 - IPv6 reverse-lookup conversion (RFC 1886) ++ * A trifle inefficient, really shouldn't use ultot... ++ */ ++static size_t /* size of text, including NUL */ ++reverse6(srcp, srclen, buf, dstp) ++const unsigned char *srcp; ++size_t srclen; ++char *buf; /* guaranteed large enough */ ++char **dstp; /* where to put result pointer */ ++{ ++ int i; ++ unsigned long piece; ++ char *p; ++ ++ if (srclen != IP6BYTES) /* "can't happen" */ ++ return 0; ++ p = buf; ++ for (i = IP6BYTES-1; i >= 0; i--) { ++ piece = srcp[i]; ++ p += ultot(piece&0xf, 16, p, 2); ++ *(p-1) = '.'; ++ p += ultot(piece>>4, 16, p, 2); ++ *(p-1) = '.'; ++ } ++ strcpy(p, "IP6.ARPA."); ++ *dstp = buf; ++ return strlen(buf) + 1; ++} ++ ++/* ++ - reverse6 - modern IPv6 reverse-lookup conversion (RFC 2874) ++ * this version removed as it was obsoleted in the end. ++ */ ++ ++#ifdef ADDRTOT_MAIN ++ ++#include ++#include ++#include ++#include ++ ++void regress(void); ++ ++int ++main(int argc, char *argv[]) ++{ ++ if (argc < 2) { ++ fprintf(stderr, "Usage: %s {addr|net/mask|begin...end|-r}\n", ++ argv[0]); ++ exit(2); ++ } ++ ++ if (strcmp(argv[1], "-r") == 0) { ++ regress(); ++ fprintf(stderr, "regress() returned?!?\n"); ++ exit(1); ++ } ++ exit(0); ++} ++ ++struct rtab { ++ char *input; ++ char format; ++ char *output; /* NULL means error expected */ ++} rtab[] = { ++ {"1.2.3.0", 0, "1.2.3.0"}, ++ {"1:2::3:4", 0, "1:2::3:4"}, ++ {"1:2::3:4", 'Q', "1:2:0:0:0:0:3:4"}, ++ {"1:2:0:0:3:4:0:0", 0, "1:2::3:4:0:0"}, ++ {"1.2.3.4", 'r' , "4.3.2.1.IN-ADDR.ARPA."}, ++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f */ ++ {"1:2::3:4", 'r', "4.0.0.0.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.IP6.ARPA."}, ++ {NULL, 0, NULL} ++}; ++ ++void ++regress() ++{ ++ struct rtab *r; ++ int status = 0; ++ ip_address a; ++ char in[100]; ++ char buf[100]; ++ const char *oops; ++ size_t n; ++ ++ for (r = rtab; r->input != NULL; r++) { ++ strcpy(in, r->input); ++ ++ /* convert it *to* internal format */ ++ oops = ttoaddr(in, strlen(in), 0, &a); ++ ++ /* now convert it back */ ++ ++ n = addrtot(&a, r->format, buf, sizeof(buf)); ++ ++ if (n == 0 && r->output == NULL) ++ {} /* okay, error expected */ ++ ++ else if (n == 0) { ++ printf("`%s' atoasr failed\n", r->input); ++ status = 1; ++ ++ } else if (r->output == NULL) { ++ printf("`%s' atoasr succeeded unexpectedly '%c'\n", ++ r->input, r->format); ++ status = 1; ++ } else { ++ if (strcasecmp(r->output, buf) != 0) { ++ printf("`%s' '%c' gave `%s', expected `%s'\n", ++ r->input, r->format, buf, r->output); ++ status = 1; ++ } ++ } ++ } ++ exit(status); ++} ++ ++#endif /* ADDRTOT_MAIN */ ++ ++/* ++ * $Log: addrtot.c,v $ ++ * Revision 1.15 2004/04/11 17:39:25 mcr ++ * removed internal.h requirements. ++ * ++ * Revision 1.14 2004/03/08 01:59:08 ken ++ * freeswan.h -> openswan.h ++ * ++ * Revision 1.13 2004/01/05 23:21:05 mcr ++ * if the address type is invalid, then return length of ++ * string! ++ * ++ * Revision 1.12 2003/12/30 06:42:48 mcr ++ * added $Log: addrtot.c,v $ ++ * added Revision 1.15 2004/04/11 17:39:25 mcr ++ * added removed internal.h requirements. ++ * added ++ * added Revision 1.14 2004/03/08 01:59:08 ken ++ * added freeswan.h -> openswan.h ++ * added ++ * added Revision 1.13 2004/01/05 23:21:05 mcr ++ * added if the address type is invalid, then return length of ++ * added string! ++ * added ++ * ++ * ++ */ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/addrtypeof.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,93 @@ ++/* ++ * extract parts of an ip_address ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: addrtypeof.c,v 1.9 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - addrtypeof - get the type of an ip_address ++ */ ++int ++addrtypeof(src) ++const ip_address *src; ++{ ++ return src->u.v4.sin_family; ++} ++ ++/* ++ - addrbytesptr - get pointer to the address bytes of an ip_address ++ */ ++size_t /* 0 for error */ ++addrbytesptr(src, dstp) ++const ip_address *src; ++const unsigned char **dstp; /* NULL means just a size query */ ++{ ++ const unsigned char *p; ++ size_t n; ++ ++ switch (src->u.v4.sin_family) { ++ case AF_INET: ++ p = (const unsigned char *)&src->u.v4.sin_addr.s_addr; ++ n = 4; ++ break; ++ case AF_INET6: ++ p = (const unsigned char *)&src->u.v6.sin6_addr; ++ n = 16; ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ if (dstp != NULL) ++ *dstp = p; ++ return n; ++} ++ ++/* ++ - addrlenof - get length of the address bytes of an ip_address ++ */ ++size_t /* 0 for error */ ++addrlenof(src) ++const ip_address *src; ++{ ++ return addrbytesptr(src, NULL); ++} ++ ++/* ++ - addrbytesof - get the address bytes of an ip_address ++ */ ++size_t /* 0 for error */ ++addrbytesof(src, dst, dstlen) ++const ip_address *src; ++unsigned char *dst; ++size_t dstlen; ++{ ++ const unsigned char *p; ++ size_t n; ++ size_t ncopy; ++ ++ n = addrbytesptr(src, &p); ++ if (n == 0) ++ return 0; ++ ++ if (dstlen > 0) { ++ ncopy = n; ++ if (ncopy > dstlen) ++ ncopy = dstlen; ++ memcpy(dst, p, ncopy); ++ } ++ return n; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/anyaddr.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,145 @@ ++/* ++ * special addresses ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: anyaddr.c,v 1.9 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* these are mostly fallbacks for the no-IPv6-support-in-library case */ ++#ifndef IN6ADDR_ANY_INIT ++#define IN6ADDR_ANY_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }}} ++#endif ++#ifndef IN6ADDR_LOOPBACK_INIT ++#define IN6ADDR_LOOPBACK_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}} ++#endif ++ ++static struct in6_addr v6any = IN6ADDR_ANY_INIT; ++static struct in6_addr v6loop = IN6ADDR_LOOPBACK_INIT; ++ ++/* ++ - anyaddr - initialize to the any-address value ++ */ ++err_t /* NULL for success, else string literal */ ++anyaddr(af, dst) ++int af; /* address family */ ++ip_address *dst; ++{ ++ uint32_t v4any = htonl(INADDR_ANY); ++ ++ switch (af) { ++ case AF_INET: ++ return initaddr((unsigned char *)&v4any, sizeof(v4any), af, dst); ++ break; ++ case AF_INET6: ++ return initaddr((unsigned char *)&v6any, sizeof(v6any), af, dst); ++ break; ++ default: ++ return "unknown address family in anyaddr/unspecaddr"; ++ break; ++ } ++} ++ ++/* ++ - unspecaddr - initialize to the unspecified-address value ++ */ ++err_t /* NULL for success, else string literal */ ++unspecaddr(af, dst) ++int af; /* address family */ ++ip_address *dst; ++{ ++ return anyaddr(af, dst); ++} ++ ++/* ++ - loopbackaddr - initialize to the loopback-address value ++ */ ++err_t /* NULL for success, else string literal */ ++loopbackaddr(af, dst) ++int af; /* address family */ ++ip_address *dst; ++{ ++ uint32_t v4loop = htonl(INADDR_LOOPBACK); ++ ++ switch (af) { ++ case AF_INET: ++ return initaddr((unsigned char *)&v4loop, sizeof(v4loop), af, dst); ++ break; ++ case AF_INET6: ++ return initaddr((unsigned char *)&v6loop, sizeof(v6loop), af, dst); ++ break; ++ default: ++ return "unknown address family in loopbackaddr"; ++ break; ++ } ++} ++ ++/* ++ - isanyaddr - test for the any-address value ++ */ ++int ++isanyaddr(src) ++const ip_address *src; ++{ ++ uint32_t v4any = htonl(INADDR_ANY); ++ int cmp; ++ ++ switch (src->u.v4.sin_family) { ++ case AF_INET: ++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4any, sizeof(v4any)); ++ break; ++ case AF_INET6: ++ cmp = memcmp(&src->u.v6.sin6_addr, &v6any, sizeof(v6any)); ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ return (cmp == 0) ? 1 : 0; ++} ++ ++/* ++ - isunspecaddr - test for the unspecified-address value ++ */ ++int ++isunspecaddr(src) ++const ip_address *src; ++{ ++ return isanyaddr(src); ++} ++ ++/* ++ - isloopbackaddr - test for the loopback-address value ++ */ ++int ++isloopbackaddr(src) ++const ip_address *src; ++{ ++ uint32_t v4loop = htonl(INADDR_LOOPBACK); ++ int cmp; ++ ++ switch (src->u.v4.sin_family) { ++ case AF_INET: ++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4loop, sizeof(v4loop)); ++ break; ++ case AF_INET6: ++ cmp = memcmp(&src->u.v6.sin6_addr, &v6loop, sizeof(v6loop)); ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ return (cmp == 0) ? 1 : 0; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/datatot.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,232 @@ ++/* ++ * convert from binary data (e.g. key) to text form ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: datatot.c,v 1.5 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++static void convert(const char *src, size_t nreal, int format, char *out); ++ ++/* ++ - datatot - convert data bytes to text ++ */ ++size_t /* true length (with NUL) for success */ ++datatot(src, srclen, format, dst, dstlen) ++const char *src; ++size_t srclen; ++int format; /* character indicating what format */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ size_t inblocksize; /* process this many bytes at a time */ ++ size_t outblocksize; /* producing this many */ ++ size_t breakevery; /* add a _ every this many (0 means don't) */ ++ size_t sincebreak; /* output bytes since last _ */ ++ char breakchar; /* character used to break between groups */ ++ char inblock[10]; /* enough for any format */ ++ char outblock[10]; /* enough for any format */ ++ char fake[1]; /* fake output area for dstlen == 0 */ ++ size_t needed; /* return value */ ++ char *stop; /* where the terminating NUL will go */ ++ size_t ntodo; /* remaining input */ ++ size_t nreal; ++ char *out; ++ char *prefix; ++ ++ breakevery = 0; ++ breakchar = '_'; ++ ++ switch (format) { ++ case 0: ++ case 'h': ++ format = 'x'; ++ breakevery = 8; ++ /* FALLTHROUGH */ ++ case 'x': ++ inblocksize = 1; ++ outblocksize = 2; ++ prefix = "0x"; ++ break; ++ case ':': ++ format = 'x'; ++ breakevery = 2; ++ breakchar = ':'; ++ /* FALLTHROUGH */ ++ case 16: ++ inblocksize = 1; ++ outblocksize = 2; ++ prefix = ""; ++ format = 'x'; ++ break; ++ case 's': ++ inblocksize = 3; ++ outblocksize = 4; ++ prefix = "0s"; ++ break; ++ case 64: /* beware, equals ' ' */ ++ inblocksize = 3; ++ outblocksize = 4; ++ prefix = ""; ++ format = 's'; ++ break; ++ default: ++ return 0; ++ break; ++ } ++ assert(inblocksize < sizeof(inblock)); ++ assert(outblocksize < sizeof(outblock)); ++ assert(breakevery % outblocksize == 0); ++ ++ if (srclen == 0) ++ return 0; ++ ntodo = srclen; ++ ++ if (dstlen == 0) { /* dispose of awkward special case */ ++ dst = fake; ++ dstlen = 1; ++ } ++ stop = dst + dstlen - 1; ++ ++ nreal = strlen(prefix); ++ needed = nreal; /* for starters */ ++ if (dstlen <= nreal) { /* prefix won't fit */ ++ strncpy(dst, prefix, dstlen - 1); ++ dst += dstlen - 1; ++ } else { ++ strcpy(dst, prefix); ++ dst += nreal; ++ } ++ assert(dst <= stop); ++ sincebreak = 0; ++ ++ while (ntodo > 0) { ++ if (ntodo < inblocksize) { /* incomplete input */ ++ memset(inblock, 0, sizeof(inblock)); ++ memcpy(inblock, src, ntodo); ++ src = inblock; ++ nreal = ntodo; ++ ntodo = inblocksize; ++ } else ++ nreal = inblocksize; ++ out = (outblocksize > stop - dst) ? outblock : dst; ++ ++ convert(src, nreal, format, out); ++ needed += outblocksize; ++ sincebreak += outblocksize; ++ if (dst < stop) { ++ if (out != dst) { ++ assert(outblocksize > stop - dst); ++ memcpy(dst, out, stop - dst); ++ dst = stop; ++ } else ++ dst += outblocksize; ++ } ++ ++ src += inblocksize; ++ ntodo -= inblocksize; ++ if (breakevery != 0 && sincebreak >= breakevery && ntodo > 0) { ++ if (dst < stop) ++ *dst++ = breakchar; ++ needed++; ++ sincebreak = 0; ++ } ++ } ++ ++ assert(dst <= stop); ++ *dst++ = '\0'; ++ needed++; ++ ++ return needed; ++} ++ ++/* ++ - convert - convert one input block to one output block ++ */ ++static void ++convert(src, nreal, format, out) ++const char *src; ++size_t nreal; /* how much of the input block is real */ ++int format; ++char *out; ++{ ++ static char hex[] = "0123456789abcdef"; ++ static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ "abcdefghijklmnopqrstuvwxyz" ++ "0123456789+/"; ++ unsigned char c; ++ unsigned char c1, c2, c3; ++ ++ assert(nreal > 0); ++ switch (format) { ++ case 'x': ++ assert(nreal == 1); ++ c = (unsigned char)*src; ++ *out++ = hex[c >> 4]; ++ *out++ = hex[c & 0xf]; ++ break; ++ case 's': ++ c1 = (unsigned char)*src++; ++ c2 = (unsigned char)*src++; ++ c3 = (unsigned char)*src++; ++ *out++ = base64[c1 >> 2]; /* top 6 bits of c1 */ ++ c = (c1 & 0x3) << 4; /* bottom 2 of c1... */ ++ c |= c2 >> 4; /* ...top 4 of c2 */ ++ *out++ = base64[c]; ++ if (nreal == 1) ++ *out++ = '='; ++ else { ++ c = (c2 & 0xf) << 2; /* bottom 4 of c2... */ ++ c |= c3 >> 6; /* ...top 2 of c3 */ ++ *out++ = base64[c]; ++ } ++ if (nreal <= 2) ++ *out++ = '='; ++ else ++ *out++ = base64[c3 & 0x3f]; /* bottom 6 of c3 */ ++ break; ++ default: ++ assert(nreal == 0); /* unknown format */ ++ break; ++ } ++} ++ ++/* ++ - datatoa - convert data to ASCII ++ * backward-compatibility synonym for datatot ++ */ ++size_t /* true length (with NUL) for success */ ++datatoa(src, srclen, format, dst, dstlen) ++const char *src; ++size_t srclen; ++int format; /* character indicating what format */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ return datatot(src, srclen, format, dst, dstlen); ++} ++ ++/* ++ - bytestoa - convert data bytes to ASCII ++ * backward-compatibility synonym for datatot ++ */ ++size_t /* true length (with NUL) for success */ ++bytestoa(src, srclen, format, dst, dstlen) ++const char *src; ++size_t srclen; ++int format; /* character indicating what format */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ return datatot(src, srclen, format, dst, dstlen); ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/goodmask.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,100 @@ ++/* ++ * minor utilities for subnet-mask manipulation ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: goodmask.c,v 1.11 2004/04/11 19:43:48 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++#ifndef ABITS ++#define ABITS 32 /* bits in an IPv4 address */ ++#endif ++ ++/* ++ - goodmask - is this a good (^1*0*$) subnet mask? ++ * You are not expected to understand this. See Henry S. Warren Jr, ++ * "Functions realizable with word-parallel logical and two's-complement ++ * addition instructions", CACM 20.6 (June 1977), p.439. ++ */ ++int /* predicate */ ++goodmask(mask) ++struct in_addr mask; ++{ ++ unsigned long x = ntohl(mask.s_addr); ++ /* clear rightmost contiguous string of 1-bits */ ++# define CRCS1B(x) (((x|(x-1))+1)&x) ++# define TOPBIT (1UL << 31) ++ ++ /* either zero, or has one string of 1-bits which is left-justified */ ++ if (x == 0 || (CRCS1B(x) == 0 && (x&TOPBIT))) ++ return 1; ++ return 0; ++} ++ ++/* ++ - masktobits - how many bits in this mask? ++ * The algorithm is essentially a binary search, but highly optimized ++ * for this particular task. ++ */ ++int /* -1 means !goodmask() */ ++masktobits(mask) ++struct in_addr mask; ++{ ++ unsigned long m = ntohl(mask.s_addr); ++ int masklen; ++ ++ if (!goodmask(mask)) ++ return -1; ++ ++ if (m&0x00000001UL) ++ return 32; ++ masklen = 0; ++ if (m&(0x0000ffffUL<<1)) { /* <<1 for 1-origin numbering */ ++ masklen |= 0x10; ++ m <<= 16; ++ } ++ if (m&(0x00ff0000UL<<1)) { ++ masklen |= 0x08; ++ m <<= 8; ++ } ++ if (m&(0x0f000000UL<<1)) { ++ masklen |= 0x04; ++ m <<= 4; ++ } ++ if (m&(0x30000000UL<<1)) { ++ masklen |= 0x02; ++ m <<= 2; ++ } ++ if (m&(0x40000000UL<<1)) ++ masklen |= 0x01; ++ ++ return masklen; ++} ++ ++/* ++ - bitstomask - return a mask with this many high bits on ++ */ ++struct in_addr ++bitstomask(n) ++int n; ++{ ++ struct in_addr result; ++ ++ if (n > 0 && n <= ABITS) ++ result.s_addr = htonl(~((1UL << (ABITS - n)) - 1)); ++ else if (n == 0) ++ result.s_addr = 0; ++ else ++ result.s_addr = 0; /* best error report we can do */ ++ return result; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/initaddr.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,50 @@ ++/* ++ * initialize address structure ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: initaddr.c,v 1.5 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - initaddr - initialize ip_address from bytes ++ */ ++err_t /* NULL for success, else string literal */ ++initaddr(src, srclen, af, dst) ++const unsigned char *src; ++size_t srclen; ++int af; /* address family */ ++ip_address *dst; ++{ ++ switch (af) { ++ case AF_INET: ++ if (srclen != 4) ++ return "IPv4 address must be exactly 4 bytes"; ++ dst->u.v4.sin_family = af; ++ dst->u.v4.sin_port = 0; /* unused */ ++ memcpy((char *)&dst->u.v4.sin_addr.s_addr, src, srclen); ++ break; ++ case AF_INET6: ++ if (srclen != 16) ++ return "IPv6 address must be exactly 16 bytes"; ++ dst->u.v6.sin6_family = af; ++ dst->u.v6.sin6_flowinfo = 0; /* unused */ ++ dst->u.v6.sin6_port = 0; /* unused */ ++ memcpy((char *)&dst->u.v6.sin6_addr, src, srclen); ++ break; ++ default: ++ return "unknown address family in initaddr"; ++ break; ++ } ++ return NULL; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/pfkey_v2_build.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1559 @@ ++/* ++ * RFC2367 PF_KEYv2 Key management API message parser ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_build.c,v 1.49 2004/04/12 02:59:06 mcr Exp $ ++ */ ++ ++/* ++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c. ++ */ ++ ++char pfkey_v2_build_c_version[] = "$Id: pfkey_v2_build.c,v 1.49 2004/04/12 02:59:06 mcr Exp $"; ++ ++/* ++ * Some ugly stuff to allow consistent debugging code for use in the ++ * kernel and in user space ++*/ ++ ++#ifdef __KERNEL__ ++ ++# include /* for printk */ ++ ++# include "openswan/ipsec_kversion.h" /* for malloc switch */ ++# ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++# else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++# endif /* MALLOC_SLAB */ ++# include /* error codes */ ++# include /* size_t */ ++# include /* mark_bh */ ++ ++# include /* struct device, and other headers */ ++# include /* eth_type_trans */ ++# include /* struct iphdr */ ++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++# include /* struct ipv6hdr */ ++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ ++ ++# define MALLOC(size) kmalloc(size, GFP_ATOMIC) ++# define FREE(obj) kfree(obj) ++# include ++#else /* __KERNEL__ */ ++ ++# include ++# include ++# include ++# include ++# include /* memset */ ++ ++# include ++ ++#endif /* __KERNEL__ */ ++ ++#include ++#include ++ ++#ifdef __KERNEL__ ++#include "openswan/radij.h" /* rd_nodes */ ++#include "openswan/ipsec_encap.h" /* sockaddr_encap */ ++#endif /* __KERNEL__ */ ++ ++ ++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ ++#include "openswan/pfkey_debug.h" ++ ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++void ++pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]) ++{ ++ int i; ++ ++ for (i = 0; i != SADB_EXT_MAX + 1; i++) { ++ extensions[i] = NULL; ++ } ++} ++ ++void ++pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]) ++{ ++ int i; ++ ++ if(!extensions) { ++ return; ++ } ++ ++ if(extensions[0]) { ++ memset(extensions[0], 0, sizeof(struct sadb_msg)); ++ FREE(extensions[0]); ++ extensions[0] = NULL; ++ } ++ ++ for (i = 1; i != SADB_EXT_MAX + 1; i++) { ++ if(extensions[i]) { ++ memset(extensions[i], 0, extensions[i]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); ++ FREE(extensions[i]); ++ extensions[i] = NULL; ++ } ++ } ++} ++ ++void ++pfkey_msg_free(struct sadb_msg **pfkey_msg) ++{ ++ if(*pfkey_msg) { ++ memset(*pfkey_msg, 0, (*pfkey_msg)->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); ++ FREE(*pfkey_msg); ++ *pfkey_msg = NULL; ++ } ++} ++ ++/* Default extension builders taken from the KLIPS code */ ++ ++int ++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext, ++ uint8_t msg_type, ++ uint8_t satype, ++ uint8_t msg_errno, ++ uint32_t seq, ++ uint32_t pid) ++{ ++ int error = 0; ++ struct sadb_msg *pfkey_msg = (struct sadb_msg *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build:\n"); ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "on_entry &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n", ++ &pfkey_ext, ++ pfkey_ext, ++ *pfkey_ext); ++ /* sanity checks... */ ++ if(pfkey_msg) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "why is pfkey_msg already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(!msg_type) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "msg type not set, must be non-zero..\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(msg_type > SADB_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "msg type too large:%d.\n", ++ msg_type); ++ SENDERR(EINVAL); ++ } ++ ++ if(satype > SADB_SATYPE_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "satype %d > max %d\n", ++ satype, SADB_SATYPE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_msg = (struct sadb_msg*) ++ MALLOC(sizeof(struct sadb_msg)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_msg, 0, sizeof(struct sadb_msg)); ++ ++ pfkey_msg->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_msg->sadb_msg_type = msg_type; ++ pfkey_msg->sadb_msg_satype = satype; ++ ++ pfkey_msg->sadb_msg_version = PF_KEY_V2; ++ pfkey_msg->sadb_msg_errno = msg_errno; ++ pfkey_msg->sadb_msg_reserved = 0; ++ pfkey_msg->sadb_msg_seq = seq; ++ pfkey_msg->sadb_msg_pid = pid; ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_hdr_build: " ++ "on_exit &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n", ++ &pfkey_ext, ++ pfkey_ext, ++ *pfkey_ext); ++errlab: ++ return error; ++} ++ ++int ++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t spi, ++ uint8_t replay_window, ++ uint8_t sa_state, ++ uint8_t auth, ++ uint8_t encrypt, ++ uint32_t flags, ++ uint32_t/*IPsecSAref_t*/ ref) ++{ ++ int error = 0; ++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "spi=%08x replay=%d sa_state=%d auth=%d encrypt=%d flags=%d\n", ++ ntohl(spi), /* in network order */ ++ replay_window, ++ sa_state, ++ auth, ++ encrypt, ++ flags); ++ /* sanity checks... */ ++ if(pfkey_sa) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "why is pfkey_sa already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(exttype != SADB_EXT_SA && ++ exttype != SADB_X_EXT_SA2) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "invalid exttype=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ if(replay_window > 64) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "replay window size: %d -- must be 0 <= size <= 64\n", ++ replay_window); ++ SENDERR(EINVAL); ++ } ++ ++ if(auth > SADB_AALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "auth=%d > SADB_AALG_MAX=%d.\n", ++ auth, ++ SADB_AALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(encrypt > SADB_EALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "encrypt=%d > SADB_EALG_MAX=%d.\n", ++ encrypt, ++ SADB_EALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(sa_state > SADB_SASTATE_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "sa_state=%d exceeds MAX=%d.\n", ++ sa_state, ++ SADB_SASTATE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(sa_state == SADB_SASTATE_DEAD) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "sa_state=%d is DEAD=%d is not allowed.\n", ++ sa_state, ++ SADB_SASTATE_DEAD); ++ SENDERR(EINVAL); ++ } ++ ++ if((IPSEC_SAREF_NULL != ref) && (ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", ++ ref, ++ IPSEC_SAREF_NULL, ++ IPSEC_SA_REF_TABLE_NUM_ENTRIES); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_sa = (struct sadb_sa*) ++ MALLOC(sizeof(struct sadb_sa)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sa_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_sa, 0, sizeof(struct sadb_sa)); ++ ++ pfkey_sa->sadb_sa_len = sizeof(*pfkey_sa) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_sa->sadb_sa_exttype = exttype; ++ pfkey_sa->sadb_sa_spi = spi; ++ pfkey_sa->sadb_sa_replay = replay_window; ++ pfkey_sa->sadb_sa_state = sa_state; ++ pfkey_sa->sadb_sa_auth = auth; ++ pfkey_sa->sadb_sa_encrypt = encrypt; ++ pfkey_sa->sadb_sa_flags = flags; ++ pfkey_sa->sadb_x_sa_ref = ref; ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_sa_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t spi, ++ uint8_t replay_window, ++ uint8_t sa_state, ++ uint8_t auth, ++ uint8_t encrypt, ++ uint32_t flags) ++{ ++ return pfkey_sa_ref_build(pfkey_ext, ++ exttype, ++ spi, ++ replay_window, ++ sa_state, ++ auth, ++ encrypt, ++ flags, ++ IPSEC_SAREF_NULL); ++} ++ ++int ++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext, ++ uint16_t exttype, ++ uint32_t allocations, ++ uint64_t bytes, ++ uint64_t addtime, ++ uint64_t usetime, ++ uint32_t packets) ++{ ++ int error = 0; ++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_lifetime_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_lifetime) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_lifetime_build: " ++ "why is pfkey_lifetime already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(exttype != SADB_EXT_LIFETIME_CURRENT && ++ exttype != SADB_EXT_LIFETIME_HARD && ++ exttype != SADB_EXT_LIFETIME_SOFT) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_lifetime_build: " ++ "invalid exttype=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_lifetime = (struct sadb_lifetime*) ++ MALLOC(sizeof(struct sadb_lifetime)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_lifetime_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_lifetime, 0, sizeof(struct sadb_lifetime)); ++ ++ pfkey_lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_lifetime->sadb_lifetime_exttype = exttype; ++ pfkey_lifetime->sadb_lifetime_allocations = allocations; ++ pfkey_lifetime->sadb_lifetime_bytes = bytes; ++ pfkey_lifetime->sadb_lifetime_addtime = addtime; ++ pfkey_lifetime->sadb_lifetime_usetime = usetime; ++ pfkey_lifetime->sadb_x_lifetime_packets = packets; ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_address_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint8_t proto, ++ uint8_t prefixlen, ++ struct sockaddr* address) ++{ ++ int error = 0; ++ int saddr_len = 0; ++ char ipaddr_txt[ADDRTOT_BUF + 6/*extra for port number*/]; ++ struct sadb_address *pfkey_address = (struct sadb_address *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_address_build: " ++ "exttype=%d proto=%d prefixlen=%d\n", ++ exttype, ++ proto, ++ prefixlen); ++ /* sanity checks... */ ++ if(pfkey_address) { ++ ERROR("pfkey_address_build: " ++ "why is pfkey_address already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if (!address) { ++ ERROR("pfkey_address_build: " "address is NULL\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(exttype) { ++ case SADB_EXT_ADDRESS_SRC: ++ case SADB_EXT_ADDRESS_DST: ++ case SADB_EXT_ADDRESS_PROXY: ++ case SADB_X_EXT_ADDRESS_DST2: ++ case SADB_X_EXT_ADDRESS_SRC_FLOW: ++ case SADB_X_EXT_ADDRESS_DST_FLOW: ++ case SADB_X_EXT_ADDRESS_SRC_MASK: ++ case SADB_X_EXT_ADDRESS_DST_MASK: ++#ifdef NAT_TRAVERSAL ++ case SADB_X_EXT_NAT_T_OA: ++#endif ++ break; ++ default: ++ ERROR("pfkey_address_build: " ++ "unrecognised ext_type=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ switch(address->sa_family) { ++ case AF_INET: ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_address_build: " ++ "found address family AF_INET.\n"); ++ saddr_len = sizeof(struct sockaddr_in); ++ sprintf(ipaddr_txt, "%d.%d.%d.%d:%d" ++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 0) & 0xFF ++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 8) & 0xFF ++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 16) & 0xFF ++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 24) & 0xFF ++ , ntohs(((struct sockaddr_in*)address)->sin_port)); ++ break; ++ case AF_INET6: ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_address_build: " ++ "found address family AF_INET6.\n"); ++ saddr_len = sizeof(struct sockaddr_in6); ++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x-%x" ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[0]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[1]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[2]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[3]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[4]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[5]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[6]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[7]) ++ , ntohs(((struct sockaddr_in6*)address)->sin6_port)); ++ break; ++ default: ++ ERROR("pfkey_address_build: " ++ "address->sa_family=%d not supported.\n", ++ address->sa_family); ++ SENDERR(EPFNOSUPPORT); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_address_build: " ++ "found address=%s.\n", ++ ipaddr_txt); ++ if(prefixlen != 0) { ++ ERROR("pfkey_address_build: " ++ "address prefixes not supported yet.\n"); ++ SENDERR(EAFNOSUPPORT); /* not supported yet */ ++ } ++ ++ /* allocate some memory for the extension */ ++ pfkey_address = (struct sadb_address*) ++ MALLOC(ALIGN_N(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)); ++ *pfkey_ext = (struct sadb_ext*)pfkey_address; ++ ++ if(pfkey_address == NULL ) { ++ ERROR("pfkey_lifetime_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_address, ++ 0, ++ ALIGN_N(sizeof(struct sadb_address) + saddr_len, ++ IPSEC_PFKEYv2_ALIGN)); ++ ++ pfkey_address->sadb_address_len = DIVUP(sizeof(struct sadb_address) + saddr_len, ++ IPSEC_PFKEYv2_ALIGN); ++ ++ pfkey_address->sadb_address_exttype = exttype; ++ pfkey_address->sadb_address_proto = proto; ++ pfkey_address->sadb_address_prefixlen = prefixlen; ++ pfkey_address->sadb_address_reserved = 0; ++ ++ memcpy((char*)pfkey_address + sizeof(struct sadb_address), ++ address, ++ saddr_len); ++ ++#if 0 ++ for(i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) { ++ pfkey_address_s_ska.sin_zero[i] = 0; ++ } ++#endif ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_address_build: " ++ "successful created len: %d.\n", pfkey_address->sadb_address_len); ++ ++ errlab: ++ return error; ++} ++ ++int ++pfkey_key_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t key_bits, ++ char* key) ++{ ++ int error = 0; ++ struct sadb_key *pfkey_key = (struct sadb_key *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_key_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_key) { ++ ERROR("pfkey_key_build: " ++ "why is pfkey_key already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(!key_bits) { ++ ERROR("pfkey_key_build: " ++ "key_bits is zero, it must be non-zero.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if( !((exttype == SADB_EXT_KEY_AUTH) || (exttype == SADB_EXT_KEY_ENCRYPT))) { ++ ERROR("pfkey_key_build: " ++ "unsupported extension type=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_key = (struct sadb_key*) ++ MALLOC(sizeof(struct sadb_key) + ++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN))) { ++ ERROR("pfkey_key_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_key, ++ 0, ++ sizeof(struct sadb_key) + ++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN); ++ ++ pfkey_key->sadb_key_len = DIVUP(sizeof(struct sadb_key) * IPSEC_PFKEYv2_ALIGN + key_bits, ++ 64); ++ pfkey_key->sadb_key_exttype = exttype; ++ pfkey_key->sadb_key_bits = key_bits; ++ pfkey_key->sadb_key_reserved = 0; ++ memcpy((char*)pfkey_key + sizeof(struct sadb_key), ++ key, ++ DIVUP(key_bits, 8)); ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_ident_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t ident_type, ++ uint64_t ident_id, ++ uint8_t ident_len, ++ char* ident_string) ++{ ++ int error = 0; ++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)*pfkey_ext; ++ int data_len = ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_ident_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_ident) { ++ ERROR("pfkey_ident_build: " ++ "why is pfkey_ident already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if( ! ((exttype == SADB_EXT_IDENTITY_SRC) || ++ (exttype == SADB_EXT_IDENTITY_DST))) { ++ ERROR("pfkey_ident_build: " ++ "unsupported extension type=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ if((ident_type == SADB_IDENTTYPE_RESERVED)) { ++ ERROR("pfkey_ident_build: " ++ "ident_type must be non-zero.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(ident_type > SADB_IDENTTYPE_MAX) { ++ ERROR("pfkey_ident_build: " ++ "identtype=%d out of range.\n", ++ ident_type); ++ SENDERR(EINVAL); ++ } ++ ++ if(((ident_type == SADB_IDENTTYPE_PREFIX) || ++ (ident_type == SADB_IDENTTYPE_FQDN)) && ++ !ident_string) { ++ ERROR("pfkey_ident_build: " ++ "string required to allocate size of extension.\n"); ++ SENDERR(EINVAL); ++ } ++ ++#if 0 ++ if((ident_type == SADB_IDENTTYPE_USERFQDN) ) { ++ } ++#endif ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_ident = (struct sadb_ident*) ++ MALLOC(ident_len * IPSEC_PFKEYv2_ALIGN))) { ++ ERROR("pfkey_ident_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_ident, 0, ident_len * IPSEC_PFKEYv2_ALIGN); ++ ++ pfkey_ident->sadb_ident_len = ident_len; ++ pfkey_ident->sadb_ident_exttype = exttype; ++ pfkey_ident->sadb_ident_type = ident_type; ++ pfkey_ident->sadb_ident_reserved = 0; ++ pfkey_ident->sadb_ident_id = ident_id; ++ memcpy((char*)pfkey_ident + sizeof(struct sadb_ident), ++ ident_string, ++ data_len); ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_sens_build(struct sadb_ext** pfkey_ext, ++ uint32_t dpd, ++ uint8_t sens_level, ++ uint8_t sens_len, ++ uint64_t* sens_bitmap, ++ uint8_t integ_level, ++ uint8_t integ_len, ++ uint64_t* integ_bitmap) ++{ ++ int error = 0; ++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)*pfkey_ext; ++ int i; ++ uint64_t* bitmap; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sens_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_sens) { ++ ERROR("pfkey_sens_build: " ++ "why is pfkey_sens already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_sens_build: " ++ "Sorry, I can't build exttype=%d yet.\n", ++ (*pfkey_ext)->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_sens = (struct sadb_sens*) ++ MALLOC(sizeof(struct sadb_sens) + ++ (sens_len + integ_len) * sizeof(uint64_t)))) { ++ ERROR("pfkey_sens_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_sens, ++ 0, ++ sizeof(struct sadb_sens) + ++ (sens_len + integ_len) * sizeof(uint64_t)); ++ ++ pfkey_sens->sadb_sens_len = (sizeof(struct sadb_sens) + ++ (sens_len + integ_len) * sizeof(uint64_t)) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_sens->sadb_sens_exttype = SADB_EXT_SENSITIVITY; ++ pfkey_sens->sadb_sens_dpd = dpd; ++ pfkey_sens->sadb_sens_sens_level = sens_level; ++ pfkey_sens->sadb_sens_sens_len = sens_len; ++ pfkey_sens->sadb_sens_integ_level = integ_level; ++ pfkey_sens->sadb_sens_integ_len = integ_len; ++ pfkey_sens->sadb_sens_reserved = 0; ++ ++ bitmap = (uint64_t*)((char*)pfkey_ext + sizeof(struct sadb_sens)); ++ for(i = 0; i < sens_len; i++) { ++ *bitmap = sens_bitmap[i]; ++ bitmap++; ++ } ++ for(i = 0; i < integ_len; i++) { ++ *bitmap = integ_bitmap[i]; ++ bitmap++; ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_prop_build(struct sadb_ext** pfkey_ext, ++ uint8_t replay, ++ unsigned int comb_num, ++ struct sadb_comb* comb) ++{ ++ int error = 0; ++ int i; ++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)*pfkey_ext; ++ struct sadb_comb *combp; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_prop_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_prop) { ++ ERROR("pfkey_prop_build: " ++ "why is pfkey_prop already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_prop = (struct sadb_prop*) ++ MALLOC(sizeof(struct sadb_prop) + ++ comb_num * sizeof(struct sadb_comb)))) { ++ ERROR("pfkey_prop_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_prop, ++ 0, ++ sizeof(struct sadb_prop) + ++ comb_num * sizeof(struct sadb_comb)); ++ ++ pfkey_prop->sadb_prop_len = (sizeof(struct sadb_prop) + ++ comb_num * sizeof(struct sadb_comb)) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; ++ pfkey_prop->sadb_prop_replay = replay; ++ ++ for(i=0; i<3; i++) { ++ pfkey_prop->sadb_prop_reserved[i] = 0; ++ } ++ ++ combp = (struct sadb_comb*)((char*)*pfkey_ext + sizeof(struct sadb_prop)); ++ for(i = 0; i < comb_num; i++) { ++ memcpy (combp, &(comb[i]), sizeof(struct sadb_comb)); ++ combp++; ++ } ++ ++#if 0 ++ uint8_t sadb_comb_auth; ++ uint8_t sadb_comb_encrypt; ++ uint16_t sadb_comb_flags; ++ uint16_t sadb_comb_auth_minbits; ++ uint16_t sadb_comb_auth_maxbits; ++ uint16_t sadb_comb_encrypt_minbits; ++ uint16_t sadb_comb_encrypt_maxbits; ++ uint32_t sadb_comb_reserved; ++ uint32_t sadb_comb_soft_allocations; ++ uint32_t sadb_comb_hard_allocations; ++ uint64_t sadb_comb_soft_bytes; ++ uint64_t sadb_comb_hard_bytes; ++ uint64_t sadb_comb_soft_addtime; ++ uint64_t sadb_comb_hard_addtime; ++ uint64_t sadb_comb_soft_usetime; ++ uint64_t sadb_comb_hard_usetime; ++ uint32_t sadb_comb_soft_packets; ++ uint32_t sadb_comb_hard_packets; ++#endif ++errlab: ++ return error; ++} ++ ++int ++pfkey_supported_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ unsigned int alg_num, ++ struct sadb_alg* alg) ++{ ++ int error = 0; ++ unsigned int i; ++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)*pfkey_ext; ++ struct sadb_alg *pfkey_alg; ++ ++ /* sanity checks... */ ++ if(pfkey_supported) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_supported_build: " ++ "why is pfkey_supported already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if( !((exttype == SADB_EXT_SUPPORTED_AUTH) || (exttype == SADB_EXT_SUPPORTED_ENCRYPT))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_supported_build: " ++ "unsupported extension type=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_supported = (struct sadb_supported*) ++ MALLOC(sizeof(struct sadb_supported) + ++ alg_num * ++ sizeof(struct sadb_alg)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_supported_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_supported, ++ 0, ++ sizeof(struct sadb_supported) + ++ alg_num * ++ sizeof(struct sadb_alg)); ++ ++ pfkey_supported->sadb_supported_len = (sizeof(struct sadb_supported) + ++ alg_num * ++ sizeof(struct sadb_alg)) / ++ IPSEC_PFKEYv2_ALIGN; ++ pfkey_supported->sadb_supported_exttype = exttype; ++ pfkey_supported->sadb_supported_reserved = 0; ++ ++ pfkey_alg = (struct sadb_alg*)((char*)pfkey_supported + sizeof(struct sadb_supported)); ++ for(i = 0; i < alg_num; i++) { ++ memcpy (pfkey_alg, &(alg[i]), sizeof(struct sadb_alg)); ++ pfkey_alg->sadb_alg_reserved = 0; ++ pfkey_alg++; ++ } ++ ++#if 0 ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_supported_build: " ++ "Sorry, I can't build exttype=%d yet.\n", ++ (*pfkey_ext)->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++ uint8_t sadb_alg_id; ++ uint8_t sadb_alg_ivlen; ++ uint16_t sadb_alg_minbits; ++ uint16_t sadb_alg_maxbits; ++ uint16_t sadb_alg_reserved; ++#endif ++errlab: ++ return error; ++} ++ ++int ++pfkey_spirange_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint32_t min, /* in network order */ ++ uint32_t max) /* in network order */ ++{ ++ int error = 0; ++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)*pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_spirange) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_spirange_build: " ++ "why is pfkey_spirange already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(ntohl(max) < ntohl(min)) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_spirange_build: " ++ "minspi=%08x must be < maxspi=%08x.\n", ++ ntohl(min), ++ ntohl(max)); ++ SENDERR(EINVAL); ++ } ++ ++ if(ntohl(min) <= 255) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_spirange_build: " ++ "minspi=%08x must be > 255.\n", ++ ntohl(min)); ++ SENDERR(EEXIST); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_spirange = (struct sadb_spirange*) ++ MALLOC(sizeof(struct sadb_spirange)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_spirange_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_spirange, ++ 0, ++ sizeof(struct sadb_spirange)); ++ ++ pfkey_spirange->sadb_spirange_len = sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_spirange->sadb_spirange_exttype = SADB_EXT_SPIRANGE; ++ pfkey_spirange->sadb_spirange_min = min; ++ pfkey_spirange->sadb_spirange_max = max; ++ pfkey_spirange->sadb_spirange_reserved = 0; ++ errlab: ++ return error; ++} ++ ++int ++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)*pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_x_kmprivate) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_kmprivate_build: " ++ "why is pfkey_x_kmprivate already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_kmprivate_build: " ++ "Sorry, I can't build exttype=%d yet.\n", ++ (*pfkey_ext)->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++ if(!(*pfkey_ext = (struct sadb_ext*) ++ pfkey_x_kmprivate = (struct sadb_x_kmprivate*) ++ MALLOC(sizeof(struct sadb_x_kmprivate)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_kmprivate_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_x_kmprivate, ++ 0, ++ sizeof(struct sadb_x_kmprivate)); ++ ++ pfkey_x_kmprivate->sadb_x_kmprivate_len = ++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_x_kmprivate->sadb_x_kmprivate_exttype = SADB_X_EXT_KMPRIVATE; ++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0; ++errlab: ++ return error; ++} ++ ++int ++pfkey_x_satype_build(struct sadb_ext** pfkey_ext, ++ uint8_t satype) ++{ ++ int error = 0; ++ int i; ++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_satype_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_x_satype) { ++ ERROR("pfkey_x_satype_build: " ++ "why is pfkey_x_satype already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(!satype) { ++ ERROR("pfkey_x_satype_build: " ++ "SA type not set, must be non-zero.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(satype > SADB_SATYPE_MAX) { ++ ERROR("pfkey_x_satype_build: " ++ "satype %d > max %d\n", ++ satype, SADB_SATYPE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_satype = (struct sadb_x_satype*) ++ MALLOC(sizeof(struct sadb_x_satype)))) { ++ ERROR("pfkey_x_satype_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ memset(pfkey_x_satype, ++ 0, ++ sizeof(struct sadb_x_satype)); ++ ++ pfkey_x_satype->sadb_x_satype_len = sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_x_satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2; ++ pfkey_x_satype->sadb_x_satype_satype = satype; ++ for(i=0; i<3; i++) { ++ pfkey_x_satype->sadb_x_satype_reserved[i] = 0; ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_x_debug_build(struct sadb_ext** pfkey_ext, ++ uint32_t tunnel, ++ uint32_t netlink, ++ uint32_t xform, ++ uint32_t eroute, ++ uint32_t spi, ++ uint32_t radij, ++ uint32_t esp, ++ uint32_t ah, ++ uint32_t rcv, ++ uint32_t pfkey, ++ uint32_t ipcomp, ++ uint32_t verbose) ++{ ++ int error = 0; ++ int i; ++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_debug_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_x_debug) { ++ ERROR("pfkey_x_debug_build: " ++ "why is pfkey_x_debug already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_debug_build: " ++ "tunnel=%x netlink=%x xform=%x eroute=%x spi=%x radij=%x esp=%x ah=%x rcv=%x pfkey=%x ipcomp=%x verbose=%x?\n", ++ tunnel, netlink, xform, eroute, spi, radij, esp, ah, rcv, pfkey, ipcomp, verbose); ++ ++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_debug = (struct sadb_x_debug*) ++ MALLOC(sizeof(struct sadb_x_debug)))) { ++ ERROR("pfkey_x_debug_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++#if 0 ++ memset(pfkey_x_debug, ++ 0, ++ sizeof(struct sadb_x_debug)); ++#endif ++ ++ pfkey_x_debug->sadb_x_debug_len = sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_x_debug->sadb_x_debug_exttype = SADB_X_EXT_DEBUG; ++ ++ pfkey_x_debug->sadb_x_debug_tunnel = tunnel; ++ pfkey_x_debug->sadb_x_debug_netlink = netlink; ++ pfkey_x_debug->sadb_x_debug_xform = xform; ++ pfkey_x_debug->sadb_x_debug_eroute = eroute; ++ pfkey_x_debug->sadb_x_debug_spi = spi; ++ pfkey_x_debug->sadb_x_debug_radij = radij; ++ pfkey_x_debug->sadb_x_debug_esp = esp; ++ pfkey_x_debug->sadb_x_debug_ah = ah; ++ pfkey_x_debug->sadb_x_debug_rcv = rcv; ++ pfkey_x_debug->sadb_x_debug_pfkey = pfkey; ++ pfkey_x_debug->sadb_x_debug_ipcomp = ipcomp; ++ pfkey_x_debug->sadb_x_debug_verbose = verbose; ++ ++ for(i=0; i<4; i++) { ++ pfkey_x_debug->sadb_x_debug_reserved[i] = 0; ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext, ++ uint8_t type) ++{ ++ int error = 0; ++ int i; ++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_type_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_x_nat_t_type) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_type_build: " ++ "why is pfkey_x_nat_t_type already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_type_build: " ++ "type=%d\n", type); ++ ++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_type = (struct sadb_x_nat_t_type*) ++ MALLOC(sizeof(struct sadb_x_nat_t_type)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_type_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_len = sizeof(struct sadb_x_nat_t_type) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type = type; ++ for(i=0; i<3; i++) { ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_reserved[i] = 0; ++ } ++ ++errlab: ++ return error; ++} ++int ++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext, ++ uint16_t exttype, ++ uint16_t port) ++{ ++ int error = 0; ++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)*pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_port_build:\n"); ++ /* sanity checks... */ ++ if(pfkey_x_nat_t_port) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_port_build: " ++ "why is pfkey_x_nat_t_port already pointing to something?\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(exttype) { ++ case SADB_X_EXT_NAT_T_SPORT: ++ case SADB_X_EXT_NAT_T_DPORT: ++ break; ++ default: ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_nat_t_port_build: " ++ "unrecognised ext_type=%d.\n", ++ exttype); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_port_build: " ++ "ext=%d, port=%d\n", exttype, port); ++ ++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_port = (struct sadb_x_nat_t_port*) ++ MALLOC(sizeof(struct sadb_x_nat_t_port)))) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_x_nat_t_port_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_len = sizeof(struct sadb_x_nat_t_port) / IPSEC_PFKEYv2_ALIGN; ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype = exttype; ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port = port; ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_reserved = 0; ++ ++errlab: ++ return error; ++} ++ ++int pfkey_x_protocol_build(struct sadb_ext **pfkey_ext, ++ uint8_t protocol) ++{ ++ int error = 0; ++ struct sadb_protocol * p = (struct sadb_protocol *)*pfkey_ext; ++ DEBUGGING(PF_KEY_DEBUG_BUILD,"pfkey_x_protocol_build: protocol=%u\n", protocol); ++ /* sanity checks... */ ++ if (p != 0) { ++ ERROR("pfkey_x_protocol_build: bogus protocol pointer\n"); ++ SENDERR(EINVAL); ++ } ++ if ((p = (struct sadb_protocol*)MALLOC(sizeof(*p))) == 0) { ++ ERROR("pfkey_build: memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ *pfkey_ext = (struct sadb_ext *)p; ++ p->sadb_protocol_len = sizeof(*p) / sizeof(uint64_t); ++ p->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL; ++ p->sadb_protocol_proto = protocol; ++ p->sadb_protocol_flags = 0; ++ p->sadb_protocol_reserved2 = 0; ++ errlab: ++ return error; ++} ++ ++ ++#if I_DONT_THINK_THIS_WILL_BE_USEFUL ++int (*ext_default_builders[SADB_EXT_MAX +1])(struct sadb_msg*, struct sadb_ext*) ++ = ++{ ++ NULL, /* pfkey_msg_build, */ ++ pfkey_sa_build, ++ pfkey_lifetime_build, ++ pfkey_lifetime_build, ++ pfkey_lifetime_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_key_build, ++ pfkey_key_build, ++ pfkey_ident_build, ++ pfkey_ident_build, ++ pfkey_sens_build, ++ pfkey_prop_build, ++ pfkey_supported_build, ++ pfkey_supported_build, ++ pfkey_spirange_build, ++ pfkey_x_kmprivate_build, ++ pfkey_x_satype_build, ++ pfkey_sa_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_address_build, ++ pfkey_x_ext_debug_build ++}; ++#endif ++ ++int ++pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext *extensions[], int dir) ++{ ++ int error = 0; ++ unsigned ext; ++ unsigned total_size; ++ struct sadb_ext *pfkey_ext; ++ int extensions_seen = 0; ++#ifndef __KERNEL__ ++ struct sadb_ext *extensions_check[SADB_EXT_MAX + 1]; ++#endif ++ ++ if(!extensions[0]) { ++ ERROR("pfkey_msg_build: " ++ "extensions[0] must be specified (struct sadb_msg).\n"); ++ SENDERR(EINVAL); ++ } ++ ++ /* figure out the total size for all the requested extensions */ ++ total_size = IPSEC_PFKEYv2_WORDS(sizeof(struct sadb_msg)); ++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) { ++ if(extensions[ext]) { ++ total_size += (extensions[ext])->sadb_ext_len; ++ } ++ } ++ ++ /* allocate that much space */ ++ *pfkey_msg = (struct sadb_msg*)MALLOC(total_size * IPSEC_PFKEYv2_ALIGN); ++ if(*pfkey_msg == NULL) { ++ ERROR("pfkey_msg_build: " ++ "memory allocation failed\n"); ++ SENDERR(ENOMEM); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_build: " ++ "pfkey_msg=0p%p allocated %lu bytes, &(extensions[0])=0p%p\n", ++ *pfkey_msg, ++ (unsigned long)(total_size * IPSEC_PFKEYv2_ALIGN), ++ &(extensions[0])); ++ ++ memcpy(*pfkey_msg, ++ extensions[0], ++ sizeof(struct sadb_msg)); ++ (*pfkey_msg)->sadb_msg_len = total_size; ++ (*pfkey_msg)->sadb_msg_reserved = 0; ++ extensions_seen = 1 ; ++ ++ /* ++ * point pfkey_ext to immediately after the space for the header, ++ * i.e. at the first extension location. ++ */ ++ pfkey_ext = (struct sadb_ext*)(((char*)(*pfkey_msg)) + sizeof(struct sadb_msg)); ++ ++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) { ++ /* copy from extension[ext] to buffer */ ++ if(extensions[ext]) { ++ /* Is this type of extension permitted for this type of message? */ ++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type] & ++ 1<sadb_msg_type], ++ 1<sadb_ext_len * IPSEC_PFKEYv2_ALIGN), ++ ext, ++ extensions[ext]->sadb_ext_type); ++ ++ memcpy(pfkey_ext, ++ extensions[ext], ++ (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); ++ ((char*)pfkey_ext) += (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN; ++ /* Mark that we have seen this extension and remember the header location */ ++ extensions_seen |= ( 1 << ext ); ++ } ++ } ++ ++ /* check required extensions */ ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_build: " ++ "extensions permitted=%08x, seen=%08x, required=%08x.\n", ++ extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type], ++ extensions_seen, ++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]); ++ ++ if((extensions_seen & ++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) != ++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) { ++ DEBUGGING(PF_KEY_DEBUG_BUILD, ++ "pfkey_msg_build: " ++ "required extensions missing:%08x.\n", ++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type] - ++ (extensions_seen & ++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) ); ++ SENDERR(EINVAL); ++ } ++ ++#ifndef __KERNEL__ ++/* ++ * this is silly, there is no need to reparse the message that we just built. ++ * ++ */ ++ if((error = pfkey_msg_parse(*pfkey_msg, NULL, extensions_check, dir))) { ++ ERROR( ++ "pfkey_msg_build: " ++ "Trouble parsing newly built pfkey message, error=%d.\n", ++ error); ++ SENDERR(-error); ++ } ++#endif ++ ++errlab: ++ ++ return error; ++} ++ ++/* ++ * $Log: pfkey_v2_build.c,v $ ++ * Revision 1.49 2004/04/12 02:59:06 mcr ++ * erroneously moved pfkey_v2_build.c ++ * ++ * Revision 1.48 2004/04/09 18:00:40 mcr ++ * Moved from linux/lib/libfreeswan/pfkey_v2_build.c,v ++ * ++ * Revision 1.47 2004/03/08 01:59:08 ken ++ * freeswan.h -> openswan.h ++ * ++ * Revision 1.46 2003/12/10 01:20:19 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.45 2003/12/04 23:01:12 mcr ++ * removed ipsec_netlink.h ++ * ++ * Revision 1.44 2003/10/31 02:27:12 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.43.4.2 2003/10/29 01:11:32 mcr ++ * added debugging for pfkey library. ++ * ++ * Revision 1.43.4.1 2003/09/21 13:59:44 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.43 2003/05/07 17:29:17 mcr ++ * new function pfkey_debug_func added for us in debugging from ++ * pfkey library. ++ * ++ * Revision 1.42 2003/01/30 02:32:09 rgb ++ * ++ * Rename SAref table macro names for clarity. ++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. ++ * ++ * Revision 1.41 2002/12/13 18:16:02 mcr ++ * restored sa_ref code ++ * ++ * Revision 1.40 2002/12/13 18:06:52 mcr ++ * temporarily removed sadb_x_sa_ref reference for 2.xx ++ * ++ * Revision 1.39 2002/12/13 17:43:28 mcr ++ * commented out access to sadb_x_sa_ref for 2.xx branch ++ * ++ * Revision 1.38 2002/10/09 03:12:05 dhr ++ * ++ * [kenb+dhr] 64-bit fixes ++ * ++ * Revision 1.37 2002/09/20 15:40:39 rgb ++ * Added new function pfkey_sa_ref_build() to accomodate saref parameter. ++ * ++ * Revision 1.36 2002/09/20 05:01:22 rgb ++ * Generalise for platform independance: fix (ia64) using unsigned for sizes. ++ * ++ * Revision 1.35 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.34 2002/05/23 07:14:11 rgb ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.33 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.32 2002/04/24 07:36:40 mcr ++ * Moved from ./lib/pfkey_v2_build.c,v ++ * ++ * Revision 1.31 2002/01/29 22:25:35 rgb ++ * Re-add ipsec_kversion.h to keep MALLOC happy. ++ * ++ * Revision 1.30 2002/01/29 01:59:09 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from openswan.h that also duplicated kversions.h ++ * code. ++ * ++ * Revision 1.29 2001/12/19 21:06:09 rgb ++ * Added port numbers to pfkey_address_build() debugging. ++ * ++ * Revision 1.28 2001/11/06 19:47:47 rgb ++ * Added packet parameter to lifetime and comb structures. ++ * ++ * Revision 1.27 2001/10/18 04:45:24 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/openswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.26 2001/09/08 21:13:34 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.25 2001/06/14 19:35:16 rgb ++ * Update copyright date. ++ * ++ * Revision 1.24 2001/03/20 03:49:45 rgb ++ * Ditch superfluous debug_pfkey declaration. ++ * Move misplaced openswan.h inclusion for kernel case. ++ * ++ * Revision 1.23 2001/03/16 07:41:50 rgb ++ * Put openswan.h include before pluto includes. ++ * ++ * Revision 1.22 2001/02/27 22:24:56 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.21 2000/11/17 18:10:30 rgb ++ * Fixed bugs mostly relating to spirange, to treat all spi variables as ++ * network byte order since this is the way PF_KEYv2 stored spis. ++ * ++ * Revision 1.20 2000/10/12 00:02:39 rgb ++ * Removed 'format, ##' nonsense from debug macros for RH7.0. ++ * ++ * Revision 1.19 2000/10/10 20:10:20 rgb ++ * Added support for debug_ipcomp and debug_verbose to klipsdebug. ++ * ++ * Revision 1.18 2000/09/12 18:59:54 rgb ++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan. ++ * ++ * Revision 1.17 2000/09/12 03:27:00 rgb ++ * Moved DEBUGGING definition to compile kernel with debug off. ++ * ++ * Revision 1.16 2000/09/08 19:22:12 rgb ++ * Fixed pfkey_prop_build() parameter to be only single indirection. ++ * Fixed struct alg copy. ++ * ++ * Revision 1.15 2000/08/20 21:40:01 rgb ++ * Added an address parameter sanity check to pfkey_address_build(). ++ * ++ * Revision 1.14 2000/08/15 17:29:23 rgb ++ * Fixes from SZI to untested pfkey_prop_build(). ++ * ++ * Revision 1.13 2000/06/02 22:54:14 rgb ++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. ++ * ++ * Revision 1.12 2000/05/10 19:24:01 rgb ++ * Fleshed out sensitivity, proposal and supported extensions. ++ * ++ * Revision 1.11 2000/03/16 14:07:23 rgb ++ * Renamed ALIGN macro to avoid fighting with others in kernel. ++ * ++ * Revision 1.10 2000/01/24 21:14:35 rgb ++ * Added disabled pluto pfkey lib debug flag. ++ * ++ * Revision 1.9 2000/01/21 06:27:32 rgb ++ * Added address cases for eroute flows. ++ * Removed unused code. ++ * Dropped unused argument to pfkey_x_satype_build(). ++ * Indented compiler directives for readability. ++ * Added klipsdebug switching capability. ++ * Fixed SADB_EXT_MAX bug not permitting last extension access. ++ * ++ * Revision 1.8 1999/12/29 21:17:41 rgb ++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg** ++ * parameter for cleaner manipulation of extensions[] and to guard ++ * against potential memory leaks. ++ * Changed the I/F to pfkey_msg_free() for the same reason. ++ * ++ * Revision 1.7 1999/12/09 23:12:20 rgb ++ * Removed unused cruft. ++ * Added argument to pfkey_sa_build() to do eroutes. ++ * Fixed exttype check in as yet unused pfkey_lifetime_build(). ++ * ++ * Revision 1.6 1999/12/07 19:54:29 rgb ++ * Removed static pluto debug flag. ++ * Added functions for pfkey message and extensions initialisation ++ * and cleanup. ++ * ++ * Revision 1.5 1999/12/01 22:20:06 rgb ++ * Changed pfkey_sa_build to accept an SPI in network byte order. ++ * Added to quiet userspace compiler. ++ * Moved pfkey_lib_debug variable into the library. ++ * Removed SATYPE check from pfkey_msg_hdr_build so FLUSH will work. ++ * Added extension assembly debugging. ++ * Isolated assignment with brackets to be sure of scope. ++ * ++ * Revision 1.4 1999/11/27 11:57:35 rgb ++ * Added ipv6 headers. ++ * Remove over-zealous algorithm sanity checkers from pfkey_sa_build. ++ * Debugging error messages added. ++ * Fixed missing auth and encrypt assignment bug. ++ * Add argument to pfkey_msg_parse() for direction. ++ * Move parse-after-build check inside pfkey_msg_build(). ++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. ++ * Add CVS log entry to bottom of file. ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/pfkey_v2_debug.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,176 @@ ++/* ++ * @(#) pfkey version 2 debugging messages ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_debug.c,v 1.9 2004/03/08 01:59:08 ken Exp $ ++ * ++ */ ++ ++#ifdef __KERNEL__ ++ ++# include /* for printk */ ++ ++# include "openswan/ipsec_kversion.h" /* for malloc switch */ ++# ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++# else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++# endif /* MALLOC_SLAB */ ++# include /* error codes */ ++# include /* size_t */ ++# include /* mark_bh */ ++ ++# include /* struct device, and other headers */ ++# include /* eth_type_trans */ ++extern int debug_pfkey; ++ ++#else /* __KERNEL__ */ ++ ++# include ++# include ++# include ++ ++#endif /* __KERNEL__ */ ++ ++#include "openswan.h" ++#include "pfkeyv2.h" ++#include "pfkey.h" ++ ++/* ++ * This file provides ASCII translations of PF_KEY magic numbers. ++ * ++ */ ++ ++static char *pfkey_sadb_ext_strings[]={ ++ "reserved", /* SADB_EXT_RESERVED 0 */ ++ "security-association", /* SADB_EXT_SA 1 */ ++ "lifetime-current", /* SADB_EXT_LIFETIME_CURRENT 2 */ ++ "lifetime-hard", /* SADB_EXT_LIFETIME_HARD 3 */ ++ "lifetime-soft", /* SADB_EXT_LIFETIME_SOFT 4 */ ++ "source-address", /* SADB_EXT_ADDRESS_SRC 5 */ ++ "destination-address", /* SADB_EXT_ADDRESS_DST 6 */ ++ "proxy-address", /* SADB_EXT_ADDRESS_PROXY 7 */ ++ "authentication-key", /* SADB_EXT_KEY_AUTH 8 */ ++ "cipher-key", /* SADB_EXT_KEY_ENCRYPT 9 */ ++ "source-identity", /* SADB_EXT_IDENTITY_SRC 10 */ ++ "destination-identity", /* SADB_EXT_IDENTITY_DST 11 */ ++ "sensitivity-label", /* SADB_EXT_SENSITIVITY 12 */ ++ "proposal", /* SADB_EXT_PROPOSAL 13 */ ++ "supported-auth", /* SADB_EXT_SUPPORTED_AUTH 14 */ ++ "supported-cipher", /* SADB_EXT_SUPPORTED_ENCRYPT 15 */ ++ "spi-range", /* SADB_EXT_SPIRANGE 16 */ ++ "X-kmpprivate", /* SADB_X_EXT_KMPRIVATE 17 */ ++ "X-satype2", /* SADB_X_EXT_SATYPE2 18 */ ++ "X-security-association", /* SADB_X_EXT_SA2 19 */ ++ "X-destination-address2", /* SADB_X_EXT_ADDRESS_DST2 20 */ ++ "X-source-flow-address", /* SADB_X_EXT_ADDRESS_SRC_FLOW 21 */ ++ "X-dest-flow-address", /* SADB_X_EXT_ADDRESS_DST_FLOW 22 */ ++ "X-source-mask", /* SADB_X_EXT_ADDRESS_SRC_MASK 23 */ ++ "X-dest-mask", /* SADB_X_EXT_ADDRESS_DST_MASK 24 */ ++ "X-set-debug", /* SADB_X_EXT_DEBUG 25 */ ++#ifdef NAT_TRAVERSAL ++ "X-NAT-T-type", /* SADB_X_EXT_NAT_T_TYPE 26 */ ++ "X-NAT-T-sport", /* SADB_X_EXT_NAT_T_SPORT 27 */ ++ "X-NAT-T-dport", /* SADB_X_EXT_NAT_T_DPORT 28 */ ++ "X-NAT-T-OA", /* SADB_X_EXT_NAT_T_OA 29 */ ++#endif ++}; ++ ++const char * ++pfkey_v2_sadb_ext_string(int ext) ++{ ++ if(ext <= SADB_EXT_MAX) { ++ return pfkey_sadb_ext_strings[ext]; ++ } else { ++ return "unknown-ext"; ++ } ++} ++ ++ ++static char *pfkey_sadb_type_strings[]={ ++ "reserved", /* SADB_RESERVED */ ++ "getspi", /* SADB_GETSPI */ ++ "update", /* SADB_UPDATE */ ++ "add", /* SADB_ADD */ ++ "delete", /* SADB_DELETE */ ++ "get", /* SADB_GET */ ++ "acquire", /* SADB_ACQUIRE */ ++ "register", /* SADB_REGISTER */ ++ "expire", /* SADB_EXPIRE */ ++ "flush", /* SADB_FLUSH */ ++ "dump", /* SADB_DUMP */ ++ "x-promisc", /* SADB_X_PROMISC */ ++ "x-pchange", /* SADB_X_PCHANGE */ ++ "x-groupsa", /* SADB_X_GRPSA */ ++ "x-addflow(eroute)", /* SADB_X_ADDFLOW */ ++ "x-delflow(eroute)", /* SADB_X_DELFLOW */ ++ "x-debug", /* SADB_X_DEBUG */ ++}; ++ ++const char * ++pfkey_v2_sadb_type_string(int sadb_type) ++{ ++ if(sadb_type <= SADB_MAX) { ++ return pfkey_sadb_type_strings[sadb_type]; ++ } else { ++ return "unknown-sadb-type"; ++ } ++} ++ ++ ++ ++ ++/* ++ * $Log: pfkey_v2_debug.c,v $ ++ * Revision 1.9 2004/03/08 01:59:08 ken ++ * freeswan.h -> openswan.h ++ * ++ * Revision 1.8 2003/12/10 01:20:19 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.7 2002/09/20 05:01:26 rgb ++ * Fixed limit inclusion error in both type and ext string conversion. ++ * ++ * Revision 1.6 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.5 2002/04/24 07:36:40 mcr ++ * Moved from ./lib/pfkey_v2_debug.c,v ++ * ++ * Revision 1.4 2002/01/29 22:25:36 rgb ++ * Re-add ipsec_kversion.h to keep MALLOC happy. ++ * ++ * Revision 1.3 2002/01/29 01:59:09 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from openswan.h that also duplicated kversions.h ++ * code. ++ * ++ * Revision 1.2 2002/01/20 20:34:50 mcr ++ * added pfkey_v2_sadb_type_string to decode sadb_type to string. ++ * ++ * Revision 1.1 2001/11/27 05:30:06 mcr ++ * initial set of debug strings for pfkey debugging. ++ * this will eventually only be included for debug builds. ++ * ++ * Revision 1.1 2001/09/21 04:12:03 mcr ++ * first compilable version. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/pfkey_v2_ext_bits.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,808 @@ ++/* ++ * RFC2367 PF_KEYv2 Key management API message parser ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_ext_bits.c,v 1.20 2004/03/08 01:59:08 ken Exp $ ++ */ ++ ++/* ++ * Template from klips/net/ipsec/ipsec/ipsec_parse.c. ++ */ ++ ++char pfkey_v2_ext_bits_c_version[] = "$Id: pfkey_v2_ext_bits.c,v 1.20 2004/03/08 01:59:08 ken Exp $"; ++ ++/* ++ * Some ugly stuff to allow consistent debugging code for use in the ++ * kernel and in user space ++*/ ++ ++#ifdef __KERNEL__ ++ ++# include /* for printk */ ++ ++# include "openswan/ipsec_kversion.h" /* for malloc switch */ ++# ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++# else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++# endif /* MALLOC_SLAB */ ++# include /* error codes */ ++# include /* size_t */ ++# include /* mark_bh */ ++ ++# include /* struct device, and other headers */ ++# include /* eth_type_trans */ ++# include /* struct iphdr */ ++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++# include ++# endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ ++ ++#else /* __KERNEL__ */ ++ ++# include ++# include ++# include ++#endif ++ ++#include ++#include ++#include ++ ++unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/] = { ++ ++/* INBOUND EXTENSIONS */ ++{ ++ ++/* PERMITTED IN */ ++{ ++/* SADB_RESERVED */ ++0 ++, ++/* SADB_GETSPI */ ++1< openswan.h ++ * ++ * Revision 1.19 2003/12/22 21:38:13 mcr ++ * removed extraenous #endif. ++ * ++ * Revision 1.18 2003/12/22 19:34:41 mcr ++ * added 0.6c NAT-T patch. ++ * ++ * Revision 1.17 2003/12/10 01:20:19 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.16 2003/10/31 02:27:12 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.15.30.1 2003/09/21 13:59:44 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.15 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.14 2002/04/24 07:36:40 mcr ++ * Moved from ./lib/pfkey_v2_ext_bits.c,v ++ * ++ * Revision 1.13 2002/01/29 22:25:36 rgb ++ * Re-add ipsec_kversion.h to keep MALLOC happy. ++ * ++ * Revision 1.12 2002/01/29 01:59:10 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from openswan.h that also duplicated kversions.h ++ * code. ++ * ++ * Revision 1.11 2001/10/18 04:45:24 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/openswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.10 2001/09/08 21:13:35 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.9 2001/06/14 19:35:16 rgb ++ * Update copyright date. ++ * ++ * Revision 1.8 2001/03/26 23:07:36 rgb ++ * Remove requirement for auth and enc key from UPDATE. ++ * ++ * Revision 1.7 2000/09/12 22:35:37 rgb ++ * Restructured to remove unused extensions from CLEARFLOW messages. ++ * ++ * Revision 1.6 2000/09/09 06:39:01 rgb ++ * Added comments for clarity. ++ * ++ * Revision 1.5 2000/06/02 22:54:14 rgb ++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. ++ * ++ * Revision 1.4 2000/01/21 06:27:56 rgb ++ * Added address cases for eroute flows. ++ * Added comments for each message type. ++ * Added klipsdebug switching capability. ++ * Fixed GRPSA bitfields. ++ * ++ * Revision 1.3 1999/12/01 22:20:27 rgb ++ * Remove requirement for a proxy address in an incoming getspi message. ++ * ++ * Revision 1.2 1999/11/27 11:57:06 rgb ++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. ++ * Add CVS log entry to bottom of file. ++ * Cleaned out unused bits. ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/pfkey_v2_parse.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1820 @@ ++/* ++ * RFC2367 PF_KEYv2 Key management API message parser ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_parse.c,v 1.59 2004/04/18 03:03:49 mcr Exp $ ++ */ ++ ++/* ++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c. ++ */ ++ ++char pfkey_v2_parse_c_version[] = "$Id: pfkey_v2_parse.c,v 1.59 2004/04/18 03:03:49 mcr Exp $"; ++ ++/* ++ * Some ugly stuff to allow consistent debugging code for use in the ++ * kernel and in user space ++*/ ++ ++#ifdef __KERNEL__ ++ ++# include /* for printk */ ++ ++#include "openswan/ipsec_kversion.h" /* for malloc switch */ ++ ++# ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++# else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++# endif /* MALLOC_SLAB */ ++# include /* error codes */ ++# include /* size_t */ ++# include /* mark_bh */ ++ ++# include /* struct device, and other headers */ ++# include /* eth_type_trans */ ++# include /* struct iphdr */ ++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++# include /* struct ipv6hdr */ ++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ ++extern int debug_pfkey; ++ ++# include ++ ++#include "openswan/ipsec_encap.h" ++ ++#else /* __KERNEL__ */ ++ ++# include ++# include ++# include ++ ++# include ++# include "constants.h" ++# include "programs/pluto/defs.h" /* for PRINTF_LIKE */ ++ ++#endif /* __KERNEL__ */ ++ ++ ++#include ++#include ++ ++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ ++ ++/* ++ * how to handle debugging for pfkey. ++ */ ++#include ++ ++unsigned int pfkey_lib_debug = PF_KEY_DEBUG_PARSE_NONE; ++void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1); ++void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1); ++ ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++struct satype_tbl { ++ uint8_t proto; ++ uint8_t satype; ++ char* name; ++} static satype_tbl[] = { ++#ifdef __KERNEL__ ++ { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" }, ++ { IPPROTO_AH, SADB_SATYPE_AH, "AH" }, ++ { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, ++#ifdef CONFIG_IPSEC_IPCOMP ++ { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" }, ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" }, ++#else /* __KERNEL__ */ ++ { SA_ESP, SADB_SATYPE_ESP, "ESP" }, ++ { SA_AH, SADB_SATYPE_AH, "AH" }, ++ { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, ++ { SA_COMP, SADB_X_SATYPE_COMP, "COMP" }, ++ { SA_INT, SADB_X_SATYPE_INT, "INT" }, ++#endif /* __KERNEL__ */ ++ { 0, 0, "UNKNOWN" } ++}; ++ ++uint8_t ++satype2proto(uint8_t satype) ++{ ++ int i =0; ++ ++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { ++ i++; ++ } ++ return satype_tbl[i].proto; ++} ++ ++uint8_t ++proto2satype(uint8_t proto) ++{ ++ int i = 0; ++ ++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { ++ i++; ++ } ++ return satype_tbl[i].satype; ++} ++ ++char* ++satype2name(uint8_t satype) ++{ ++ int i = 0; ++ ++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { ++ i++; ++ } ++ return satype_tbl[i].name; ++} ++ ++char* ++proto2name(uint8_t proto) ++{ ++ int i = 0; ++ ++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { ++ i++; ++ } ++ return satype_tbl[i].name; ++} ++ ++/* Default extension parsers taken from the KLIPS code */ ++ ++DEBUG_NO_STATIC int ++pfkey_sa_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; ++#if 0 ++ struct sadb_sa sav2; ++#endif ++ ++ /* sanity checks... */ ++ if(!pfkey_sa) { ++ ERROR("pfkey_sa_parse: " ++ "NULL pointer passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++#if 0 ++ /* check if this structure is short, and if so, fix it up. ++ * XXX this is NOT the way to do things. ++ */ ++ if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) { ++ ++ /* yes, so clear out a temporary structure, and copy first */ ++ memset(&sav2, 0, sizeof(sav2)); ++ memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1)); ++ sav2.sadb_x_sa_ref=-1; ++ sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_sa = &sav2; ++ } ++#endif ++ ++ ++ if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n", ++ pfkey_sa->sadb_sa_len, ++ (int)sizeof(struct sadb_sa)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n", ++ pfkey_sa->sadb_sa_encrypt, ++ SADB_EALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n", ++ pfkey_sa->sadb_sa_auth, ++ SADB_AALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "state=%d exceeds MAX=%d.\n", ++ pfkey_sa->sadb_sa_state, ++ SADB_SASTATE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "state=%d is DEAD=%d.\n", ++ pfkey_sa->sadb_sa_state, ++ SADB_SASTATE_DEAD); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_sa->sadb_sa_replay > 64) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "replay window size: %d -- must be 0 <= size <= 64\n", ++ pfkey_sa->sadb_sa_replay); ++ SENDERR(EINVAL); ++ } ++ ++ if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) || ++ (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2))) ++ { ++ ERROR( ++ "pfkey_sa_parse: " ++ "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n", ++ pfkey_sa->sadb_sa_exttype, ++ SADB_EXT_SA, ++ SADB_X_EXT_SA2); ++ SENDERR(EINVAL); ++ } ++ ++ if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { ++ ERROR( ++ "pfkey_sa_parse: " ++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", ++ pfkey_sa->sadb_x_sa_ref, ++ IPSEC_SAREF_NULL, ++ IPSEC_SA_REF_TABLE_NUM_ENTRIES); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_sa_parse: " ++ "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n", ++ pfkey_sa->sadb_sa_len, ++ pfkey_sa->sadb_sa_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype), ++ (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi), ++ pfkey_sa->sadb_sa_replay, ++ pfkey_sa->sadb_sa_state, ++ pfkey_sa->sadb_sa_auth, ++ pfkey_sa->sadb_sa_encrypt, ++ pfkey_sa->sadb_sa_flags, ++ pfkey_sa->sadb_x_sa_ref); ++ ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_lifetime_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_lifetime_parse:enter\n"); ++ /* sanity checks... */ ++ if(!pfkey_lifetime) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_lifetime_parse: " ++ "NULL pointer passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_lifetime->sadb_lifetime_len != ++ sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_lifetime_parse: " ++ "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n", ++ pfkey_lifetime->sadb_lifetime_len, ++ (int)sizeof(struct sadb_lifetime)); ++ SENDERR(EINVAL); ++ } ++ ++ if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) && ++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) && ++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_lifetime_parse: " ++ "unexpected ext_type=%d.\n", ++ pfkey_lifetime->sadb_lifetime_exttype); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_lifetime_parse: " ++ "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n", ++ pfkey_lifetime->sadb_lifetime_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype), ++ pfkey_lifetime->sadb_lifetime_allocations, ++ (unsigned)pfkey_lifetime->sadb_lifetime_bytes, ++ (unsigned)pfkey_lifetime->sadb_lifetime_addtime, ++ (unsigned)pfkey_lifetime->sadb_lifetime_usetime, ++ pfkey_lifetime->sadb_x_lifetime_packets); ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_address_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ int saddr_len = 0; ++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; ++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); ++ char ipaddr_txt[ADDRTOT_BUF]; ++ ++ /* sanity checks... */ ++ if(!pfkey_address) { ++ ERROR( ++ "pfkey_address_parse: " ++ "NULL pointer passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_address->sadb_address_len < ++ (sizeof(struct sadb_address) + sizeof(struct sockaddr))/ ++ IPSEC_PFKEYv2_ALIGN) { ++ ERROR("pfkey_address_parse: " ++ "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", ++ pfkey_address->sadb_address_len, ++ (int)sizeof(struct sadb_address), ++ (int)sizeof(struct sockaddr)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_address->sadb_address_reserved) { ++ ERROR("pfkey_address_parse: " ++ "res=%d, must be zero.\n", ++ pfkey_address->sadb_address_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_address->sadb_address_exttype) { ++ case SADB_EXT_ADDRESS_SRC: ++ case SADB_EXT_ADDRESS_DST: ++ case SADB_EXT_ADDRESS_PROXY: ++ case SADB_X_EXT_ADDRESS_DST2: ++ case SADB_X_EXT_ADDRESS_SRC_FLOW: ++ case SADB_X_EXT_ADDRESS_DST_FLOW: ++ case SADB_X_EXT_ADDRESS_SRC_MASK: ++ case SADB_X_EXT_ADDRESS_DST_MASK: ++#ifdef NAT_TRAVERSAL ++ case SADB_X_EXT_NAT_T_OA: ++#endif ++ break; ++ default: ++ ERROR( ++ "pfkey_address_parse: " ++ "unexpected ext_type=%d.\n", ++ pfkey_address->sadb_address_exttype); ++ SENDERR(EINVAL); ++ } ++ ++ switch(s->sa_family) { ++ case AF_INET: ++ saddr_len = sizeof(struct sockaddr_in); ++ sprintf(ipaddr_txt, "%d.%d.%d.%d" ++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF ++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF ++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF ++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF); ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_address_parse: " ++ "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n", ++ pfkey_address->sadb_address_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), ++ s->sa_family, ++ ipaddr_txt, ++ pfkey_address->sadb_address_proto, ++ ntohs(((struct sockaddr_in*)s)->sin_port)); ++ break; ++ case AF_INET6: ++ saddr_len = sizeof(struct sockaddr_in6); ++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x" ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6]) ++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7])); ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_address_parse: " ++ "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n", ++ pfkey_address->sadb_address_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), ++ s->sa_family, ++ ipaddr_txt, ++ pfkey_address->sadb_address_proto, ++ ((struct sockaddr_in6*)s)->sin6_port); ++ break; ++ default: ++ ERROR( ++ "pfkey_address_parse: " ++ "s->sa_family=%d not supported.\n", ++ s->sa_family); ++ SENDERR(EPFNOSUPPORT); ++ } ++ ++ if(pfkey_address->sadb_address_len != ++ DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) { ++ ERROR( ++ "pfkey_address_parse: " ++ "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", ++ pfkey_address->sadb_address_len, ++ (int)sizeof(struct sadb_address), ++ saddr_len); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_address->sadb_address_prefixlen != 0) { ++ ERROR( ++ "pfkey_address_parse: " ++ "address prefixes not supported yet.\n"); ++ SENDERR(EAFNOSUPPORT); /* not supported yet */ ++ } ++ ++ /* XXX check if port!=0 */ ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_address_parse: successful.\n"); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_key_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; ++ ++ /* sanity checks... */ ++ ++ if(!pfkey_key) { ++ ERROR( ++ "pfkey_key_parse: " ++ "NULL pointer passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) { ++ ERROR( ++ "pfkey_key_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_key->sadb_key_len, ++ (int)sizeof(struct sadb_key)); ++ SENDERR(EINVAL); ++ } ++ ++ if(!pfkey_key->sadb_key_bits) { ++ ERROR( ++ "pfkey_key_parse: " ++ "key length set to zero, must be non-zero.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_key->sadb_key_len != ++ DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits, ++ PFKEYBITS)) { ++ ERROR( ++ "pfkey_key_parse: " ++ "key length=%d does not agree with extension length=%d.\n", ++ pfkey_key->sadb_key_bits, ++ pfkey_key->sadb_key_len); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_key->sadb_key_reserved) { ++ ERROR( ++ "pfkey_key_parse: " ++ "res=%d, must be zero.\n", ++ pfkey_key->sadb_key_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) || ++ (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) { ++ ERROR( ++ "pfkey_key_parse: " ++ "expecting extension type AUTH or ENCRYPT, got %d.\n", ++ pfkey_key->sadb_key_exttype); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_key_parse: " ++ "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n", ++ pfkey_key->sadb_key_len, ++ pfkey_key->sadb_key_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype), ++ pfkey_key->sadb_key_bits, ++ pfkey_key->sadb_key_reserved); ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_ident_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { ++ ERROR( ++ "pfkey_ident_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_ident->sadb_ident_len, ++ (int)sizeof(struct sadb_ident)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) { ++ ERROR( ++ "pfkey_ident_parse: " ++ "ident_type=%d out of range, must be less than %d.\n", ++ pfkey_ident->sadb_ident_type, ++ SADB_IDENTTYPE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_ident->sadb_ident_reserved) { ++ ERROR( ++ "pfkey_ident_parse: " ++ "res=%d, must be zero.\n", ++ pfkey_ident->sadb_ident_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ /* string terminator/padding must be zero */ ++ if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { ++ if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) { ++ ERROR( ++ "pfkey_ident_parse: " ++ "string padding must be zero, last is 0x%02x.\n", ++ *((char*)pfkey_ident + ++ pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) || ++ (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) { ++ ERROR( ++ "pfkey_key_parse: " ++ "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n", ++ pfkey_ident->sadb_ident_exttype); ++ SENDERR(EINVAL); ++ } ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_sens_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_sens_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_sens->sadb_sens_len, ++ (int)sizeof(struct sadb_sens)); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_sens_parse: " ++ "Sorry, I can't parse exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++#if 0 ++ SENDERR(EINVAL); /* don't process these yet */ ++#endif ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_prop_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ int i, num_comb; ++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext; ++ struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop)); ++ ++ /* sanity checks... */ ++ if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) || ++ (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n", ++ pfkey_prop->sadb_prop_len, ++ (int)sizeof(struct sadb_prop), ++ (int)sizeof(struct sadb_comb)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_prop->sadb_prop_replay > 64) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "replay window size: %d -- must be 0 <= size <= 64\n", ++ pfkey_prop->sadb_prop_replay); ++ SENDERR(EINVAL); ++ } ++ ++ for(i=0; i<3; i++) { ++ if(pfkey_prop->sadb_prop_reserved[i]) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "res[%d]=%d, must be zero.\n", ++ i, pfkey_prop->sadb_prop_reserved[i]); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb); ++ ++ for(i = 0; i < num_comb; i++) { ++ if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n", ++ i, ++ pfkey_comb->sadb_comb_auth, ++ SADB_AALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_auth) { ++ if(!pfkey_comb->sadb_comb_auth_minbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n", ++ i); ++ SENDERR(EINVAL); ++ } ++ if(!pfkey_comb->sadb_comb_auth_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n", ++ i); ++ SENDERR(EINVAL); ++ } ++ if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_auth_minbits, ++ pfkey_comb->sadb_comb_auth_maxbits); ++ SENDERR(EINVAL); ++ } ++ } else { ++ if(pfkey_comb->sadb_comb_auth_minbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_auth_minbits); ++ SENDERR(EINVAL); ++ } ++ if(pfkey_comb->sadb_comb_auth_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_auth_maxbits); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_comb_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n", ++ i, ++ pfkey_comb->sadb_comb_encrypt, ++ SADB_EALG_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_encrypt) { ++ if(!pfkey_comb->sadb_comb_encrypt_minbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n", ++ i); ++ SENDERR(EINVAL); ++ } ++ if(!pfkey_comb->sadb_comb_encrypt_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n", ++ i); ++ SENDERR(EINVAL); ++ } ++ if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_encrypt_minbits, ++ pfkey_comb->sadb_comb_encrypt_maxbits); ++ SENDERR(EINVAL); ++ } ++ } else { ++ if(pfkey_comb->sadb_comb_encrypt_minbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_encrypt_minbits); ++ SENDERR(EINVAL); ++ } ++ if(pfkey_comb->sadb_comb_encrypt_maxbits) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_encrypt_maxbits); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ /* XXX do sanity check on flags */ ++ ++ if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n", ++ i, ++ pfkey_comb->sadb_comb_soft_allocations, ++ pfkey_comb->sadb_comb_hard_allocations); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n", ++ i, ++ (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes, ++ (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n", ++ i, ++ (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime, ++ (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n", ++ i, ++ (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime, ++ (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n", ++ i, ++ pfkey_comb->sadb_x_comb_soft_packets, ++ pfkey_comb->sadb_x_comb_hard_packets); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_comb->sadb_comb_reserved) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_prop_parse: " ++ "comb[%d].res=%d, must be zero.\n", ++ i, ++ pfkey_comb->sadb_comb_reserved); ++ SENDERR(EINVAL); ++ } ++ pfkey_comb++; ++ } ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_supported_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ unsigned int i, num_alg; ++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext; ++ struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported)); ++ ++ /* sanity checks... */ ++ if((pfkey_supported->sadb_supported_len < ++ sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) || ++ (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - ++ sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) { ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n", ++ pfkey_supported->sadb_supported_len, ++ (int)sizeof(struct sadb_supported), ++ (int)sizeof(struct sadb_alg)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_supported->sadb_supported_reserved) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "res=%d, must be zero.\n", ++ pfkey_supported->sadb_supported_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg); ++ ++ for(i = 0; i < num_alg; i++) { ++ /* process algo description */ ++ if(pfkey_alg->sadb_alg_reserved) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n", ++ i, ++ pfkey_alg->sadb_alg_id, ++ pfkey_alg->sadb_alg_ivlen, ++ pfkey_alg->sadb_alg_minbits, ++ pfkey_alg->sadb_alg_maxbits, ++ pfkey_alg->sadb_alg_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ /* XXX can alg_id auth/enc be determined from info given? ++ Yes, but OpenBSD's method does not iteroperate with rfc2367. ++ rgb, 2000-04-06 */ ++ ++ switch(pfkey_supported->sadb_supported_exttype) { ++ case SADB_EXT_SUPPORTED_AUTH: ++ if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n", ++ i, ++ pfkey_alg->sadb_alg_id, ++ SADB_AALG_MAX); ++ SENDERR(EINVAL); ++ } ++ break; ++ case SADB_EXT_SUPPORTED_ENCRYPT: ++ if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", ++ i, ++ pfkey_alg->sadb_alg_id, ++ SADB_EALG_MAX); ++ SENDERR(EINVAL); ++ } ++ break; ++ default: ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_supported_parse: " ++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", ++ i, ++ pfkey_alg->sadb_alg_id, ++ SADB_EALG_MAX); ++ SENDERR(EINVAL); ++ } ++ pfkey_alg++; ++ } ++ ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_spirange_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_spirange->sadb_spirange_len != ++ sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_spirange_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_spirange->sadb_spirange_len, ++ (int)sizeof(struct sadb_spirange)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_spirange->sadb_spirange_reserved) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_spirange_parse: " ++ "reserved=%d must be set to zero.\n", ++ pfkey_spirange->sadb_spirange_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_spirange_parse: " ++ "minspi=%08x must be < maxspi=%08x.\n", ++ ntohl(pfkey_spirange->sadb_spirange_min), ++ ntohl(pfkey_spirange->sadb_spirange_max)); ++ SENDERR(EINVAL); ++ } ++ ++ if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_spirange_parse: " ++ "minspi=%08x must be > 255.\n", ++ ntohl(pfkey_spirange->sadb_spirange_min)); ++ SENDERR(EEXIST); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_spirange_parse: " ++ "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n", ++ pfkey_spirange->sadb_spirange_len, ++ pfkey_spirange->sadb_spirange_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype), ++ pfkey_spirange->sadb_spirange_min, ++ pfkey_spirange->sadb_spirange_max, ++ pfkey_spirange->sadb_spirange_reserved); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext; ++ ++ /* sanity checks... */ ++ if(pfkey_x_kmprivate->sadb_x_kmprivate_len < ++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_kmprivate_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_x_kmprivate->sadb_x_kmprivate_len, ++ (int)sizeof(struct sadb_x_kmprivate)); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_kmprivate_parse: " ++ "reserved=%d must be set to zero.\n", ++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_kmprivate_parse: " ++ "Sorry, I can't parse exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_satype_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ int i; ++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_x_satype_parse: enter\n"); ++ /* sanity checks... */ ++ if(pfkey_x_satype->sadb_x_satype_len != ++ sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_satype_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_x_satype->sadb_x_satype_len, ++ (int)sizeof(struct sadb_x_satype)); ++ SENDERR(EINVAL); ++ } ++ ++ if(!pfkey_x_satype->sadb_x_satype_satype) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_satype_parse: " ++ "satype is zero, must be non-zero.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_satype_parse: " ++ "satype %d > max %d, invalid.\n", ++ pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_satype_parse: " ++ "proto lookup from satype=%d failed.\n", ++ pfkey_x_satype->sadb_x_satype_satype); ++ SENDERR(EINVAL); ++ } ++ ++ for(i = 0; i < 3; i++) { ++ if(pfkey_x_satype->sadb_x_satype_reserved[i]) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_satype_parse: " ++ "reserved[%d]=%d must be set to zero.\n", ++ i, pfkey_x_satype->sadb_x_satype_reserved[i]); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_x_satype_parse: " ++ "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n", ++ pfkey_x_satype->sadb_x_satype_len, ++ pfkey_x_satype->sadb_x_satype_exttype, ++ pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype), ++ pfkey_x_satype->sadb_x_satype_satype, ++ satype2name(pfkey_x_satype->sadb_x_satype_satype), ++ pfkey_x_satype->sadb_x_satype_reserved[0], ++ pfkey_x_satype->sadb_x_satype_reserved[1], ++ pfkey_x_satype->sadb_x_satype_reserved[2]); ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ int i; ++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_x_debug_parse: enter\n"); ++ /* sanity checks... */ ++ if(pfkey_x_debug->sadb_x_debug_len != ++ sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_debug_parse: " ++ "size wrong ext_len=%d, key_ext_len=%d.\n", ++ pfkey_x_debug->sadb_x_debug_len, ++ (int)sizeof(struct sadb_x_debug)); ++ SENDERR(EINVAL); ++ } ++ ++ for(i = 0; i < 4; i++) { ++ if(pfkey_x_debug->sadb_x_debug_reserved[i]) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_debug_parse: " ++ "reserved[%d]=%d must be set to zero.\n", ++ i, pfkey_x_debug->sadb_x_debug_reserved[i]); ++ SENDERR(EINVAL); ++ } ++ } ++ ++errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_ext_protocol_parse(struct sadb_ext *pfkey_ext) ++{ ++ int error = 0; ++ struct sadb_protocol *p = (struct sadb_protocol *)pfkey_ext; ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, "pfkey_x_protocol_parse:\n"); ++ /* sanity checks... */ ++ ++ if (p->sadb_protocol_len != sizeof(*p)/IPSEC_PFKEYv2_ALIGN) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_x_protocol_parse: size wrong ext_len=%d, key_ext_len=%d.\n", ++ p->sadb_protocol_len, (int)sizeof(*p)); ++ SENDERR(EINVAL); ++ } ++ ++ if (p->sadb_protocol_reserved2 != 0) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_protocol_parse: res=%d, must be zero.\n", ++ p->sadb_protocol_reserved2); ++ SENDERR(EINVAL); ++ } ++ ++ errlab: ++ return error; ++} ++ ++#ifdef NAT_TRAVERSAL ++DEBUG_NO_STATIC int ++pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext) ++{ ++ return 0; ++} ++DEBUG_NO_STATIC int ++pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext) ++{ ++ return 0; ++} ++#endif ++ ++#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME}; ++ ++DEFINEPARSER(pfkey_sa_parse); ++DEFINEPARSER(pfkey_lifetime_parse); ++DEFINEPARSER(pfkey_address_parse); ++DEFINEPARSER(pfkey_key_parse); ++DEFINEPARSER(pfkey_ident_parse); ++DEFINEPARSER(pfkey_sens_parse); ++DEFINEPARSER(pfkey_prop_parse); ++DEFINEPARSER(pfkey_supported_parse); ++DEFINEPARSER(pfkey_spirange_parse); ++DEFINEPARSER(pfkey_x_kmprivate_parse); ++DEFINEPARSER(pfkey_x_satype_parse); ++DEFINEPARSER(pfkey_x_ext_debug_parse); ++DEFINEPARSER(pfkey_x_ext_protocol_parse); ++#ifdef NAT_TRAVERSAL ++DEFINEPARSER(pfkey_x_ext_nat_t_type_parse); ++DEFINEPARSER(pfkey_x_ext_nat_t_port_parse); ++#endif ++ ++struct pf_key_ext_parsers_def *ext_default_parsers[]= ++{ ++ NULL, /* pfkey_msg_parse, */ ++ &pfkey_sa_parse_def, ++ &pfkey_lifetime_parse_def, ++ &pfkey_lifetime_parse_def, ++ &pfkey_lifetime_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_key_parse_def, ++ &pfkey_key_parse_def, ++ &pfkey_ident_parse_def, ++ &pfkey_ident_parse_def, ++ &pfkey_sens_parse_def, ++ &pfkey_prop_parse_def, ++ &pfkey_supported_parse_def, ++ &pfkey_supported_parse_def, ++ &pfkey_spirange_parse_def, ++ &pfkey_x_kmprivate_parse_def, ++ &pfkey_x_satype_parse_def, ++ &pfkey_sa_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_address_parse_def, ++ &pfkey_x_ext_debug_parse_def, ++ &pfkey_x_ext_protocol_parse_def ++#ifdef NAT_TRAVERSAL ++ , ++ &pfkey_x_ext_nat_t_type_parse_def, ++ &pfkey_x_ext_nat_t_port_parse_def, ++ &pfkey_x_ext_nat_t_port_parse_def, ++ &pfkey_address_parse_def ++#endif ++}; ++ ++int ++pfkey_msg_parse(struct sadb_msg *pfkey_msg, ++ struct pf_key_ext_parsers_def *ext_parsers[], ++ struct sadb_ext *extensions[], ++ int dir) ++{ ++ int error = 0; ++ int remain; ++ struct sadb_ext *pfkey_ext; ++ int extensions_seen = 0; ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_msg_parse: " ++ "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", ++ pfkey_msg->sadb_msg_version, ++ pfkey_msg->sadb_msg_type, ++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), ++ pfkey_msg->sadb_msg_errno, ++ pfkey_msg->sadb_msg_satype, ++ satype2name(pfkey_msg->sadb_msg_satype), ++ pfkey_msg->sadb_msg_len, ++ pfkey_msg->sadb_msg_reserved, ++ pfkey_msg->sadb_msg_seq, ++ pfkey_msg->sadb_msg_pid); ++ ++ if(ext_parsers == NULL) ext_parsers = ext_default_parsers; ++ ++ pfkey_extensions_init(extensions); ++ ++ remain = pfkey_msg->sadb_msg_len; ++ remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; ++ ++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg + ++ sizeof(struct sadb_msg)); ++ ++ extensions[0] = (struct sadb_ext *) pfkey_msg; ++ ++ ++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { ++ ERROR("pfkey_msg_parse: " ++ "not PF_KEY_V2 msg, found %d, should be %d.\n", ++ pfkey_msg->sadb_msg_version, ++ PF_KEY_V2); ++ SENDERR(EINVAL); ++ } ++ ++ if(!pfkey_msg->sadb_msg_type) { ++ ERROR("pfkey_msg_parse: " ++ "msg type not set, must be non-zero..\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(pfkey_msg->sadb_msg_type > SADB_MAX) { ++ ERROR("pfkey_msg_parse: " ++ "msg type=%d > max=%d.\n", ++ pfkey_msg->sadb_msg_type, ++ SADB_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_msg->sadb_msg_type) { ++ case SADB_GETSPI: ++ case SADB_UPDATE: ++ case SADB_ADD: ++ case SADB_DELETE: ++ case SADB_GET: ++ case SADB_X_GRPSA: ++ case SADB_X_ADDFLOW: ++ if(!satype2proto(pfkey_msg->sadb_msg_satype)) { ++ ERROR("pfkey_msg_parse: " ++ "satype %d conversion to proto failed for msg_type %d (%s).\n", ++ pfkey_msg->sadb_msg_satype, ++ pfkey_msg->sadb_msg_type, ++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); ++ SENDERR(EINVAL); ++ } else { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n", ++ pfkey_msg->sadb_msg_satype, ++ satype2name(pfkey_msg->sadb_msg_satype), ++ satype2proto(pfkey_msg->sadb_msg_satype), ++ pfkey_msg->sadb_msg_type, ++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); ++ } ++ case SADB_ACQUIRE: ++ case SADB_REGISTER: ++ case SADB_EXPIRE: ++ if(!pfkey_msg->sadb_msg_satype) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "satype is zero, must be non-zero for msg_type %d(%s).\n", ++ pfkey_msg->sadb_msg_type, ++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); ++ SENDERR(EINVAL); ++ } ++ default: ++ break; ++ } ++ ++ /* errno must not be set in downward messages */ ++ /* this is not entirely true... a response to an ACQUIRE could return an error */ ++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "errno set to %d.\n", ++ pfkey_msg->sadb_msg_errno); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_msg_parse: " ++ "remain=%d, ext_type=%d(%s), ext_len=%d.\n", ++ remain, ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ pfkey_ext->sadb_ext_len); ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_msg_parse: " ++ "extensions permitted=%08x, required=%08x.\n", ++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); ++ ++ extensions_seen = 1; ++ ++ while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) { ++ /* Is there enough message left to support another extension header? */ ++ if(remain < pfkey_ext->sadb_ext_len) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "remain %d less than ext len %d.\n", ++ remain, pfkey_ext->sadb_ext_len); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_msg_parse: " ++ "parsing ext type=%d(%s) remain=%d.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ remain); ++ ++ /* Is the extension header type valid? */ ++ if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ SADB_EXT_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ /* Have we already seen this type of extension? */ ++ if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0) ++ { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "ext type %d(%s) already seen.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); ++ SENDERR(EINVAL); ++ } ++ ++ /* Do I even know about this type of extension? */ ++ if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "ext type %d(%s) unknown, ignoring.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); ++ goto next_ext; ++ } ++ ++ /* Is this type of extension permitted for this type of message? */ ++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] & ++ 1<sadb_ext_type)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], ++ 1<sadb_ext_type); ++ SENDERR(EINVAL); ++ } ++ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_msg_parse: " ++ "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n", ++ remain, ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ pfkey_ext->sadb_ext_len, ++ pfkey_ext, ++ ext_parsers[pfkey_ext->sadb_ext_type]->parser_name); ++ ++ /* Parse the extension */ ++ if((error = ++ (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "extension parsing for type %d(%s) failed with error %d.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), ++ error); ++ SENDERR(-error); ++ } ++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, ++ "pfkey_msg_parse: " ++ "Extension %d(%s) parsed.\n", ++ pfkey_ext->sadb_ext_type, ++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); ++ ++ /* Mark that we have seen this extension and remember the header location */ ++ extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type ); ++ extensions[pfkey_ext->sadb_ext_type] = pfkey_ext; ++ ++ next_ext: ++ /* Calculate how much message remains */ ++ remain -= pfkey_ext->sadb_ext_len; ++ ++ if(!remain) { ++ break; ++ } ++ /* Find the next extension header */ ++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext + ++ pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); ++ } ++ ++ if(remain) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "unexpected remainder of %d.\n", ++ remain); ++ /* why is there still something remaining? */ ++ SENDERR(EINVAL); ++ } ++ ++ /* check required extensions */ ++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, ++ "pfkey_msg_parse: " ++ "extensions permitted=%08x, seen=%08x, required=%08x.\n", ++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], ++ extensions_seen, ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); ++ ++ /* don't check further if it is an error return message since it ++ may not have a body */ ++ if(pfkey_msg->sadb_msg_errno) { ++ SENDERR(-error); ++ } ++ ++ if((extensions_seen & ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) != ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "required extensions missing:%08x.\n", ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] - ++ (extensions_seen & ++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type])); ++ SENDERR(EINVAL); ++ } ++ ++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW) ++ && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW) ++ != SADB_X_EXT_ADDRESS_DELFLOW) ++ && (((extensions_seen & (1<sadb_sa_flags ++ & SADB_X_SAFLAGS_CLEARFLOW) ++ != SADB_X_SAFLAGS_CLEARFLOW))) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n", ++ SADB_X_EXT_ADDRESS_DELFLOW ++ - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW), ++ (1<sadb_msg_type) { ++ case SADB_ADD: ++ case SADB_UPDATE: ++ /* check maturity */ ++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != ++ SADB_SASTATE_MATURE) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "state=%d for add or update should be MATURE=%d.\n", ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, ++ SADB_SASTATE_MATURE); ++ SENDERR(EINVAL); ++ } ++ ++ /* check AH and ESP */ ++ switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) { ++ case SADB_SATYPE_AH: ++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth != ++ SADB_AALG_NONE)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "auth alg is zero, must be non-zero for AH SAs.\n"); ++ SENDERR(EINVAL); ++ } ++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt != ++ SADB_EALG_NONE) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "AH handed encalg=%d, must be zero.\n", ++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt); ++ SENDERR(EINVAL); ++ } ++ break; ++ case SADB_SATYPE_ESP: ++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != ++ SADB_EALG_NONE)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n", ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); ++ SENDERR(EINVAL); ++ } ++ if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt == ++ SADB_EALG_NULL) && ++ (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth == ++ SADB_AALG_NONE) ) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "ESP handed encNULL+authNONE, illegal combination.\n"); ++ SENDERR(EINVAL); ++ } ++ break; ++ case SADB_X_SATYPE_COMP: ++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != ++ SADB_EALG_NONE)) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n", ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); ++ SENDERR(EINVAL); ++ } ++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth != ++ SADB_AALG_NONE) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "COMP handed auth=%d, must be zero.\n", ++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth); ++ SENDERR(EINVAL); ++ } ++ break; ++ default: ++ break; ++ } ++ if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) { ++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, ++ "pfkey_msg_parse: " ++ "spi=%08x must be > 255.\n", ++ ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi)); ++ SENDERR(EINVAL); ++ } ++ default: ++ break; ++ } ++errlab: ++ ++ return error; ++} ++ ++/* ++ * $Log: pfkey_v2_parse.c,v $ ++ * Revision 1.59 2004/04/18 03:03:49 mcr ++ * renamed common include files from pluto directory. ++ * ++ * Revision 1.58 2004/03/08 01:59:08 ken ++ * freeswan.h -> openswan.h ++ * ++ * Revision 1.57 2003/12/10 01:20:19 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.56 2003/12/04 23:01:12 mcr ++ * removed ipsec_netlink.h ++ * ++ * Revision 1.55 2003/11/07 01:30:37 ken ++ * Cast sizeof() to int to keep things 64bit clean ++ * ++ * Revision 1.54 2003/10/31 02:27:12 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.53.20.2 2003/10/29 01:11:32 mcr ++ * added debugging for pfkey library. ++ * ++ * Revision 1.53.20.1 2003/09/21 13:59:44 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.53 2003/01/30 02:32:09 rgb ++ * ++ * Rename SAref table macro names for clarity. ++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. ++ * ++ * Revision 1.52 2002/12/30 06:53:07 mcr ++ * deal with short SA structures... #if 0 out for now. Probably ++ * not quite the right way. ++ * ++ * Revision 1.51 2002/12/13 18:16:02 mcr ++ * restored sa_ref code ++ * ++ * Revision 1.50 2002/12/13 18:06:52 mcr ++ * temporarily removed sadb_x_sa_ref reference for 2.xx ++ * ++ * Revision 1.49 2002/10/05 05:02:58 dhr ++ * ++ * C labels go on statements ++ * ++ * Revision 1.48 2002/09/20 15:40:45 rgb ++ * Added sadb_x_sa_ref to struct sadb_sa. ++ * ++ * Revision 1.47 2002/09/20 05:01:31 rgb ++ * Fixed usage of pfkey_lib_debug. ++ * Format for function declaration style consistency. ++ * Added text labels to elucidate numeric values presented. ++ * Re-organised debug output to reduce noise in output. ++ * ++ * Revision 1.46 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.45 2002/05/23 07:14:11 rgb ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.44 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.43 2002/04/24 07:36:40 mcr ++ * Moved from ./lib/pfkey_v2_parse.c,v ++ * ++ * Revision 1.42 2002/01/29 22:25:36 rgb ++ * Re-add ipsec_kversion.h to keep MALLOC happy. ++ * ++ * Revision 1.41 2002/01/29 01:59:10 mcr ++ * removal of kversions.h - sources that needed it now use ipsec_param.h. ++ * updating of IPv6 structures to match latest in6.h version. ++ * removed dead code from openswan.h that also duplicated kversions.h ++ * code. ++ * ++ * Revision 1.40 2002/01/20 20:34:50 mcr ++ * added pfkey_v2_sadb_type_string to decode sadb_type to string. ++ * ++ * Revision 1.39 2001/11/27 05:29:22 mcr ++ * pfkey parses are now maintained by a structure ++ * that includes their name for debug purposes. ++ * DEBUGGING() macro changed so that it takes a debug ++ * level so that pf_key() can use this to decode the ++ * structures without innundanting humans. ++ * Also uses pfkey_v2_sadb_ext_string() in messages. ++ * ++ * Revision 1.38 2001/11/06 19:47:47 rgb ++ * Added packet parameter to lifetime and comb structures. ++ * ++ * Revision 1.37 2001/10/18 04:45:24 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/openswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.36 2001/06/14 19:35:16 rgb ++ * Update copyright date. ++ * ++ * Revision 1.35 2001/05/03 19:44:51 rgb ++ * Standardise on SENDERR() macro. ++ * ++ * Revision 1.34 2001/03/16 07:41:51 rgb ++ * Put openswan.h include before pluto includes. ++ * ++ * Revision 1.33 2001/02/27 07:13:51 rgb ++ * Added satype2name() function. ++ * Added text to default satype_tbl entry. ++ * Added satype2name() conversions for most satype debug output. ++ * ++ * Revision 1.32 2001/02/26 20:01:09 rgb ++ * Added internal IP protocol 61 for magic SAs. ++ * Ditch unused sadb_satype2proto[], replaced by satype2proto(). ++ * Re-formatted debug output (split lines, consistent spacing). ++ * Removed acquire, register and expire requirements for a known satype. ++ * Changed message type checking to a switch structure. ++ * Verify expected NULL auth for IPCOMP. ++ * Enforced spi > 0x100 requirement, now that pass uses a magic SA for ++ * appropriate message types. ++ * ++ * Revision 1.31 2000/12/01 07:09:00 rgb ++ * Added ipcomp sanity check to require encalgo is set. ++ * ++ * Revision 1.30 2000/11/17 18:10:30 rgb ++ * Fixed bugs mostly relating to spirange, to treat all spi variables as ++ * network byte order since this is the way PF_KEYv2 stored spis. ++ * ++ * Revision 1.29 2000/10/12 00:02:39 rgb ++ * Removed 'format, ##' nonsense from debug macros for RH7.0. ++ * ++ * Revision 1.28 2000/09/20 16:23:04 rgb ++ * Remove over-paranoid extension check in the presence of sadb_msg_errno. ++ * ++ * Revision 1.27 2000/09/20 04:04:21 rgb ++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in ++ * oopsen. ++ * ++ * Revision 1.26 2000/09/15 11:37:02 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.25 2000/09/12 22:35:37 rgb ++ * Restructured to remove unused extensions from CLEARFLOW messages. ++ * ++ * Revision 1.24 2000/09/12 18:59:54 rgb ++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan. ++ * ++ * Revision 1.23 2000/09/12 03:27:00 rgb ++ * Moved DEBUGGING definition to compile kernel with debug off. ++ * ++ * Revision 1.22 2000/09/09 06:39:27 rgb ++ * Restrict pfkey errno check to downward messages only. ++ * ++ * Revision 1.21 2000/09/08 19:22:34 rgb ++ * Enabled pfkey_sens_parse(). ++ * Added check for errno on downward acquire messages only. ++ * ++ * Revision 1.20 2000/09/01 18:48:23 rgb ++ * Fixed reserved check bug and added debug output in ++ * pfkey_supported_parse(). ++ * Fixed debug output label bug in pfkey_ident_parse(). ++ * ++ * Revision 1.19 2000/08/27 01:55:26 rgb ++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. ++ * ++ * Revision 1.18 2000/08/24 17:00:36 rgb ++ * Ignore unknown extensions instead of failing. ++ * ++ * Revision 1.17 2000/06/02 22:54:14 rgb ++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. ++ * ++ * Revision 1.16 2000/05/10 19:25:11 rgb ++ * Fleshed out proposal and supported extensions. ++ * ++ * Revision 1.15 2000/01/24 21:15:31 rgb ++ * Added disabled pluto pfkey lib debug flag. ++ * Added algo debugging reporting. ++ * ++ * Revision 1.14 2000/01/22 23:24:29 rgb ++ * Added new functions proto2satype() and satype2proto() and lookup ++ * table satype_tbl. Also added proto2name() since it was easy. ++ * ++ * Revision 1.13 2000/01/21 09:43:59 rgb ++ * Cast ntohl(spi) as (unsigned long int) to shut up compiler. ++ * ++ * Revision 1.12 2000/01/21 06:28:19 rgb ++ * Added address cases for eroute flows. ++ * Indented compiler directives for readability. ++ * Added klipsdebug switching capability. ++ * ++ * Revision 1.11 1999/12/29 21:14:59 rgb ++ * Fixed debug text cut and paste typo. ++ * ++ * Revision 1.10 1999/12/10 17:45:24 rgb ++ * Added address debugging. ++ * ++ * Revision 1.9 1999/12/09 23:11:42 rgb ++ * Ditched include since we no longer use memset(). ++ * Use new pfkey_extensions_init() instead of memset(). ++ * Added check for SATYPE in pfkey_msg_build(). ++ * Tidy up comments and debugging comments. ++ * ++ * Revision 1.8 1999/12/07 19:55:26 rgb ++ * Removed unused first argument from extension parsers. ++ * Removed static pluto debug flag. ++ * Moved message type and state checking to pfkey_msg_parse(). ++ * Changed print[fk] type from lx to x to quiet compiler. ++ * Removed redundant remain check. ++ * Changed __u* types to uint* to avoid use of asm/types.h and ++ * sys/types.h in userspace code. ++ * ++ * Revision 1.7 1999/12/01 22:20:51 rgb ++ * Moved pfkey_lib_debug variable into the library. ++ * Added pfkey version check into header parsing. ++ * Added check for SATYPE only for those extensions that require a ++ * non-zero value. ++ * ++ * Revision 1.6 1999/11/27 11:58:05 rgb ++ * Added ipv6 headers. ++ * Moved sadb_satype2proto protocol lookup table from ++ * klips/net/ipsec/pfkey_v2_parser.c. ++ * Enable lifetime_current checking. ++ * Debugging error messages added. ++ * Add argument to pfkey_msg_parse() for direction. ++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. ++ * Add CVS log entry to bottom of file. ++ * Moved auth and enc alg check to pfkey_msg_parse(). ++ * Enable accidentally disabled spirange parsing. ++ * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/prng.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,201 @@ ++/* ++ * crypto-class pseudorandom number generator ++ * currently uses same algorithm as RC4(TM), from Schneier 2nd ed p397 ++ * Copyright (C) 2002 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: prng.c,v 1.6 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - prng_init - initialize PRNG from a key ++ */ ++void ++prng_init(prng, key, keylen) ++struct prng *prng; ++const unsigned char *key; ++size_t keylen; ++{ ++ unsigned char k[256]; ++ int i, j; ++ unsigned const char *p; ++ unsigned const char *keyend = key + keylen; ++ unsigned char t; ++ ++ for (i = 0; i <= 255; i++) ++ prng->sbox[i] = i; ++ p = key; ++ for (i = 0; i <= 255; i++) { ++ k[i] = *p++; ++ if (p >= keyend) ++ p = key; ++ } ++ j = 0; ++ for (i = 0; i <= 255; i++) { ++ j = (j + prng->sbox[i] + k[i]) & 0xff; ++ t = prng->sbox[i]; ++ prng->sbox[i] = prng->sbox[j]; ++ prng->sbox[j] = t; ++ k[i] = 0; /* clear out key memory */ ++ } ++ prng->i = 0; ++ prng->j = 0; ++ prng->count = 0; ++} ++ ++/* ++ - prng_bytes - get some pseudorandom bytes from PRNG ++ */ ++void ++prng_bytes(prng, dst, dstlen) ++struct prng *prng; ++unsigned char *dst; ++size_t dstlen; ++{ ++ int i, j, t; ++ unsigned char *p = dst; ++ size_t remain = dstlen; ++# define MAX 4000000000ul ++ ++ while (remain > 0) { ++ i = (prng->i + 1) & 0xff; ++ prng->i = i; ++ j = (prng->j + prng->sbox[i]) & 0xff; ++ prng->j = j; ++ t = prng->sbox[i]; ++ prng->sbox[i] = prng->sbox[j]; ++ prng->sbox[j] = t; ++ t = (t + prng->sbox[i]) & 0xff; ++ *p++ = prng->sbox[t]; ++ remain--; ++ } ++ if (prng->count < MAX - dstlen) ++ prng->count += dstlen; ++ else ++ prng->count = MAX; ++} ++ ++/* ++ - prnt_count - how many bytes have been extracted from PRNG so far? ++ */ ++unsigned long ++prng_count(prng) ++struct prng *prng; ++{ ++ return prng->count; ++} ++ ++/* ++ - prng_final - clear out PRNG to ensure nothing left in memory ++ */ ++void ++prng_final(prng) ++struct prng *prng; ++{ ++ int i; ++ ++ for (i = 0; i <= 255; i++) ++ prng->sbox[i] = 0; ++ prng->i = 0; ++ prng->j = 0; ++ prng->count = 0; /* just for good measure */ ++} ++ ++ ++ ++#ifdef PRNG_MAIN ++ ++#include ++ ++void regress(); ++ ++int ++main(argc, argv) ++int argc; ++char *argv[]; ++{ ++ struct prng pr; ++ unsigned char buf[100]; ++ unsigned char *p; ++ size_t n; ++ ++ if (argc < 2) { ++ fprintf(stderr, "Usage: %s {key|-r}\n", argv[0]); ++ exit(2); ++ } ++ ++ if (strcmp(argv[1], "-r") == 0) { ++ regress(); ++ fprintf(stderr, "regress() returned?!?\n"); ++ exit(1); ++ } ++ ++ prng_init(&pr, argv[1], strlen(argv[1])); ++ prng_bytes(&pr, buf, 32); ++ printf("0x"); ++ for (p = buf, n = 32; n > 0; p++, n--) ++ printf("%02x", *p); ++ printf("\n%lu bytes\n", prng_count(&pr)); ++ prng_final(&pr); ++ exit(0); ++} ++ ++void ++regress() ++{ ++ struct prng pr; ++ unsigned char buf[100]; ++ unsigned char *p; ++ size_t n; ++ /* somewhat non-random sample key */ ++ unsigned char key[] = "here we go gathering nuts in May"; ++ /* first thirty bytes of output from that key */ ++ unsigned char good[] = "\x3f\x02\x8e\x4a\x2a\xea\x23\x18\x92\x7c" ++ "\x09\x52\x83\x61\xaa\x26\xce\xbb\x9d\x71" ++ "\x71\xe5\x10\x22\xaf\x60\x54\x8d\x5b\x28"; ++ int nzero, none; ++ int show = 0; ++ ++ prng_init(&pr, key, strlen(key)); ++ prng_bytes(&pr, buf, sizeof(buf)); ++ for (p = buf, n = sizeof(buf); n > 0; p++, n--) { ++ if (*p == 0) ++ nzero++; ++ if (*p == 255) ++ none++; ++ } ++ if (nzero > 3 || none > 3) { ++ fprintf(stderr, "suspiciously non-random output!\n"); ++ show = 1; ++ } ++ if (memcmp(buf, good, strlen(good)) != 0) { ++ fprintf(stderr, "incorrect output!\n"); ++ show = 1; ++ } ++ if (show) { ++ fprintf(stderr, "0x"); ++ for (p = buf, n = sizeof(buf); n > 0; p++, n--) ++ fprintf(stderr, "%02x", *p); ++ fprintf(stderr, "\n"); ++ exit(1); ++ } ++ if (prng_count(&pr) != sizeof(buf)) { ++ fprintf(stderr, "got %u bytes, but count is %lu\n", ++ sizeof(buf), prng_count(&pr)); ++ exit(1); ++ } ++ prng_final(&pr); ++ exit(0); ++} ++ ++#endif /* PRNG_MAIN */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/rangetoa.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,60 @@ ++/* ++ * convert binary form of address range to ASCII ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: rangetoa.c,v 1.8 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - rangetoa - convert address range to ASCII ++ */ ++size_t /* space needed for full conversion */ ++rangetoa(addrs, format, dst, dstlen) ++struct in_addr addrs[2]; ++int format; /* character */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ size_t len; ++ size_t rest; ++ int n; ++ char *p; ++ ++ switch (format) { ++ case 0: ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ len = addrtoa(addrs[0], 0, dst, dstlen); ++ if (len < dstlen) ++ for (p = dst + len - 1, n = 3; len < dstlen && n > 0; ++ p++, len++, n--) ++ *p = '.'; ++ else ++ p = NULL; ++ if (len < dstlen) ++ rest = dstlen - len; ++ else { ++ if (dstlen > 0) ++ *(dst + dstlen - 1) = '\0'; ++ rest = 0; ++ } ++ ++ len += addrtoa(addrs[1], 0, p, rest); ++ ++ return len; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/satot.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,133 @@ ++/* ++ * convert from binary form of SA ID to text ++ * Copyright (C) 2000, 2001 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: satot.c,v 1.12 2004/04/11 17:10:21 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++static struct typename { ++ char type; ++ char *name; ++} typenames[] = { ++ { SA_AH, "ah" }, ++ { SA_ESP, "esp" }, ++ { SA_IPIP, "tun" }, ++ { SA_COMP, "comp" }, ++ { SA_INT, "int" }, ++ { 0, NULL } ++}; ++ ++/* ++ - satot - convert SA to text "ah507@1.2.3.4" ++ */ ++size_t /* space needed for full conversion */ ++satot(sa, format, dst, dstlen) ++const ip_said *sa; ++int format; /* character */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ size_t len = 0; /* 0 means "not recognized yet" */ ++ int base; ++ int showversion; /* use delimiter to show IP version? */ ++ struct typename *tn; ++ char *p; ++ char *pre; ++ char buf[10+1+ULTOT_BUF+ADDRTOT_BUF]; ++ char unk[10]; ++ ++ switch (format) { ++ case 0: ++ base = 16; ++ showversion = 1; ++ break; ++ case 'f': ++ base = 17; ++ showversion = 1; ++ break; ++ case 'x': ++ base = 'x'; ++ showversion = 0; ++ break; ++ case 'd': ++ base = 10; ++ showversion = 0; ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ pre = NULL; ++ for (tn = typenames; tn->name != NULL; tn++) ++ if (sa->proto == tn->type) { ++ pre = tn->name; ++ break; /* NOTE BREAK OUT */ ++ } ++ if (pre == NULL) { /* unknown protocol */ ++ strcpy(unk, "unk"); ++ (void) ultot((unsigned char)sa->proto, 10, unk+strlen(unk), ++ sizeof(unk)-strlen(unk)); ++ pre = unk; ++ } ++ ++ if (strcmp(pre, PASSTHROUGHTYPE) == 0 && ++ sa->spi == PASSTHROUGHSPI && ++ isunspecaddr(&sa->dst)) { ++ strcpy(buf, (addrtypeof(&sa->dst) == AF_INET) ? ++ PASSTHROUGH4NAME : ++ PASSTHROUGH6NAME); ++ len = strlen(buf); ++ } ++ ++ if (sa->proto == SA_INT) { ++ switch (ntohl(sa->spi)) { ++ case SPI_PASS: p = "%pass"; break; ++ case SPI_DROP: p = "%drop"; break; ++ case SPI_REJECT: p = "%reject"; break; ++ case SPI_HOLD: p = "%hold"; break; ++ case SPI_TRAP: p = "%trap"; break; ++ case SPI_TRAPSUBNET: p = "%trapsubnet"; break; ++ default: p = NULL; break; ++ } ++ if (p != NULL) { ++ strcpy(buf, p); ++ len = strlen(buf); ++ } ++ } ++ ++ if (len == 0) { /* general case needed */ ++ strcpy(buf, pre); ++ len = strlen(buf); ++ if (showversion) { ++ *(buf+len) = (addrtypeof(&sa->dst) == AF_INET) ? '.' : ++ ':'; ++ len++; ++ *(buf+len) = '\0'; ++ } ++ len += ultot(ntohl(sa->spi), base, buf+len, sizeof(buf)-len); ++ *(buf+len-1) = '@'; ++ len += addrtot(&sa->dst, 0, buf+len, sizeof(buf)-len); ++ *(buf+len) = '\0'; ++ } ++ ++ if (dst != NULL) { ++ if (len > dstlen) ++ *(buf+dstlen-1) = '\0'; ++ strcpy(dst, buf); ++ } ++ return len; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/subnetof.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,59 @@ ++/* ++ * minor network-address manipulation utilities ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: subnetof.c,v 1.7 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - subnetof - given address and mask, return subnet part ++ */ ++struct in_addr ++subnetof(addr, mask) ++struct in_addr addr; ++struct in_addr mask; ++{ ++ struct in_addr result; ++ ++ result.s_addr = addr.s_addr & mask.s_addr; ++ return result; ++} ++ ++/* ++ - hostof - given address and mask, return host part ++ */ ++struct in_addr ++hostof(addr, mask) ++struct in_addr addr; ++struct in_addr mask; ++{ ++ struct in_addr result; ++ ++ result.s_addr = addr.s_addr & ~mask.s_addr; ++ return result; ++} ++ ++/* ++ - broadcastof - given (network) address and mask, return broadcast address ++ */ ++struct in_addr ++broadcastof(addr, mask) ++struct in_addr addr; ++struct in_addr mask; ++{ ++ struct in_addr result; ++ ++ result.s_addr = addr.s_addr | ~mask.s_addr; ++ return result; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/subnettoa.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,61 @@ ++/* ++ * convert binary form of subnet description to ASCII ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: subnettoa.c,v 1.10 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - subnettoa - convert address and mask to ASCII "addr/mask" ++ * Output expresses the mask as a bit count if possible, else dotted decimal. ++ */ ++size_t /* space needed for full conversion */ ++subnettoa(addr, mask, format, dst, dstlen) ++struct in_addr addr; ++struct in_addr mask; ++int format; /* character */ ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ size_t len; ++ size_t rest; ++ int n; ++ char *p; ++ ++ switch (format) { ++ case 0: ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ len = addrtoa(addr, 0, dst, dstlen); ++ if (len < dstlen) { ++ dst[len - 1] = '/'; ++ p = dst + len; ++ rest = dstlen - len; ++ } else { ++ p = NULL; ++ rest = 0; ++ } ++ ++ n = masktobits(mask); ++ if (n >= 0) ++ len += ultoa((unsigned long)n, 10, p, rest); ++ else ++ len += addrtoa(mask, 0, p, rest); ++ ++ return len; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/ultoa.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,66 @@ ++/* ++ * convert unsigned long to ASCII ++ * Copyright (C) 1998, 1999 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: ultoa.c,v 1.9 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - ultoa - convert unsigned long to decimal ASCII ++ */ ++size_t /* length required for full conversion */ ++ultoa(n, base, dst, dstlen) ++unsigned long n; ++int base; ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ char buf[3*sizeof(unsigned long) + 1]; ++ char *bufend = buf + sizeof(buf); ++ size_t len; ++ char *p; ++ static char hex[] = "0123456789abcdef"; ++ ++ p = bufend; ++ *--p = '\0'; ++ if (base == 10) { ++ do { ++ *--p = n%10 + '0'; ++ n /= 10; ++ } while (n != 0); ++ } else if (base == 16) { ++ do { ++ *--p = hex[n&0xf]; ++ n >>= 4; ++ } while (n != 0); ++ *--p = 'x'; ++ *--p = '0'; ++ } else if (base == 8) { ++ do { ++ *--p = (n&07) + '0'; ++ n >>= 3; ++ } while (n != 0); ++ *--p = '0'; ++ } else ++ *--p = '?'; ++ ++ len = bufend - p; ++ ++ if (dstlen > 0) { ++ if (len > dstlen) ++ *(p + dstlen - 1) = '\0'; ++ strcpy(dst, p); ++ } ++ return len; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/libfreeswan/ultot.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,82 @@ ++/* ++ * convert unsigned long to text ++ * Copyright (C) 2000 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: ultot.c,v 1.4 2004/04/11 17:39:25 mcr Exp $ ++ */ ++#include "openswan.h" ++ ++/* ++ - ultot - convert unsigned long to text ++ */ ++size_t /* length required for full conversion */ ++ultot(n, base, dst, dstlen) ++unsigned long n; ++int base; ++char *dst; /* need not be valid if dstlen is 0 */ ++size_t dstlen; ++{ ++ char buf[3*sizeof(unsigned long) + 1]; ++ char *bufend = buf + sizeof(buf); ++ size_t len; ++ char *p; ++ static char hex[] = "0123456789abcdef"; ++# define HEX32 (32/4) ++ ++ p = bufend; ++ *--p = '\0'; ++ switch (base) { ++ case 10: ++ case 'd': ++ do { ++ *--p = n%10 + '0'; ++ n /= 10; ++ } while (n != 0); ++ break; ++ case 16: ++ case 17: ++ case 'x': ++ do { ++ *--p = hex[n&0xf]; ++ n >>= 4; ++ } while (n != 0); ++ if (base == 17) ++ while (bufend - p < HEX32 + 1) ++ *--p = '0'; ++ if (base == 'x') { ++ *--p = 'x'; ++ *--p = '0'; ++ } ++ break; ++ case 8: ++ case 'o': ++ do { ++ *--p = (n&07) + '0'; ++ n >>= 3; ++ } while (n != 0); ++ if (base == 'o') ++ *--p = '0'; ++ break; ++ default: ++ return 0; ++ break; ++ } ++ ++ len = bufend - p; ++ if (dstlen > 0) { ++ if (len > dstlen) ++ *(p + dstlen - 1) = '\0'; ++ strcpy(dst, p); ++ } ++ return len; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/Makefile Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,118 @@ ++# (kernel) Makefile for IPCOMP zlib deflate code ++# Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++# Copyright (C) 2000 Svenning Soerensen ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. See . ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# for more details. ++# ++# RCSID $Id: Makefile,v 1.9 2002/04/24 07:55:32 mcr Exp $ ++# ++ ++ ++ ++include ../Makefile.inc ++ ++ ++ ++ifndef TOPDIR ++TOPDIR := /usr/src/linux ++endif ++ ++ ++L_TARGET := zlib.a ++ ++obj-y := ++ ++include Makefile.objs ++ ++EXTRA_CFLAGS += $(KLIPSCOMPILE) ++ ++EXTRA_CFLAGS += -Wall ++#EXTRA_CFLAGS += -Wconversion ++#EXTRA_CFLAGS += -Wmissing-prototypes ++EXTRA_CFLAGS += -Wpointer-arith ++#EXTRA_CFLAGS += -Wcast-qual ++#EXTRA_CFLAGS += -Wmissing-declarations ++EXTRA_CFLAGS += -Wstrict-prototypes ++#EXTRA_CFLAGS += -pedantic ++#EXTRA_CFLAGS += -W ++#EXTRA_CFLAGS += -Wwrite-strings ++EXTRA_CFLAGS += -Wbad-function-cast ++EXTRA_CFLAGS += -DIPCOMP_PREFIX ++ ++.S.o: ++ $(CC) -D__ASSEMBLY__ -DNO_UNDERLINE -traditional -c $< -o $*.o ++ ++asm-obj-$(CONFIG_M586) += match586.o ++asm-obj-$(CONFIG_M586TSC) += match586.o ++asm-obj-$(CONFIG_M586MMX) += match586.o ++asm-obj-$(CONFIG_M686) += match686.o ++asm-obj-$(CONFIG_MPENTIUMIII) += match686.o ++asm-obj-$(CONFIG_MPENTIUM4) += match686.o ++asm-obj-$(CONFIG_MK6) += match586.o ++asm-obj-$(CONFIG_MK7) += match686.o ++asm-obj-$(CONFIG_MCRUSOE) += match586.o ++asm-obj-$(CONFIG_MWINCHIPC6) += match586.o ++asm-obj-$(CONFIG_MWINCHIP2) += match686.o ++asm-obj-$(CONFIG_MWINCHIP3D) += match686.o ++ ++obj-y += $(asm-obj-y) ++ifneq ($(strip $(asm-obj-y)),) ++ EXTRA_CFLAGS += -DASMV ++endif ++ ++active-objs := $(sort $(obj-y) $(obj-m)) ++L_OBJS := $(obj-y) ++M_OBJS := $(obj-m) ++MIX_OBJS := $(filter $(export-objs), $(active-objs)) ++ ++include $(TOPDIR)/Rules.make ++ ++$(obj-y) : $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h ++ ++ ++clean: ++ -rm -f *.o *.a ++ ++checkprograms: ++programs: $(L_TARGET) ++ ++# ++# $Log: Makefile,v $ ++# Revision 1.9 2002/04/24 07:55:32 mcr ++# #include patches and Makefiles for post-reorg compilation. ++# ++# Revision 1.8 2002/04/24 07:36:44 mcr ++# Moved from ./zlib/Makefile,v ++# ++# Revision 1.7 2002/03/27 23:34:35 mcr ++# added programs: target ++# ++# Revision 1.6 2001/12/05 20:19:08 henry ++# use new compile-control variable ++# ++# Revision 1.5 2001/11/27 16:38:08 mcr ++# added new "checkprograms" target to deal with programs that ++# are required for "make check", but that may not be ready to ++# build for every user due to external dependancies. ++# ++# Revision 1.4 2001/10/24 14:46:24 henry ++# Makefile.inc ++# ++# Revision 1.3 2001/04/21 23:05:24 rgb ++# Update asm directives for 2.4 style makefiles. ++# ++# Revision 1.2 2001/01/29 22:22:00 rgb ++# Convert to 2.4 new style with back compat. ++# ++# Revision 1.1.1.1 2000/09/29 18:51:33 rgb ++# zlib_beginnings ++# ++# +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/Makefile.objs Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,27 @@ ++obj-$(CONFIG_IPSEC_IPCOMP) += adler32.o ++obj-$(CONFIG_IPSEC_IPCOMP) += deflate.o ++obj-$(CONFIG_IPSEC_IPCOMP) += infblock.o ++obj-$(CONFIG_IPSEC_IPCOMP) += infcodes.o ++obj-$(CONFIG_IPSEC_IPCOMP) += inffast.o ++obj-$(CONFIG_IPSEC_IPCOMP) += inflate.o ++obj-$(CONFIG_IPSEC_IPCOMP) += inftrees.o ++obj-$(CONFIG_IPSEC_IPCOMP) += infutil.o ++obj-$(CONFIG_IPSEC_IPCOMP) += trees.o ++obj-$(CONFIG_IPSEC_IPCOMP) += zutil.o ++ ++asm-obj-$(CONFIG_M586) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_M586TSC) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_M586MMX) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_M686) += ${LIBZLIBSRCDIR}/match686.o ++asm-obj-$(CONFIG_MPENTIUMIII) += ${LIBZLIBSRCDIR}/match686.o ++asm-obj-$(CONFIG_MPENTIUM4) += ${LIBZLIBSRCDIR}/match686.o ++asm-obj-$(CONFIG_MK6) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_MK7) += ${LIBZLIBSRCDIR}/match686.o ++asm-obj-$(CONFIG_MCRUSOE) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_MWINCHIPC6) += ${LIBZLIBSRCDIR}/match586.o ++asm-obj-$(CONFIG_MWINCHIP2) += ${LIBZLIBSRCDIR}/match686.o ++asm-obj-$(CONFIG_MWINCHIP3D) += ${LIBZLIBSRCDIR}/match686.o ++ ++EXTRA_CFLAGS += -DIPCOMP_PREFIX ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/README Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,147 @@ ++zlib 1.1.4 is a general purpose data compression library. All the code ++is thread safe. The data format used by the zlib library ++is described by RFCs (Request for Comments) 1950 to 1952 in the files ++http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate ++format) and rfc1952.txt (gzip format). These documents are also available in ++other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html ++ ++All functions of the compression library are documented in the file zlib.h ++(volunteer to write man pages welcome, contact jloup@gzip.org). A usage ++example of the library is given in the file example.c which also tests that ++the library is working correctly. Another example is given in the file ++minigzip.c. The compression library itself is composed of all source files ++except example.c and minigzip.c. ++ ++To compile all files and run the test program, follow the instructions ++given at the top of Makefile. In short "make test; make install" ++should work for most machines. For Unix: "./configure; make test; make install" ++For MSDOS, use one of the special makefiles such as Makefile.msc. ++For VMS, use Make_vms.com or descrip.mms. ++ ++Questions about zlib should be sent to , or to ++Gilles Vollant for the Windows DLL version. ++The zlib home page is http://www.zlib.org or http://www.gzip.org/zlib/ ++Before reporting a problem, please check this site to verify that ++you have the latest version of zlib; otherwise get the latest version and ++check whether the problem still exists or not. ++ ++PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html ++before asking for help. ++ ++Mark Nelson wrote an article about zlib for the Jan. 1997 ++issue of Dr. Dobb's Journal; a copy of the article is available in ++http://dogma.net/markn/articles/zlibtool/zlibtool.htm ++ ++The changes made in version 1.1.4 are documented in the file ChangeLog. ++The only changes made since 1.1.3 are bug corrections: ++ ++- ZFREE was repeated on same allocation on some error conditions. ++ This creates a security problem described in ++ http://www.zlib.org/advisory-2002-03-11.txt ++- Returned incorrect error (Z_MEM_ERROR) on some invalid data ++- Avoid accesses before window for invalid distances with inflate window ++ less than 32K. ++- force windowBits > 8 to avoid a bug in the encoder for a window size ++ of 256 bytes. (A complete fix will be available in 1.1.5). ++ ++The beta version 1.1.5beta includes many more changes. A new official ++version 1.1.5 will be released as soon as extensive testing has been ++completed on it. ++ ++ ++Unsupported third party contributions are provided in directory "contrib". ++ ++A Java implementation of zlib is available in the Java Development Kit ++http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html ++See the zlib home page http://www.zlib.org for details. ++ ++A Perl interface to zlib written by Paul Marquess ++is in the CPAN (Comprehensive Perl Archive Network) sites ++http://www.cpan.org/modules/by-module/Compress/ ++ ++A Python interface to zlib written by A.M. Kuchling ++is available in Python 1.5 and later versions, see ++http://www.python.org/doc/lib/module-zlib.html ++ ++A zlib binding for TCL written by Andreas Kupries ++is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html ++ ++An experimental package to read and write files in .zip format, ++written on top of zlib by Gilles Vollant , is ++available at http://www.winimage.com/zLibDll/unzip.html ++and also in the contrib/minizip directory of zlib. ++ ++ ++Notes for some targets: ++ ++- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc ++ and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL ++ The zlib DLL support was initially done by Alessandro Iacopetti and is ++ now maintained by Gilles Vollant . Check the zlib DLL ++ home page at http://www.winimage.com/zLibDll ++ ++ From Visual Basic, you can call the DLL functions which do not take ++ a structure as argument: compress, uncompress and all gz* functions. ++ See contrib/visual-basic.txt for more information, or get ++ http://www.tcfb.com/dowseware/cmp-z-it.zip ++ ++- For 64-bit Irix, deflate.c must be compiled without any optimization. ++ With -O, one libpng test fails. The test works in 32 bit mode (with ++ the -n32 compiler flag). The compiler bug has been reported to SGI. ++ ++- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 ++ it works when compiled with cc. ++ ++- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 ++ is necessary to get gzprintf working correctly. This is done by configure. ++ ++- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works ++ with other compilers. Use "make test" to check your compiler. ++ ++- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. ++ ++- For Turbo C the small model is supported only with reduced performance to ++ avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 ++ ++- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html ++ Per Harald Myrvang ++ ++ ++Acknowledgments: ++ ++ The deflate format used by zlib was defined by Phil Katz. The deflate ++ and zlib specifications were written by L. Peter Deutsch. Thanks to all the ++ people who reported problems and suggested various improvements in zlib; ++ they are too numerous to cite here. ++ ++Copyright notice: ++ ++ (C) 1995-2002 Jean-loup Gailly and Mark Adler ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++ ++ Jean-loup Gailly Mark Adler ++ jloup@gzip.org madler@alumni.caltech.edu ++ ++If you use the zlib library in a product, we would appreciate *not* ++receiving lengthy legal documents to sign. The sources are provided ++for free but without warranty of any kind. The library has been ++entirely written by Jean-loup Gailly and Mark Adler; it does not ++include third-party code. ++ ++If you redistribute modified sources, we would appreciate that you include ++in the file ChangeLog history information documenting your changes. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/README.freeswan Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,13 @@ ++The only changes made to these files for use in FreeS/WAN are: ++ ++ - In zconf.h, macros are defined to prefix global symbols with "ipcomp_" ++ (or "_ipcomp"), when compiled with -DIPCOMP_PREFIX. ++ - The copyright strings are defined local (static) ++ ++ The above changes are made to avoid name collisions with ppp_deflate ++ and ext2compr. ++ ++ - Files not needed for FreeS/WAN have been removed ++ ++ See the "README" file for information about where to obtain the complete ++ zlib package. +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/adler32.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,49 @@ ++/* adler32.c -- compute the Adler-32 checksum of a data stream ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* @(#) $Id: adler32.c,v 1.4 2002/04/24 07:55:32 mcr Exp $ */ ++ ++#include ++#include "zconf.h" ++ ++#define BASE 65521L /* largest prime smaller than 65536 */ ++#define NMAX 5552 ++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ ++ ++#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} ++#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); ++#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); ++#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); ++#define DO16(buf) DO8(buf,0); DO8(buf,8); ++ ++/* ========================================================================= */ ++uLong ZEXPORT adler32(adler, buf, len) ++ uLong adler; ++ const Bytef *buf; ++ uInt len; ++{ ++ unsigned long s1 = adler & 0xffff; ++ unsigned long s2 = (adler >> 16) & 0xffff; ++ int k; ++ ++ if (buf == Z_NULL) return 1L; ++ ++ while (len > 0) { ++ k = len < NMAX ? len : NMAX; ++ len -= k; ++ while (k >= 16) { ++ DO16(buf); ++ buf += 16; ++ k -= 16; ++ } ++ if (k != 0) do { ++ s1 += *buf++; ++ s2 += s1; ++ } while (--k); ++ s1 %= BASE; ++ s2 %= BASE; ++ } ++ return (s2 << 16) | s1; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/deflate.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1351 @@ ++/* deflate.c -- compress data using the deflation algorithm ++ * Copyright (C) 1995-2002 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* ++ * ALGORITHM ++ * ++ * The "deflation" process depends on being able to identify portions ++ * of the input text which are identical to earlier input (within a ++ * sliding window trailing behind the input currently being processed). ++ * ++ * The most straightforward technique turns out to be the fastest for ++ * most input files: try all possible matches and select the longest. ++ * The key feature of this algorithm is that insertions into the string ++ * dictionary are very simple and thus fast, and deletions are avoided ++ * completely. Insertions are performed at each input character, whereas ++ * string matches are performed only when the previous match ends. So it ++ * is preferable to spend more time in matches to allow very fast string ++ * insertions and avoid deletions. The matching algorithm for small ++ * strings is inspired from that of Rabin & Karp. A brute force approach ++ * is used to find longer strings when a small match has been found. ++ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze ++ * (by Leonid Broukhis). ++ * A previous version of this file used a more sophisticated algorithm ++ * (by Fiala and Greene) which is guaranteed to run in linear amortized ++ * time, but has a larger average cost, uses more memory and is patented. ++ * However the F&G algorithm may be faster for some highly redundant ++ * files if the parameter max_chain_length (described below) is too large. ++ * ++ * ACKNOWLEDGEMENTS ++ * ++ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and ++ * I found it in 'freeze' written by Leonid Broukhis. ++ * Thanks to many people for bug reports and testing. ++ * ++ * REFERENCES ++ * ++ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". ++ * Available in ftp://ds.internic.net/rfc/rfc1951.txt ++ * ++ * A description of the Rabin and Karp algorithm is given in the book ++ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. ++ * ++ * Fiala,E.R., and Greene,D.H. ++ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 ++ * ++ */ ++ ++/* @(#) $Id: deflate.c,v 1.3 2002/04/24 07:36:44 mcr Exp $ */ ++ ++#include "deflate.h" ++ ++local const char deflate_copyright[] = ++ " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly "; ++/* ++ If you use the zlib library in a product, an acknowledgment is welcome ++ in the documentation of your product. If for some reason you cannot ++ include such an acknowledgment, I would appreciate that you keep this ++ copyright string in the executable of your product. ++ */ ++ ++/* =========================================================================== ++ * Function prototypes. ++ */ ++typedef enum { ++ need_more, /* block not completed, need more input or more output */ ++ block_done, /* block flush performed */ ++ finish_started, /* finish started, need only more output at next deflate */ ++ finish_done /* finish done, accept no more input or output */ ++} block_state; ++ ++typedef block_state (*compress_func) OF((deflate_state *s, int flush)); ++/* Compression function. Returns the block state after the call. */ ++ ++local void fill_window OF((deflate_state *s)); ++local block_state deflate_stored OF((deflate_state *s, int flush)); ++local block_state deflate_fast OF((deflate_state *s, int flush)); ++local block_state deflate_slow OF((deflate_state *s, int flush)); ++local void lm_init OF((deflate_state *s)); ++local void putShortMSB OF((deflate_state *s, uInt b)); ++local void flush_pending OF((z_streamp strm)); ++local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); ++#ifdef ASMV ++ void match_init OF((void)); /* asm code initialization */ ++ uInt longest_match OF((deflate_state *s, IPos cur_match)); ++#else ++local uInt longest_match OF((deflate_state *s, IPos cur_match)); ++#endif ++ ++#ifdef DEBUG ++local void check_match OF((deflate_state *s, IPos start, IPos match, ++ int length)); ++#endif ++ ++/* =========================================================================== ++ * Local data ++ */ ++ ++#define NIL 0 ++/* Tail of hash chains */ ++ ++#ifndef TOO_FAR ++# define TOO_FAR 4096 ++#endif ++/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ ++ ++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) ++/* Minimum amount of lookahead, except at the end of the input file. ++ * See deflate.c for comments about the MIN_MATCH+1. ++ */ ++ ++/* Values for max_lazy_match, good_match and max_chain_length, depending on ++ * the desired pack level (0..9). The values given below have been tuned to ++ * exclude worst case performance for pathological files. Better values may be ++ * found for specific files. ++ */ ++typedef struct config_s { ++ ush good_length; /* reduce lazy search above this match length */ ++ ush max_lazy; /* do not perform lazy search above this match length */ ++ ush nice_length; /* quit search above this match length */ ++ ush max_chain; ++ compress_func func; ++} config; ++ ++local const config configuration_table[10] = { ++/* good lazy nice chain */ ++/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ ++/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ ++/* 2 */ {4, 5, 16, 8, deflate_fast}, ++/* 3 */ {4, 6, 32, 32, deflate_fast}, ++ ++/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ ++/* 5 */ {8, 16, 32, 32, deflate_slow}, ++/* 6 */ {8, 16, 128, 128, deflate_slow}, ++/* 7 */ {8, 32, 128, 256, deflate_slow}, ++/* 8 */ {32, 128, 258, 1024, deflate_slow}, ++/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ ++ ++/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 ++ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different ++ * meaning. ++ */ ++ ++#define EQUAL 0 ++/* result of memcmp for equal strings */ ++ ++struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ ++ ++/* =========================================================================== ++ * Update a hash value with the given input byte ++ * IN assertion: all calls to to UPDATE_HASH are made with consecutive ++ * input characters, so that a running hash key can be computed from the ++ * previous key instead of complete recalculation each time. ++ */ ++#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) ++ ++ ++/* =========================================================================== ++ * Insert string str in the dictionary and set match_head to the previous head ++ * of the hash chain (the most recent string with same hash key). Return ++ * the previous length of the hash chain. ++ * If this file is compiled with -DFASTEST, the compression level is forced ++ * to 1, and no hash chains are maintained. ++ * IN assertion: all calls to to INSERT_STRING are made with consecutive ++ * input characters and the first MIN_MATCH bytes of str are valid ++ * (except for the last MIN_MATCH-1 bytes of the input file). ++ */ ++#ifdef FASTEST ++#define INSERT_STRING(s, str, match_head) \ ++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ ++ match_head = s->head[s->ins_h], \ ++ s->head[s->ins_h] = (Pos)(str)) ++#else ++#define INSERT_STRING(s, str, match_head) \ ++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ ++ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ ++ s->head[s->ins_h] = (Pos)(str)) ++#endif ++ ++/* =========================================================================== ++ * Initialize the hash table (avoiding 64K overflow for 16 bit systems). ++ * prev[] will be initialized on the fly. ++ */ ++#define CLEAR_HASH(s) \ ++ s->head[s->hash_size-1] = NIL; \ ++ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); ++ ++/* ========================================================================= */ ++int ZEXPORT deflateInit_(strm, level, version, stream_size) ++ z_streamp strm; ++ int level; ++ const char *version; ++ int stream_size; ++{ ++ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, ++ Z_DEFAULT_STRATEGY, version, stream_size); ++ /* To do: ignore strm->next_in if we use it as window */ ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, ++ version, stream_size) ++ z_streamp strm; ++ int level; ++ int method; ++ int windowBits; ++ int memLevel; ++ int strategy; ++ const char *version; ++ int stream_size; ++{ ++ deflate_state *s; ++ int noheader = 0; ++ static const char* my_version = ZLIB_VERSION; ++ ++ ushf *overlay; ++ /* We overlay pending_buf and d_buf+l_buf. This works since the average ++ * output size for (length,distance) codes is <= 24 bits. ++ */ ++ ++ if (version == Z_NULL || version[0] != my_version[0] || ++ stream_size != sizeof(z_stream)) { ++ return Z_VERSION_ERROR; ++ } ++ if (strm == Z_NULL) return Z_STREAM_ERROR; ++ ++ strm->msg = Z_NULL; ++ if (strm->zalloc == Z_NULL) { ++ return Z_STREAM_ERROR; ++/* strm->zalloc = zcalloc; ++ strm->opaque = (voidpf)0;*/ ++ } ++ if (strm->zfree == Z_NULL) return Z_STREAM_ERROR; /* strm->zfree = zcfree; */ ++ ++ if (level == Z_DEFAULT_COMPRESSION) level = 6; ++#ifdef FASTEST ++ level = 1; ++#endif ++ ++ if (windowBits < 0) { /* undocumented feature: suppress zlib header */ ++ noheader = 1; ++ windowBits = -windowBits; ++ } ++ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || ++ windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || ++ strategy < 0 || strategy > Z_HUFFMAN_ONLY) { ++ return Z_STREAM_ERROR; ++ } ++ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); ++ if (s == Z_NULL) return Z_MEM_ERROR; ++ strm->state = (struct internal_state FAR *)s; ++ s->strm = strm; ++ ++ s->noheader = noheader; ++ s->w_bits = windowBits; ++ s->w_size = 1 << s->w_bits; ++ s->w_mask = s->w_size - 1; ++ ++ s->hash_bits = memLevel + 7; ++ s->hash_size = 1 << s->hash_bits; ++ s->hash_mask = s->hash_size - 1; ++ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); ++ ++ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); ++ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); ++ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); ++ ++ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ ++ ++ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); ++ s->pending_buf = (uchf *) overlay; ++ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); ++ ++ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || ++ s->pending_buf == Z_NULL) { ++ strm->msg = ERR_MSG(Z_MEM_ERROR); ++ deflateEnd (strm); ++ return Z_MEM_ERROR; ++ } ++ s->d_buf = overlay + s->lit_bufsize/sizeof(ush); ++ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; ++ ++ s->level = level; ++ s->strategy = strategy; ++ s->method = (Byte)method; ++ ++ return deflateReset(strm); ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) ++ z_streamp strm; ++ const Bytef *dictionary; ++ uInt dictLength; ++{ ++ deflate_state *s; ++ uInt length = dictLength; ++ uInt n; ++ IPos hash_head = 0; ++ ++ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || ++ strm->state->status != INIT_STATE) return Z_STREAM_ERROR; ++ ++ s = strm->state; ++ strm->adler = adler32(strm->adler, dictionary, dictLength); ++ ++ if (length < MIN_MATCH) return Z_OK; ++ if (length > MAX_DIST(s)) { ++ length = MAX_DIST(s); ++#ifndef USE_DICT_HEAD ++ dictionary += dictLength - length; /* use the tail of the dictionary */ ++#endif ++ } ++ zmemcpy(s->window, dictionary, length); ++ s->strstart = length; ++ s->block_start = (long)length; ++ ++ /* Insert all strings in the hash table (except for the last two bytes). ++ * s->lookahead stays null, so s->ins_h will be recomputed at the next ++ * call of fill_window. ++ */ ++ s->ins_h = s->window[0]; ++ UPDATE_HASH(s, s->ins_h, s->window[1]); ++ for (n = 0; n <= length - MIN_MATCH; n++) { ++ INSERT_STRING(s, n, hash_head); ++ } ++ if (hash_head) hash_head = 0; /* to make compiler happy */ ++ return Z_OK; ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflateReset (strm) ++ z_streamp strm; ++{ ++ deflate_state *s; ++ ++ if (strm == Z_NULL || strm->state == Z_NULL || ++ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; ++ ++ strm->total_in = strm->total_out = 0; ++ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ ++ strm->data_type = Z_UNKNOWN; ++ ++ s = (deflate_state *)strm->state; ++ s->pending = 0; ++ s->pending_out = s->pending_buf; ++ ++ if (s->noheader < 0) { ++ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ ++ } ++ s->status = s->noheader ? BUSY_STATE : INIT_STATE; ++ strm->adler = 1; ++ s->last_flush = Z_NO_FLUSH; ++ ++ _tr_init(s); ++ lm_init(s); ++ ++ return Z_OK; ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflateParams(strm, level, strategy) ++ z_streamp strm; ++ int level; ++ int strategy; ++{ ++ deflate_state *s; ++ compress_func func; ++ int err = Z_OK; ++ ++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; ++ s = strm->state; ++ ++ if (level == Z_DEFAULT_COMPRESSION) { ++ level = 6; ++ } ++ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { ++ return Z_STREAM_ERROR; ++ } ++ func = configuration_table[s->level].func; ++ ++ if (func != configuration_table[level].func && strm->total_in != 0) { ++ /* Flush the last buffer: */ ++ err = deflate(strm, Z_PARTIAL_FLUSH); ++ } ++ if (s->level != level) { ++ s->level = level; ++ s->max_lazy_match = configuration_table[level].max_lazy; ++ s->good_match = configuration_table[level].good_length; ++ s->nice_match = configuration_table[level].nice_length; ++ s->max_chain_length = configuration_table[level].max_chain; ++ } ++ s->strategy = strategy; ++ return err; ++} ++ ++/* ========================================================================= ++ * Put a short in the pending buffer. The 16-bit value is put in MSB order. ++ * IN assertion: the stream state is correct and there is enough room in ++ * pending_buf. ++ */ ++local void putShortMSB (s, b) ++ deflate_state *s; ++ uInt b; ++{ ++ put_byte(s, (Byte)(b >> 8)); ++ put_byte(s, (Byte)(b & 0xff)); ++} ++ ++/* ========================================================================= ++ * Flush as much pending output as possible. All deflate() output goes ++ * through this function so some applications may wish to modify it ++ * to avoid allocating a large strm->next_out buffer and copying into it. ++ * (See also read_buf()). ++ */ ++local void flush_pending(strm) ++ z_streamp strm; ++{ ++ unsigned len = strm->state->pending; ++ ++ if (len > strm->avail_out) len = strm->avail_out; ++ if (len == 0) return; ++ ++ zmemcpy(strm->next_out, strm->state->pending_out, len); ++ strm->next_out += len; ++ strm->state->pending_out += len; ++ strm->total_out += len; ++ strm->avail_out -= len; ++ strm->state->pending -= len; ++ if (strm->state->pending == 0) { ++ strm->state->pending_out = strm->state->pending_buf; ++ } ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflate (strm, flush) ++ z_streamp strm; ++ int flush; ++{ ++ int old_flush; /* value of flush param for previous deflate call */ ++ deflate_state *s; ++ ++ if (strm == Z_NULL || strm->state == Z_NULL || ++ flush > Z_FINISH || flush < 0) { ++ return Z_STREAM_ERROR; ++ } ++ s = strm->state; ++ ++ if (strm->next_out == Z_NULL || ++ (strm->next_in == Z_NULL && strm->avail_in != 0) || ++ (s->status == FINISH_STATE && flush != Z_FINISH)) { ++ ERR_RETURN(strm, Z_STREAM_ERROR); ++ } ++ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); ++ ++ s->strm = strm; /* just in case */ ++ old_flush = s->last_flush; ++ s->last_flush = flush; ++ ++ /* Write the zlib header */ ++ if (s->status == INIT_STATE) { ++ ++ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; ++ uInt level_flags = (s->level-1) >> 1; ++ ++ if (level_flags > 3) level_flags = 3; ++ header |= (level_flags << 6); ++ if (s->strstart != 0) header |= PRESET_DICT; ++ header += 31 - (header % 31); ++ ++ s->status = BUSY_STATE; ++ putShortMSB(s, header); ++ ++ /* Save the adler32 of the preset dictionary: */ ++ if (s->strstart != 0) { ++ putShortMSB(s, (uInt)(strm->adler >> 16)); ++ putShortMSB(s, (uInt)(strm->adler & 0xffff)); ++ } ++ strm->adler = 1L; ++ } ++ ++ /* Flush as much pending output as possible */ ++ if (s->pending != 0) { ++ flush_pending(strm); ++ if (strm->avail_out == 0) { ++ /* Since avail_out is 0, deflate will be called again with ++ * more output space, but possibly with both pending and ++ * avail_in equal to zero. There won't be anything to do, ++ * but this is not an error situation so make sure we ++ * return OK instead of BUF_ERROR at next call of deflate: ++ */ ++ s->last_flush = -1; ++ return Z_OK; ++ } ++ ++ /* Make sure there is something to do and avoid duplicate consecutive ++ * flushes. For repeated and useless calls with Z_FINISH, we keep ++ * returning Z_STREAM_END instead of Z_BUFF_ERROR. ++ */ ++ } else if (strm->avail_in == 0 && flush <= old_flush && ++ flush != Z_FINISH) { ++ ERR_RETURN(strm, Z_BUF_ERROR); ++ } ++ ++ /* User must not provide more input after the first FINISH: */ ++ if (s->status == FINISH_STATE && strm->avail_in != 0) { ++ ERR_RETURN(strm, Z_BUF_ERROR); ++ } ++ ++ /* Start a new block or continue the current one. ++ */ ++ if (strm->avail_in != 0 || s->lookahead != 0 || ++ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { ++ block_state bstate; ++ ++ bstate = (*(configuration_table[s->level].func))(s, flush); ++ ++ if (bstate == finish_started || bstate == finish_done) { ++ s->status = FINISH_STATE; ++ } ++ if (bstate == need_more || bstate == finish_started) { ++ if (strm->avail_out == 0) { ++ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ ++ } ++ return Z_OK; ++ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call ++ * of deflate should use the same flush parameter to make sure ++ * that the flush is complete. So we don't have to output an ++ * empty block here, this will be done at next call. This also ++ * ensures that for a very small output buffer, we emit at most ++ * one empty block. ++ */ ++ } ++ if (bstate == block_done) { ++ if (flush == Z_PARTIAL_FLUSH) { ++ _tr_align(s); ++ } else { /* FULL_FLUSH or SYNC_FLUSH */ ++ _tr_stored_block(s, (char*)0, 0L, 0); ++ /* For a full flush, this empty block will be recognized ++ * as a special marker by inflate_sync(). ++ */ ++ if (flush == Z_FULL_FLUSH) { ++ CLEAR_HASH(s); /* forget history */ ++ } ++ } ++ flush_pending(strm); ++ if (strm->avail_out == 0) { ++ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ ++ return Z_OK; ++ } ++ } ++ } ++ Assert(strm->avail_out > 0, "bug2"); ++ ++ if (flush != Z_FINISH) return Z_OK; ++ if (s->noheader) return Z_STREAM_END; ++ ++ /* Write the zlib trailer (adler32) */ ++ putShortMSB(s, (uInt)(strm->adler >> 16)); ++ putShortMSB(s, (uInt)(strm->adler & 0xffff)); ++ flush_pending(strm); ++ /* If avail_out is zero, the application will call deflate again ++ * to flush the rest. ++ */ ++ s->noheader = -1; /* write the trailer only once! */ ++ return s->pending != 0 ? Z_OK : Z_STREAM_END; ++} ++ ++/* ========================================================================= */ ++int ZEXPORT deflateEnd (strm) ++ z_streamp strm; ++{ ++ int status; ++ ++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; ++ ++ status = strm->state->status; ++ if (status != INIT_STATE && status != BUSY_STATE && ++ status != FINISH_STATE) { ++ return Z_STREAM_ERROR; ++ } ++ ++ /* Deallocate in reverse order of allocations: */ ++ TRY_FREE(strm, strm->state->pending_buf); ++ TRY_FREE(strm, strm->state->head); ++ TRY_FREE(strm, strm->state->prev); ++ TRY_FREE(strm, strm->state->window); ++ ++ ZFREE(strm, strm->state); ++ strm->state = Z_NULL; ++ ++ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; ++} ++ ++/* ========================================================================= ++ * Copy the source state to the destination state. ++ * To simplify the source, this is not supported for 16-bit MSDOS (which ++ * doesn't have enough memory anyway to duplicate compression states). ++ */ ++int ZEXPORT deflateCopy (dest, source) ++ z_streamp dest; ++ z_streamp source; ++{ ++#ifdef MAXSEG_64K ++ return Z_STREAM_ERROR; ++#else ++ deflate_state *ds; ++ deflate_state *ss; ++ ushf *overlay; ++ ++ ++ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { ++ return Z_STREAM_ERROR; ++ } ++ ++ ss = source->state; ++ ++ *dest = *source; ++ ++ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); ++ if (ds == Z_NULL) return Z_MEM_ERROR; ++ dest->state = (struct internal_state FAR *) ds; ++ *ds = *ss; ++ ds->strm = dest; ++ ++ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ++ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ++ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); ++ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ++ ds->pending_buf = (uchf *) overlay; ++ ++ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ++ ds->pending_buf == Z_NULL) { ++ deflateEnd (dest); ++ return Z_MEM_ERROR; ++ } ++ /* following zmemcpy do not work for 16-bit MSDOS */ ++ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); ++ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); ++ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); ++ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ++ ++ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ++ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ++ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ++ ++ ds->l_desc.dyn_tree = ds->dyn_ltree; ++ ds->d_desc.dyn_tree = ds->dyn_dtree; ++ ds->bl_desc.dyn_tree = ds->bl_tree; ++ ++ return Z_OK; ++#endif ++} ++ ++/* =========================================================================== ++ * Read a new buffer from the current input stream, update the adler32 ++ * and total number of bytes read. All deflate() input goes through ++ * this function so some applications may wish to modify it to avoid ++ * allocating a large strm->next_in buffer and copying from it. ++ * (See also flush_pending()). ++ */ ++local int read_buf(strm, buf, size) ++ z_streamp strm; ++ Bytef *buf; ++ unsigned size; ++{ ++ unsigned len = strm->avail_in; ++ ++ if (len > size) len = size; ++ if (len == 0) return 0; ++ ++ strm->avail_in -= len; ++ ++ if (!strm->state->noheader) { ++ strm->adler = adler32(strm->adler, strm->next_in, len); ++ } ++ zmemcpy(buf, strm->next_in, len); ++ strm->next_in += len; ++ strm->total_in += len; ++ ++ return (int)len; ++} ++ ++/* =========================================================================== ++ * Initialize the "longest match" routines for a new zlib stream ++ */ ++local void lm_init (s) ++ deflate_state *s; ++{ ++ s->window_size = (ulg)2L*s->w_size; ++ ++ CLEAR_HASH(s); ++ ++ /* Set the default configuration parameters: ++ */ ++ s->max_lazy_match = configuration_table[s->level].max_lazy; ++ s->good_match = configuration_table[s->level].good_length; ++ s->nice_match = configuration_table[s->level].nice_length; ++ s->max_chain_length = configuration_table[s->level].max_chain; ++ ++ s->strstart = 0; ++ s->block_start = 0L; ++ s->lookahead = 0; ++ s->match_length = s->prev_length = MIN_MATCH-1; ++ s->match_available = 0; ++ s->ins_h = 0; ++#ifdef ASMV ++ match_init(); /* initialize the asm code */ ++#endif ++} ++ ++/* =========================================================================== ++ * Set match_start to the longest match starting at the given string and ++ * return its length. Matches shorter or equal to prev_length are discarded, ++ * in which case the result is equal to prev_length and match_start is ++ * garbage. ++ * IN assertions: cur_match is the head of the hash chain for the current ++ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 ++ * OUT assertion: the match length is not greater than s->lookahead. ++ */ ++#ifndef ASMV ++/* For 80x86 and 680x0, an optimized version will be provided in match.asm or ++ * match.S. The code will be functionally equivalent. ++ */ ++#ifndef FASTEST ++local uInt longest_match(s, cur_match) ++ deflate_state *s; ++ IPos cur_match; /* current match */ ++{ ++ unsigned chain_length = s->max_chain_length;/* max hash chain length */ ++ register Bytef *scan = s->window + s->strstart; /* current string */ ++ register Bytef *match; /* matched string */ ++ register int len; /* length of current match */ ++ int best_len = s->prev_length; /* best match length so far */ ++ int nice_match = s->nice_match; /* stop if match long enough */ ++ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? ++ s->strstart - (IPos)MAX_DIST(s) : NIL; ++ /* Stop when cur_match becomes <= limit. To simplify the code, ++ * we prevent matches with the string of window index 0. ++ */ ++ Posf *prev = s->prev; ++ uInt wmask = s->w_mask; ++ ++#ifdef UNALIGNED_OK ++ /* Compare two bytes at a time. Note: this is not always beneficial. ++ * Try with and without -DUNALIGNED_OK to check. ++ */ ++ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; ++ register ush scan_start = *(ushf*)scan; ++ register ush scan_end = *(ushf*)(scan+best_len-1); ++#else ++ register Bytef *strend = s->window + s->strstart + MAX_MATCH; ++ register Byte scan_end1 = scan[best_len-1]; ++ register Byte scan_end = scan[best_len]; ++#endif ++ ++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. ++ * It is easy to get rid of this optimization if necessary. ++ */ ++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); ++ ++ /* Do not waste too much time if we already have a good match: */ ++ if (s->prev_length >= s->good_match) { ++ chain_length >>= 2; ++ } ++ /* Do not look for matches beyond the end of the input. This is necessary ++ * to make deflate deterministic. ++ */ ++ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; ++ ++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); ++ ++ do { ++ Assert(cur_match < s->strstart, "no future"); ++ match = s->window + cur_match; ++ ++ /* Skip to next match if the match length cannot increase ++ * or if the match length is less than 2: ++ */ ++#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) ++ /* This code assumes sizeof(unsigned short) == 2. Do not use ++ * UNALIGNED_OK if your compiler uses a different size. ++ */ ++ if (*(ushf*)(match+best_len-1) != scan_end || ++ *(ushf*)match != scan_start) continue; ++ ++ /* It is not necessary to compare scan[2] and match[2] since they are ++ * always equal when the other bytes match, given that the hash keys ++ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at ++ * strstart+3, +5, ... up to strstart+257. We check for insufficient ++ * lookahead only every 4th comparison; the 128th check will be made ++ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is ++ * necessary to put more guard bytes at the end of the window, or ++ * to check more often for insufficient lookahead. ++ */ ++ Assert(scan[2] == match[2], "scan[2]?"); ++ scan++, match++; ++ do { ++ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && ++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) && ++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) && ++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) && ++ scan < strend); ++ /* The funny "do {}" generates better code on most compilers */ ++ ++ /* Here, scan <= window+strstart+257 */ ++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); ++ if (*scan == *match) scan++; ++ ++ len = (MAX_MATCH - 1) - (int)(strend-scan); ++ scan = strend - (MAX_MATCH-1); ++ ++#else /* UNALIGNED_OK */ ++ ++ if (match[best_len] != scan_end || ++ match[best_len-1] != scan_end1 || ++ *match != *scan || ++ *++match != scan[1]) continue; ++ ++ /* The check at best_len-1 can be removed because it will be made ++ * again later. (This heuristic is not always a win.) ++ * It is not necessary to compare scan[2] and match[2] since they ++ * are always equal when the other bytes match, given that ++ * the hash keys are equal and that HASH_BITS >= 8. ++ */ ++ scan += 2, match++; ++ Assert(*scan == *match, "match[2]?"); ++ ++ /* We check for insufficient lookahead only every 8th comparison; ++ * the 256th check will be made at strstart+258. ++ */ ++ do { ++ } while (*++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ scan < strend); ++ ++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); ++ ++ len = MAX_MATCH - (int)(strend - scan); ++ scan = strend - MAX_MATCH; ++ ++#endif /* UNALIGNED_OK */ ++ ++ if (len > best_len) { ++ s->match_start = cur_match; ++ best_len = len; ++ if (len >= nice_match) break; ++#ifdef UNALIGNED_OK ++ scan_end = *(ushf*)(scan+best_len-1); ++#else ++ scan_end1 = scan[best_len-1]; ++ scan_end = scan[best_len]; ++#endif ++ } ++ } while ((cur_match = prev[cur_match & wmask]) > limit ++ && --chain_length != 0); ++ ++ if ((uInt)best_len <= s->lookahead) return (uInt)best_len; ++ return s->lookahead; ++} ++ ++#else /* FASTEST */ ++/* --------------------------------------------------------------------------- ++ * Optimized version for level == 1 only ++ */ ++local uInt longest_match(s, cur_match) ++ deflate_state *s; ++ IPos cur_match; /* current match */ ++{ ++ register Bytef *scan = s->window + s->strstart; /* current string */ ++ register Bytef *match; /* matched string */ ++ register int len; /* length of current match */ ++ register Bytef *strend = s->window + s->strstart + MAX_MATCH; ++ ++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. ++ * It is easy to get rid of this optimization if necessary. ++ */ ++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); ++ ++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); ++ ++ Assert(cur_match < s->strstart, "no future"); ++ ++ match = s->window + cur_match; ++ ++ /* Return failure if the match length is less than 2: ++ */ ++ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; ++ ++ /* The check at best_len-1 can be removed because it will be made ++ * again later. (This heuristic is not always a win.) ++ * It is not necessary to compare scan[2] and match[2] since they ++ * are always equal when the other bytes match, given that ++ * the hash keys are equal and that HASH_BITS >= 8. ++ */ ++ scan += 2, match += 2; ++ Assert(*scan == *match, "match[2]?"); ++ ++ /* We check for insufficient lookahead only every 8th comparison; ++ * the 256th check will be made at strstart+258. ++ */ ++ do { ++ } while (*++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ scan < strend); ++ ++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); ++ ++ len = MAX_MATCH - (int)(strend - scan); ++ ++ if (len < MIN_MATCH) return MIN_MATCH - 1; ++ ++ s->match_start = cur_match; ++ return len <= s->lookahead ? len : s->lookahead; ++} ++#endif /* FASTEST */ ++#endif /* ASMV */ ++ ++#ifdef DEBUG ++/* =========================================================================== ++ * Check that the match at match_start is indeed a match. ++ */ ++local void check_match(s, start, match, length) ++ deflate_state *s; ++ IPos start, match; ++ int length; ++{ ++ /* check that the match is indeed a match */ ++ if (zmemcmp(s->window + match, ++ s->window + start, length) != EQUAL) { ++ fprintf(stderr, " start %u, match %u, length %d\n", ++ start, match, length); ++ do { ++ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); ++ } while (--length != 0); ++ z_error("invalid match"); ++ } ++ if (z_verbose > 1) { ++ fprintf(stderr,"\\[%d,%d]", start-match, length); ++ do { putc(s->window[start++], stderr); } while (--length != 0); ++ } ++} ++#else ++# define check_match(s, start, match, length) ++#endif ++ ++/* =========================================================================== ++ * Fill the window when the lookahead becomes insufficient. ++ * Updates strstart and lookahead. ++ * ++ * IN assertion: lookahead < MIN_LOOKAHEAD ++ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD ++ * At least one byte has been read, or avail_in == 0; reads are ++ * performed for at least two bytes (required for the zip translate_eol ++ * option -- not supported here). ++ */ ++local void fill_window(s) ++ deflate_state *s; ++{ ++ register unsigned n, m; ++ register Posf *p; ++ unsigned more; /* Amount of free space at the end of the window. */ ++ uInt wsize = s->w_size; ++ ++ do { ++ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); ++ ++ /* Deal with !@#$% 64K limit: */ ++ if (more == 0 && s->strstart == 0 && s->lookahead == 0) { ++ more = wsize; ++ ++ } else if (more == (unsigned)(-1)) { ++ /* Very unlikely, but possible on 16 bit machine if strstart == 0 ++ * and lookahead == 1 (input done one byte at time) ++ */ ++ more--; ++ ++ /* If the window is almost full and there is insufficient lookahead, ++ * move the upper half to the lower one to make room in the upper half. ++ */ ++ } else if (s->strstart >= wsize+MAX_DIST(s)) { ++ ++ zmemcpy(s->window, s->window+wsize, (unsigned)wsize); ++ s->match_start -= wsize; ++ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ ++ s->block_start -= (long) wsize; ++ ++ /* Slide the hash table (could be avoided with 32 bit values ++ at the expense of memory usage). We slide even when level == 0 ++ to keep the hash table consistent if we switch back to level > 0 ++ later. (Using level 0 permanently is not an optimal usage of ++ zlib, so we don't care about this pathological case.) ++ */ ++ n = s->hash_size; ++ p = &s->head[n]; ++ do { ++ m = *--p; ++ *p = (Pos)(m >= wsize ? m-wsize : NIL); ++ } while (--n); ++ ++ n = wsize; ++#ifndef FASTEST ++ p = &s->prev[n]; ++ do { ++ m = *--p; ++ *p = (Pos)(m >= wsize ? m-wsize : NIL); ++ /* If n is not on any hash chain, prev[n] is garbage but ++ * its value will never be used. ++ */ ++ } while (--n); ++#endif ++ more += wsize; ++ } ++ if (s->strm->avail_in == 0) return; ++ ++ /* If there was no sliding: ++ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && ++ * more == window_size - lookahead - strstart ++ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) ++ * => more >= window_size - 2*WSIZE + 2 ++ * In the BIG_MEM or MMAP case (not yet supported), ++ * window_size == input_size + MIN_LOOKAHEAD && ++ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. ++ * Otherwise, window_size == 2*WSIZE so more >= 2. ++ * If there was sliding, more >= WSIZE. So in all cases, more >= 2. ++ */ ++ Assert(more >= 2, "more < 2"); ++ ++ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); ++ s->lookahead += n; ++ ++ /* Initialize the hash value now that we have some input: */ ++ if (s->lookahead >= MIN_MATCH) { ++ s->ins_h = s->window[s->strstart]; ++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); ++#if MIN_MATCH != 3 ++ Call UPDATE_HASH() MIN_MATCH-3 more times ++#endif ++ } ++ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, ++ * but this is not important since only literal bytes will be emitted. ++ */ ++ ++ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); ++} ++ ++/* =========================================================================== ++ * Flush the current block, with given end-of-file flag. ++ * IN assertion: strstart is set to the end of the current match. ++ */ ++#define FLUSH_BLOCK_ONLY(s, eof) { \ ++ _tr_flush_block(s, (s->block_start >= 0L ? \ ++ (charf *)&s->window[(unsigned)s->block_start] : \ ++ (charf *)Z_NULL), \ ++ (ulg)((long)s->strstart - s->block_start), \ ++ (eof)); \ ++ s->block_start = s->strstart; \ ++ flush_pending(s->strm); \ ++ Tracev((stderr,"[FLUSH]")); \ ++} ++ ++/* Same but force premature exit if necessary. */ ++#define FLUSH_BLOCK(s, eof) { \ ++ FLUSH_BLOCK_ONLY(s, eof); \ ++ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ ++} ++ ++/* =========================================================================== ++ * Copy without compression as much as possible from the input stream, return ++ * the current block state. ++ * This function does not insert new strings in the dictionary since ++ * uncompressible data is probably not useful. This function is used ++ * only for the level=0 compression option. ++ * NOTE: this function should be optimized to avoid extra copying from ++ * window to pending_buf. ++ */ ++local block_state deflate_stored(s, flush) ++ deflate_state *s; ++ int flush; ++{ ++ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited ++ * to pending_buf_size, and each stored block has a 5 byte header: ++ */ ++ ulg max_block_size = 0xffff; ++ ulg max_start; ++ ++ if (max_block_size > s->pending_buf_size - 5) { ++ max_block_size = s->pending_buf_size - 5; ++ } ++ ++ /* Copy as much as possible from input to output: */ ++ for (;;) { ++ /* Fill the window as much as possible: */ ++ if (s->lookahead <= 1) { ++ ++ Assert(s->strstart < s->w_size+MAX_DIST(s) || ++ s->block_start >= (long)s->w_size, "slide too late"); ++ ++ fill_window(s); ++ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; ++ ++ if (s->lookahead == 0) break; /* flush the current block */ ++ } ++ Assert(s->block_start >= 0L, "block gone"); ++ ++ s->strstart += s->lookahead; ++ s->lookahead = 0; ++ ++ /* Emit a stored block if pending_buf will be full: */ ++ max_start = s->block_start + max_block_size; ++ if (s->strstart == 0 || (ulg)s->strstart >= max_start) { ++ /* strstart == 0 is possible when wraparound on 16-bit machine */ ++ s->lookahead = (uInt)(s->strstart - max_start); ++ s->strstart = (uInt)max_start; ++ FLUSH_BLOCK(s, 0); ++ } ++ /* Flush if we may have to slide, otherwise block_start may become ++ * negative and the data will be gone: ++ */ ++ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { ++ FLUSH_BLOCK(s, 0); ++ } ++ } ++ FLUSH_BLOCK(s, flush == Z_FINISH); ++ return flush == Z_FINISH ? finish_done : block_done; ++} ++ ++/* =========================================================================== ++ * Compress as much as possible from the input stream, return the current ++ * block state. ++ * This function does not perform lazy evaluation of matches and inserts ++ * new strings in the dictionary only for unmatched strings or for short ++ * matches. It is used only for the fast compression options. ++ */ ++local block_state deflate_fast(s, flush) ++ deflate_state *s; ++ int flush; ++{ ++ IPos hash_head = NIL; /* head of the hash chain */ ++ int bflush; /* set if current block must be flushed */ ++ ++ for (;;) { ++ /* Make sure that we always have enough lookahead, except ++ * at the end of the input file. We need MAX_MATCH bytes ++ * for the next match, plus MIN_MATCH bytes to insert the ++ * string following the next match. ++ */ ++ if (s->lookahead < MIN_LOOKAHEAD) { ++ fill_window(s); ++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { ++ return need_more; ++ } ++ if (s->lookahead == 0) break; /* flush the current block */ ++ } ++ ++ /* Insert the string window[strstart .. strstart+2] in the ++ * dictionary, and set hash_head to the head of the hash chain: ++ */ ++ if (s->lookahead >= MIN_MATCH) { ++ INSERT_STRING(s, s->strstart, hash_head); ++ } ++ ++ /* Find the longest match, discarding those <= prev_length. ++ * At this point we have always match_length < MIN_MATCH ++ */ ++ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { ++ /* To simplify the code, we prevent matches with the string ++ * of window index 0 (in particular we have to avoid a match ++ * of the string with itself at the start of the input file). ++ */ ++ if (s->strategy != Z_HUFFMAN_ONLY) { ++ s->match_length = longest_match (s, hash_head); ++ } ++ /* longest_match() sets match_start */ ++ } ++ if (s->match_length >= MIN_MATCH) { ++ check_match(s, s->strstart, s->match_start, s->match_length); ++ ++ _tr_tally_dist(s, s->strstart - s->match_start, ++ s->match_length - MIN_MATCH, bflush); ++ ++ s->lookahead -= s->match_length; ++ ++ /* Insert new strings in the hash table only if the match length ++ * is not too large. This saves time but degrades compression. ++ */ ++#ifndef FASTEST ++ if (s->match_length <= s->max_insert_length && ++ s->lookahead >= MIN_MATCH) { ++ s->match_length--; /* string at strstart already in hash table */ ++ do { ++ s->strstart++; ++ INSERT_STRING(s, s->strstart, hash_head); ++ /* strstart never exceeds WSIZE-MAX_MATCH, so there are ++ * always MIN_MATCH bytes ahead. ++ */ ++ } while (--s->match_length != 0); ++ s->strstart++; ++ } else ++#endif ++ { ++ s->strstart += s->match_length; ++ s->match_length = 0; ++ s->ins_h = s->window[s->strstart]; ++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); ++#if MIN_MATCH != 3 ++ Call UPDATE_HASH() MIN_MATCH-3 more times ++#endif ++ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not ++ * matter since it will be recomputed at next deflate call. ++ */ ++ } ++ } else { ++ /* No match, output a literal byte */ ++ Tracevv((stderr,"%c", s->window[s->strstart])); ++ _tr_tally_lit (s, s->window[s->strstart], bflush); ++ s->lookahead--; ++ s->strstart++; ++ } ++ if (bflush) FLUSH_BLOCK(s, 0); ++ } ++ FLUSH_BLOCK(s, flush == Z_FINISH); ++ return flush == Z_FINISH ? finish_done : block_done; ++} ++ ++/* =========================================================================== ++ * Same as above, but achieves better compression. We use a lazy ++ * evaluation for matches: a match is finally adopted only if there is ++ * no better match at the next window position. ++ */ ++local block_state deflate_slow(s, flush) ++ deflate_state *s; ++ int flush; ++{ ++ IPos hash_head = NIL; /* head of hash chain */ ++ int bflush; /* set if current block must be flushed */ ++ ++ /* Process the input block. */ ++ for (;;) { ++ /* Make sure that we always have enough lookahead, except ++ * at the end of the input file. We need MAX_MATCH bytes ++ * for the next match, plus MIN_MATCH bytes to insert the ++ * string following the next match. ++ */ ++ if (s->lookahead < MIN_LOOKAHEAD) { ++ fill_window(s); ++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { ++ return need_more; ++ } ++ if (s->lookahead == 0) break; /* flush the current block */ ++ } ++ ++ /* Insert the string window[strstart .. strstart+2] in the ++ * dictionary, and set hash_head to the head of the hash chain: ++ */ ++ if (s->lookahead >= MIN_MATCH) { ++ INSERT_STRING(s, s->strstart, hash_head); ++ } ++ ++ /* Find the longest match, discarding those <= prev_length. ++ */ ++ s->prev_length = s->match_length, s->prev_match = s->match_start; ++ s->match_length = MIN_MATCH-1; ++ ++ if (hash_head != NIL && s->prev_length < s->max_lazy_match && ++ s->strstart - hash_head <= MAX_DIST(s)) { ++ /* To simplify the code, we prevent matches with the string ++ * of window index 0 (in particular we have to avoid a match ++ * of the string with itself at the start of the input file). ++ */ ++ if (s->strategy != Z_HUFFMAN_ONLY) { ++ s->match_length = longest_match (s, hash_head); ++ } ++ /* longest_match() sets match_start */ ++ ++ if (s->match_length <= 5 && (s->strategy == Z_FILTERED || ++ (s->match_length == MIN_MATCH && ++ s->strstart - s->match_start > TOO_FAR))) { ++ ++ /* If prev_match is also MIN_MATCH, match_start is garbage ++ * but we will ignore the current match anyway. ++ */ ++ s->match_length = MIN_MATCH-1; ++ } ++ } ++ /* If there was a match at the previous step and the current ++ * match is not better, output the previous match: ++ */ ++ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { ++ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; ++ /* Do not insert strings in hash table beyond this. */ ++ ++ check_match(s, s->strstart-1, s->prev_match, s->prev_length); ++ ++ _tr_tally_dist(s, s->strstart -1 - s->prev_match, ++ s->prev_length - MIN_MATCH, bflush); ++ ++ /* Insert in hash table all strings up to the end of the match. ++ * strstart-1 and strstart are already inserted. If there is not ++ * enough lookahead, the last two strings are not inserted in ++ * the hash table. ++ */ ++ s->lookahead -= s->prev_length-1; ++ s->prev_length -= 2; ++ do { ++ if (++s->strstart <= max_insert) { ++ INSERT_STRING(s, s->strstart, hash_head); ++ } ++ } while (--s->prev_length != 0); ++ s->match_available = 0; ++ s->match_length = MIN_MATCH-1; ++ s->strstart++; ++ ++ if (bflush) FLUSH_BLOCK(s, 0); ++ ++ } else if (s->match_available) { ++ /* If there was no match at the previous position, output a ++ * single literal. If there was a match but the current match ++ * is longer, truncate the previous match to a single literal. ++ */ ++ Tracevv((stderr,"%c", s->window[s->strstart-1])); ++ _tr_tally_lit(s, s->window[s->strstart-1], bflush); ++ if (bflush) { ++ FLUSH_BLOCK_ONLY(s, 0); ++ } ++ s->strstart++; ++ s->lookahead--; ++ if (s->strm->avail_out == 0) return need_more; ++ } else { ++ /* There is no previous match to compare with, wait for ++ * the next step to decide. ++ */ ++ s->match_available = 1; ++ s->strstart++; ++ s->lookahead--; ++ } ++ } ++ Assert (flush != Z_NO_FLUSH, "no flush?"); ++ if (s->match_available) { ++ Tracevv((stderr,"%c", s->window[s->strstart-1])); ++ _tr_tally_lit(s, s->window[s->strstart-1], bflush); ++ s->match_available = 0; ++ } ++ FLUSH_BLOCK(s, flush == Z_FINISH); ++ return flush == Z_FINISH ? finish_done : block_done; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/deflate.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,318 @@ ++/* deflate.h -- internal compression state ++ * Copyright (C) 1995-2002 Jean-loup Gailly ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/* @(#) $Id: deflate.h,v 1.4 2002/04/24 07:55:32 mcr Exp $ */ ++ ++#ifndef _DEFLATE_H ++#define _DEFLATE_H ++ ++#include "zlib/zutil.h" ++ ++/* =========================================================================== ++ * Internal compression state. ++ */ ++ ++#define LENGTH_CODES 29 ++/* number of length codes, not counting the special END_BLOCK code */ ++ ++#define LITERALS 256 ++/* number of literal bytes 0..255 */ ++ ++#define L_CODES (LITERALS+1+LENGTH_CODES) ++/* number of Literal or Length codes, including the END_BLOCK code */ ++ ++#define D_CODES 30 ++/* number of distance codes */ ++ ++#define BL_CODES 19 ++/* number of codes used to transfer the bit lengths */ ++ ++#define HEAP_SIZE (2*L_CODES+1) ++/* maximum heap size */ ++ ++#define MAX_BITS 15 ++/* All codes must not exceed MAX_BITS bits */ ++ ++#define INIT_STATE 42 ++#define BUSY_STATE 113 ++#define FINISH_STATE 666 ++/* Stream status */ ++ ++ ++/* Data structure describing a single value and its code string. */ ++typedef struct ct_data_s { ++ union { ++ ush freq; /* frequency count */ ++ ush code; /* bit string */ ++ } fc; ++ union { ++ ush dad; /* father node in Huffman tree */ ++ ush len; /* length of bit string */ ++ } dl; ++} FAR ct_data; ++ ++#define Freq fc.freq ++#define Code fc.code ++#define Dad dl.dad ++#define Len dl.len ++ ++typedef struct static_tree_desc_s static_tree_desc; ++ ++typedef struct tree_desc_s { ++ ct_data *dyn_tree; /* the dynamic tree */ ++ int max_code; /* largest code with non zero frequency */ ++ static_tree_desc *stat_desc; /* the corresponding static tree */ ++} FAR tree_desc; ++ ++typedef ush Pos; ++typedef Pos FAR Posf; ++typedef unsigned IPos; ++ ++/* A Pos is an index in the character window. We use short instead of int to ++ * save space in the various tables. IPos is used only for parameter passing. ++ */ ++ ++typedef struct internal_state { ++ z_streamp strm; /* pointer back to this zlib stream */ ++ int status; /* as the name implies */ ++ Bytef *pending_buf; /* output still pending */ ++ ulg pending_buf_size; /* size of pending_buf */ ++ Bytef *pending_out; /* next pending byte to output to the stream */ ++ int pending; /* nb of bytes in the pending buffer */ ++ int noheader; /* suppress zlib header and adler32 */ ++ Byte data_type; /* UNKNOWN, BINARY or ASCII */ ++ Byte method; /* STORED (for zip only) or DEFLATED */ ++ int last_flush; /* value of flush param for previous deflate call */ ++ ++ /* used by deflate.c: */ ++ ++ uInt w_size; /* LZ77 window size (32K by default) */ ++ uInt w_bits; /* log2(w_size) (8..16) */ ++ uInt w_mask; /* w_size - 1 */ ++ ++ Bytef *window; ++ /* Sliding window. Input bytes are read into the second half of the window, ++ * and move to the first half later to keep a dictionary of at least wSize ++ * bytes. With this organization, matches are limited to a distance of ++ * wSize-MAX_MATCH bytes, but this ensures that IO is always ++ * performed with a length multiple of the block size. Also, it limits ++ * the window size to 64K, which is quite useful on MSDOS. ++ * To do: use the user input buffer as sliding window. ++ */ ++ ++ ulg window_size; ++ /* Actual size of window: 2*wSize, except when the user input buffer ++ * is directly used as sliding window. ++ */ ++ ++ Posf *prev; ++ /* Link to older string with same hash index. To limit the size of this ++ * array to 64K, this link is maintained only for the last 32K strings. ++ * An index in this array is thus a window index modulo 32K. ++ */ ++ ++ Posf *head; /* Heads of the hash chains or NIL. */ ++ ++ uInt ins_h; /* hash index of string to be inserted */ ++ uInt hash_size; /* number of elements in hash table */ ++ uInt hash_bits; /* log2(hash_size) */ ++ uInt hash_mask; /* hash_size-1 */ ++ ++ uInt hash_shift; ++ /* Number of bits by which ins_h must be shifted at each input ++ * step. It must be such that after MIN_MATCH steps, the oldest ++ * byte no longer takes part in the hash key, that is: ++ * hash_shift * MIN_MATCH >= hash_bits ++ */ ++ ++ long block_start; ++ /* Window position at the beginning of the current output block. Gets ++ * negative when the window is moved backwards. ++ */ ++ ++ uInt match_length; /* length of best match */ ++ IPos prev_match; /* previous match */ ++ int match_available; /* set if previous match exists */ ++ uInt strstart; /* start of string to insert */ ++ uInt match_start; /* start of matching string */ ++ uInt lookahead; /* number of valid bytes ahead in window */ ++ ++ uInt prev_length; ++ /* Length of the best match at previous step. Matches not greater than this ++ * are discarded. This is used in the lazy match evaluation. ++ */ ++ ++ uInt max_chain_length; ++ /* To speed up deflation, hash chains are never searched beyond this ++ * length. A higher limit improves compression ratio but degrades the ++ * speed. ++ */ ++ ++ uInt max_lazy_match; ++ /* Attempt to find a better match only when the current match is strictly ++ * smaller than this value. This mechanism is used only for compression ++ * levels >= 4. ++ */ ++# define max_insert_length max_lazy_match ++ /* Insert new strings in the hash table only if the match length is not ++ * greater than this length. This saves time but degrades compression. ++ * max_insert_length is used only for compression levels <= 3. ++ */ ++ ++ int level; /* compression level (1..9) */ ++ int strategy; /* favor or force Huffman coding*/ ++ ++ uInt good_match; ++ /* Use a faster search when the previous match is longer than this */ ++ ++ int nice_match; /* Stop searching when current match exceeds this */ ++ ++ /* used by trees.c: */ ++ /* Didn't use ct_data typedef below to supress compiler warning */ ++ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ ++ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ ++ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ ++ ++ struct tree_desc_s l_desc; /* desc. for literal tree */ ++ struct tree_desc_s d_desc; /* desc. for distance tree */ ++ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ++ ++ ush bl_count[MAX_BITS+1]; ++ /* number of codes at each bit length for an optimal tree */ ++ ++ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ ++ int heap_len; /* number of elements in the heap */ ++ int heap_max; /* element of largest frequency */ ++ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. ++ * The same heap array is used to build all trees. ++ */ ++ ++ uch depth[2*L_CODES+1]; ++ /* Depth of each subtree used as tie breaker for trees of equal frequency ++ */ ++ ++ uchf *l_buf; /* buffer for literals or lengths */ ++ ++ uInt lit_bufsize; ++ /* Size of match buffer for literals/lengths. There are 4 reasons for ++ * limiting lit_bufsize to 64K: ++ * - frequencies can be kept in 16 bit counters ++ * - if compression is not successful for the first block, all input ++ * data is still in the window so we can still emit a stored block even ++ * when input comes from standard input. (This can also be done for ++ * all blocks if lit_bufsize is not greater than 32K.) ++ * - if compression is not successful for a file smaller than 64K, we can ++ * even emit a stored file instead of a stored block (saving 5 bytes). ++ * This is applicable only for zip (not gzip or zlib). ++ * - creating new Huffman trees less frequently may not provide fast ++ * adaptation to changes in the input data statistics. (Take for ++ * example a binary file with poorly compressible code followed by ++ * a highly compressible string table.) Smaller buffer sizes give ++ * fast adaptation but have of course the overhead of transmitting ++ * trees more frequently. ++ * - I can't count above 4 ++ */ ++ ++ uInt last_lit; /* running index in l_buf */ ++ ++ ushf *d_buf; ++ /* Buffer for distances. To simplify the code, d_buf and l_buf have ++ * the same number of elements. To use different lengths, an extra flag ++ * array would be necessary. ++ */ ++ ++ ulg opt_len; /* bit length of current block with optimal trees */ ++ ulg static_len; /* bit length of current block with static trees */ ++ uInt matches; /* number of string matches in current block */ ++ int last_eob_len; /* bit length of EOB code for last block */ ++ ++#ifdef DEBUG ++ ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ++ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ ++#endif ++ ++ ush bi_buf; ++ /* Output buffer. bits are inserted starting at the bottom (least ++ * significant bits). ++ */ ++ int bi_valid; ++ /* Number of valid bits in bi_buf. All bits above the last valid bit ++ * are always zero. ++ */ ++ ++} FAR deflate_state; ++ ++/* Output a byte on the stream. ++ * IN assertion: there is enough room in pending_buf. ++ */ ++#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} ++ ++ ++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) ++/* Minimum amount of lookahead, except at the end of the input file. ++ * See deflate.c for comments about the MIN_MATCH+1. ++ */ ++ ++#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) ++/* In order to simplify the code, particularly on 16 bit machines, match ++ * distances are limited to MAX_DIST instead of WSIZE. ++ */ ++ ++ /* in trees.c */ ++void _tr_init OF((deflate_state *s)); ++int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); ++void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, ++ int eof)); ++void _tr_align OF((deflate_state *s)); ++void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, ++ int eof)); ++ ++#define d_code(dist) \ ++ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) ++/* Mapping from a distance to a distance code. dist is the distance - 1 and ++ * must not have side effects. _dist_code[256] and _dist_code[257] are never ++ * used. ++ */ ++ ++#ifndef DEBUG ++/* Inline versions of _tr_tally for speed: */ ++ ++#if defined(GEN_TREES_H) || !defined(STDC) ++ extern uch _length_code[]; ++ extern uch _dist_code[]; ++#else ++ extern const uch _length_code[]; ++ extern const uch _dist_code[]; ++#endif ++ ++# define _tr_tally_lit(s, c, flush) \ ++ { uch cc = (c); \ ++ s->d_buf[s->last_lit] = 0; \ ++ s->l_buf[s->last_lit++] = cc; \ ++ s->dyn_ltree[cc].Freq++; \ ++ flush = (s->last_lit == s->lit_bufsize-1); \ ++ } ++# define _tr_tally_dist(s, distance, length, flush) \ ++ { uch len = (length); \ ++ ush dist = (distance); \ ++ s->d_buf[s->last_lit] = dist; \ ++ s->l_buf[s->last_lit++] = len; \ ++ dist--; \ ++ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ ++ s->dyn_dtree[d_code(dist)].Freq++; \ ++ flush = (s->last_lit == s->lit_bufsize-1); \ ++ } ++#else ++# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) ++# define _tr_tally_dist(s, distance, length, flush) \ ++ flush = _tr_tally(s, distance, length) ++#endif ++ ++#endif /* _DEFLATE_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infblock.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,403 @@ ++/* infblock.c -- interpret and process block types to last block ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "infblock.h" ++#include "inftrees.h" ++#include "infcodes.h" ++#include "infutil.h" ++ ++struct inflate_codes_state {int dummy;}; /* for buggy compilers */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++/* Table for deflate from PKZIP's appnote.txt. */ ++local const uInt border[] = { /* Order of the bit length code lengths */ ++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; ++ ++/* ++ Notes beyond the 1.93a appnote.txt: ++ ++ 1. Distance pointers never point before the beginning of the output ++ stream. ++ 2. Distance pointers can point back across blocks, up to 32k away. ++ 3. There is an implied maximum of 7 bits for the bit length table and ++ 15 bits for the actual data. ++ 4. If only one code exists, then it is encoded using one bit. (Zero ++ would be more efficient, but perhaps a little confusing.) If two ++ codes exist, they are coded using one bit each (0 and 1). ++ 5. There is no way of sending zero distance codes--a dummy must be ++ sent if there are none. (History: a pre 2.0 version of PKZIP would ++ store blocks with no distance codes, but this was discovered to be ++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow ++ zero distance codes, which is sent as one code of zero bits in ++ length. ++ 6. There are up to 286 literal/length codes. Code 256 represents the ++ end-of-block. Note however that the static length tree defines ++ 288 codes just to fill out the Huffman codes. Codes 286 and 287 ++ cannot be used though, since there is no length base or extra bits ++ defined for them. Similarily, there are up to 30 distance codes. ++ However, static trees define 32 codes (all 5 bits) to fill out the ++ Huffman codes, but the last two had better not show up in the data. ++ 7. Unzip can check dynamic Huffman blocks for complete code sets. ++ The exception is that a single code would not be complete (see #4). ++ 8. The five bits following the block type is really the number of ++ literal codes sent minus 257. ++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits ++ (1+6+6). Therefore, to output three times the length, you output ++ three codes (1+1+1), whereas to output four times the same length, ++ you only need two codes (1+3). Hmm. ++ 10. In the tree reconstruction algorithm, Code = Code + Increment ++ only if BitLength(i) is not zero. (Pretty obvious.) ++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) ++ 12. Note: length code 284 can represent 227-258, but length code 285 ++ really is 258. The last length deserves its own, short code ++ since it gets used a lot in very redundant files. The length ++ 258 is special since 258 - 3 (the min match length) is 255. ++ 13. The literal/length and distance code bit lengths are read as a ++ single stream of lengths. It is possible (and advantageous) for ++ a repeat code (16, 17, or 18) to go across the boundary between ++ the two sets of lengths. ++ */ ++ ++ ++void inflate_blocks_reset(s, z, c) ++inflate_blocks_statef *s; ++z_streamp z; ++uLongf *c; ++{ ++ if (c != Z_NULL) ++ *c = s->check; ++ if (s->mode == BTREE || s->mode == DTREE) ++ ZFREE(z, s->sub.trees.blens); ++ if (s->mode == CODES) ++ inflate_codes_free(s->sub.decode.codes, z); ++ s->mode = TYPE; ++ s->bitk = 0; ++ s->bitb = 0; ++ s->read = s->write = s->window; ++ if (s->checkfn != Z_NULL) ++ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0); ++ Tracev((stderr, "inflate: blocks reset\n")); ++} ++ ++ ++inflate_blocks_statef *inflate_blocks_new(z, c, w) ++z_streamp z; ++check_func c; ++uInt w; ++{ ++ inflate_blocks_statef *s; ++ ++ if ((s = (inflate_blocks_statef *)ZALLOC ++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) ++ return s; ++ if ((s->hufts = ++ (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) ++ { ++ ZFREE(z, s); ++ return Z_NULL; ++ } ++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) ++ { ++ ZFREE(z, s->hufts); ++ ZFREE(z, s); ++ return Z_NULL; ++ } ++ s->end = s->window + w; ++ s->checkfn = c; ++ s->mode = TYPE; ++ Tracev((stderr, "inflate: blocks allocated\n")); ++ inflate_blocks_reset(s, z, Z_NULL); ++ return s; ++} ++ ++ ++int inflate_blocks(s, z, r) ++inflate_blocks_statef *s; ++z_streamp z; ++int r; ++{ ++ uInt t; /* temporary storage */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ ++ /* copy input/output information to locals (UPDATE macro restores) */ ++ LOAD ++ ++ /* process input based on current state */ ++ while (1) switch (s->mode) ++ { ++ case TYPE: ++ NEEDBITS(3) ++ t = (uInt)b & 7; ++ s->last = t & 1; ++ switch (t >> 1) ++ { ++ case 0: /* stored */ ++ Tracev((stderr, "inflate: stored block%s\n", ++ s->last ? " (last)" : "")); ++ DUMPBITS(3) ++ t = k & 7; /* go to byte boundary */ ++ DUMPBITS(t) ++ s->mode = LENS; /* get length of stored block */ ++ break; ++ case 1: /* fixed */ ++ Tracev((stderr, "inflate: fixed codes block%s\n", ++ s->last ? " (last)" : "")); ++ { ++ uInt bl, bd; ++ inflate_huft *tl, *td; ++ ++ inflate_trees_fixed(&bl, &bd, &tl, &td, z); ++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); ++ if (s->sub.decode.codes == Z_NULL) ++ { ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ } ++ DUMPBITS(3) ++ s->mode = CODES; ++ break; ++ case 2: /* dynamic */ ++ Tracev((stderr, "inflate: dynamic codes block%s\n", ++ s->last ? " (last)" : "")); ++ DUMPBITS(3) ++ s->mode = TABLE; ++ break; ++ case 3: /* illegal */ ++ DUMPBITS(3) ++ s->mode = BAD; ++ z->msg = (char*)"invalid block type"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ break; ++ case LENS: ++ NEEDBITS(32) ++ if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) ++ { ++ s->mode = BAD; ++ z->msg = (char*)"invalid stored block lengths"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ s->sub.left = (uInt)b & 0xffff; ++ b = k = 0; /* dump bits */ ++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); ++ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); ++ break; ++ case STORED: ++ if (n == 0) ++ LEAVE ++ NEEDOUT ++ t = s->sub.left; ++ if (t > n) t = n; ++ if (t > m) t = m; ++ zmemcpy(q, p, t); ++ p += t; n -= t; ++ q += t; m -= t; ++ if ((s->sub.left -= t) != 0) ++ break; ++ Tracev((stderr, "inflate: stored end, %lu total out\n", ++ z->total_out + (q >= s->read ? q - s->read : ++ (s->end - s->read) + (q - s->window)))); ++ s->mode = s->last ? DRY : TYPE; ++ break; ++ case TABLE: ++ NEEDBITS(14) ++ s->sub.trees.table = t = (uInt)b & 0x3fff; ++#ifndef PKZIP_BUG_WORKAROUND ++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) ++ { ++ s->mode = BAD; ++ z->msg = (char*)"too many length or distance symbols"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++#endif ++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); ++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) ++ { ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ DUMPBITS(14) ++ s->sub.trees.index = 0; ++ Tracev((stderr, "inflate: table sizes ok\n")); ++ s->mode = BTREE; ++ case BTREE: ++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) ++ { ++ NEEDBITS(3) ++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; ++ DUMPBITS(3) ++ } ++ while (s->sub.trees.index < 19) ++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0; ++ s->sub.trees.bb = 7; ++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, ++ &s->sub.trees.tb, s->hufts, z); ++ if (t != Z_OK) ++ { ++ r = t; ++ if (r == Z_DATA_ERROR) ++ { ++ ZFREE(z, s->sub.trees.blens); ++ s->mode = BAD; ++ } ++ LEAVE ++ } ++ s->sub.trees.index = 0; ++ Tracev((stderr, "inflate: bits tree ok\n")); ++ s->mode = DTREE; ++ case DTREE: ++ while (t = s->sub.trees.table, ++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) ++ { ++ inflate_huft *h; ++ uInt i, j, c; ++ ++ t = s->sub.trees.bb; ++ NEEDBITS(t) ++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); ++ t = h->bits; ++ c = h->base; ++ if (c < 16) ++ { ++ DUMPBITS(t) ++ s->sub.trees.blens[s->sub.trees.index++] = c; ++ } ++ else /* c == 16..18 */ ++ { ++ i = c == 18 ? 7 : c - 14; ++ j = c == 18 ? 11 : 3; ++ NEEDBITS(t + i) ++ DUMPBITS(t) ++ j += (uInt)b & inflate_mask[i]; ++ DUMPBITS(i) ++ i = s->sub.trees.index; ++ t = s->sub.trees.table; ++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || ++ (c == 16 && i < 1)) ++ { ++ ZFREE(z, s->sub.trees.blens); ++ s->mode = BAD; ++ z->msg = (char*)"invalid bit length repeat"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ } ++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0; ++ do { ++ s->sub.trees.blens[i++] = c; ++ } while (--j); ++ s->sub.trees.index = i; ++ } ++ } ++ s->sub.trees.tb = Z_NULL; ++ { ++ uInt bl, bd; ++ inflate_huft *tl, *td; ++ inflate_codes_statef *c; ++ ++ bl = 9; /* must be <= 9 for lookahead assumptions */ ++ bd = 6; /* must be <= 9 for lookahead assumptions */ ++ t = s->sub.trees.table; ++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), ++ s->sub.trees.blens, &bl, &bd, &tl, &td, ++ s->hufts, z); ++ if (t != Z_OK) ++ { ++ if (t == (uInt)Z_DATA_ERROR) ++ { ++ ZFREE(z, s->sub.trees.blens); ++ s->mode = BAD; ++ } ++ r = t; ++ LEAVE ++ } ++ Tracev((stderr, "inflate: trees ok\n")); ++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) ++ { ++ r = Z_MEM_ERROR; ++ LEAVE ++ } ++ s->sub.decode.codes = c; ++ } ++ ZFREE(z, s->sub.trees.blens); ++ s->mode = CODES; ++ case CODES: ++ UPDATE ++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) ++ return inflate_flush(s, z, r); ++ r = Z_OK; ++ inflate_codes_free(s->sub.decode.codes, z); ++ LOAD ++ Tracev((stderr, "inflate: codes end, %lu total out\n", ++ z->total_out + (q >= s->read ? q - s->read : ++ (s->end - s->read) + (q - s->window)))); ++ if (!s->last) ++ { ++ s->mode = TYPE; ++ break; ++ } ++ s->mode = DRY; ++ case DRY: ++ FLUSH ++ if (s->read != s->write) ++ LEAVE ++ s->mode = DONE; ++ case DONE: ++ r = Z_STREAM_END; ++ LEAVE ++ case BAD: ++ r = Z_DATA_ERROR; ++ LEAVE ++ default: ++ r = Z_STREAM_ERROR; ++ LEAVE ++ } ++} ++ ++ ++int inflate_blocks_free(s, z) ++inflate_blocks_statef *s; ++z_streamp z; ++{ ++ inflate_blocks_reset(s, z, Z_NULL); ++ ZFREE(z, s->window); ++ ZFREE(z, s->hufts); ++ ZFREE(z, s); ++ Tracev((stderr, "inflate: blocks freed\n")); ++ return Z_OK; ++} ++ ++ ++void inflate_set_dictionary(s, d, n) ++inflate_blocks_statef *s; ++const Bytef *d; ++uInt n; ++{ ++ zmemcpy(s->window, d, n); ++ s->read = s->write = s->window + n; ++} ++ ++ ++/* Returns true if inflate is currently at the end of a block generated ++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. ++ * IN assertion: s != Z_NULL ++ */ ++int inflate_blocks_sync_point(s) ++inflate_blocks_statef *s; ++{ ++ return s->mode == LENS; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infblock.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,39 @@ ++/* infblock.h -- header to use infblock.c ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++struct inflate_blocks_state; ++typedef struct inflate_blocks_state FAR inflate_blocks_statef; ++ ++extern inflate_blocks_statef * inflate_blocks_new OF(( ++ z_streamp z, ++ check_func c, /* check function */ ++ uInt w)); /* window size */ ++ ++extern int inflate_blocks OF(( ++ inflate_blocks_statef *, ++ z_streamp , ++ int)); /* initial return code */ ++ ++extern void inflate_blocks_reset OF(( ++ inflate_blocks_statef *, ++ z_streamp , ++ uLongf *)); /* check value on output */ ++ ++extern int inflate_blocks_free OF(( ++ inflate_blocks_statef *, ++ z_streamp)); ++ ++extern void inflate_set_dictionary OF(( ++ inflate_blocks_statef *s, ++ const Bytef *d, /* dictionary */ ++ uInt n)); /* dictionary length */ ++ ++extern int inflate_blocks_sync_point OF(( ++ inflate_blocks_statef *s)); +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infcodes.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,251 @@ ++/* infcodes.c -- process literals and length/distance pairs ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "inftrees.h" ++#include "infblock.h" ++#include "infcodes.h" ++#include "infutil.h" ++#include "inffast.h" ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ ++ START, /* x: set up for LEN */ ++ LEN, /* i: get length/literal/eob next */ ++ LENEXT, /* i: getting length extra (have base) */ ++ DIST, /* i: get distance next */ ++ DISTEXT, /* i: getting distance extra */ ++ COPY, /* o: copying bytes in window, waiting for space */ ++ LIT, /* o: got literal, waiting for output space */ ++ WASH, /* o: got eob, possibly still output waiting */ ++ END, /* x: got eob and all data flushed */ ++ BADCODE} /* x: got error */ ++inflate_codes_mode; ++ ++/* inflate codes private state */ ++struct inflate_codes_state { ++ ++ /* mode */ ++ inflate_codes_mode mode; /* current inflate_codes mode */ ++ ++ /* mode dependent information */ ++ uInt len; ++ union { ++ struct { ++ inflate_huft *tree; /* pointer into tree */ ++ uInt need; /* bits needed */ ++ } code; /* if LEN or DIST, where in tree */ ++ uInt lit; /* if LIT, literal */ ++ struct { ++ uInt get; /* bits to get for extra */ ++ uInt dist; /* distance back to copy from */ ++ } copy; /* if EXT or COPY, where and how much */ ++ } sub; /* submode */ ++ ++ /* mode independent information */ ++ Byte lbits; /* ltree bits decoded per branch */ ++ Byte dbits; /* dtree bits decoder per branch */ ++ inflate_huft *ltree; /* literal/length/eob tree */ ++ inflate_huft *dtree; /* distance tree */ ++ ++}; ++ ++ ++inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) ++uInt bl, bd; ++inflate_huft *tl; ++inflate_huft *td; /* need separate declaration for Borland C++ */ ++z_streamp z; ++{ ++ inflate_codes_statef *c; ++ ++ if ((c = (inflate_codes_statef *) ++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) ++ { ++ c->mode = START; ++ c->lbits = (Byte)bl; ++ c->dbits = (Byte)bd; ++ c->ltree = tl; ++ c->dtree = td; ++ Tracev((stderr, "inflate: codes new\n")); ++ } ++ return c; ++} ++ ++ ++int inflate_codes(s, z, r) ++inflate_blocks_statef *s; ++z_streamp z; ++int r; ++{ ++ uInt j; /* temporary storage */ ++ inflate_huft *t; /* temporary pointer */ ++ uInt e; /* extra bits or operation */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ Bytef *f; /* pointer to copy strings from */ ++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ ++ ++ /* copy input/output information to locals (UPDATE macro restores) */ ++ LOAD ++ ++ /* process input and output based on current state */ ++ while (1) switch (c->mode) ++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ ++ case START: /* x: set up for LEN */ ++#ifndef SLOW ++ if (m >= 258 && n >= 10) ++ { ++ UPDATE ++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); ++ LOAD ++ if (r != Z_OK) ++ { ++ c->mode = r == Z_STREAM_END ? WASH : BADCODE; ++ break; ++ } ++ } ++#endif /* !SLOW */ ++ c->sub.code.need = c->lbits; ++ c->sub.code.tree = c->ltree; ++ c->mode = LEN; ++ case LEN: /* i: get length/literal/eob next */ ++ j = c->sub.code.need; ++ NEEDBITS(j) ++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); ++ DUMPBITS(t->bits) ++ e = (uInt)(t->exop); ++ if (e == 0) /* literal */ ++ { ++ c->sub.lit = t->base; ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: literal '%c'\n" : ++ "inflate: literal 0x%02x\n", t->base)); ++ c->mode = LIT; ++ break; ++ } ++ if (e & 16) /* length */ ++ { ++ c->sub.copy.get = e & 15; ++ c->len = t->base; ++ c->mode = LENEXT; ++ break; ++ } ++ if ((e & 64) == 0) /* next table */ ++ { ++ c->sub.code.need = e; ++ c->sub.code.tree = t + t->base; ++ break; ++ } ++ if (e & 32) /* end of block */ ++ { ++ Tracevv((stderr, "inflate: end of block\n")); ++ c->mode = WASH; ++ break; ++ } ++ c->mode = BADCODE; /* invalid code */ ++ z->msg = (char*)"invalid literal/length code"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ case LENEXT: /* i: getting length extra (have base) */ ++ j = c->sub.copy.get; ++ NEEDBITS(j) ++ c->len += (uInt)b & inflate_mask[j]; ++ DUMPBITS(j) ++ c->sub.code.need = c->dbits; ++ c->sub.code.tree = c->dtree; ++ Tracevv((stderr, "inflate: length %u\n", c->len)); ++ c->mode = DIST; ++ case DIST: /* i: get distance next */ ++ j = c->sub.code.need; ++ NEEDBITS(j) ++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); ++ DUMPBITS(t->bits) ++ e = (uInt)(t->exop); ++ if (e & 16) /* distance */ ++ { ++ c->sub.copy.get = e & 15; ++ c->sub.copy.dist = t->base; ++ c->mode = DISTEXT; ++ break; ++ } ++ if ((e & 64) == 0) /* next table */ ++ { ++ c->sub.code.need = e; ++ c->sub.code.tree = t + t->base; ++ break; ++ } ++ c->mode = BADCODE; /* invalid code */ ++ z->msg = (char*)"invalid distance code"; ++ r = Z_DATA_ERROR; ++ LEAVE ++ case DISTEXT: /* i: getting distance extra */ ++ j = c->sub.copy.get; ++ NEEDBITS(j) ++ c->sub.copy.dist += (uInt)b & inflate_mask[j]; ++ DUMPBITS(j) ++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); ++ c->mode = COPY; ++ case COPY: /* o: copying bytes in window, waiting for space */ ++ f = q - c->sub.copy.dist; ++ while (f < s->window) /* modulo window size-"while" instead */ ++ f += s->end - s->window; /* of "if" handles invalid distances */ ++ while (c->len) ++ { ++ NEEDOUT ++ OUTBYTE(*f++) ++ if (f == s->end) ++ f = s->window; ++ c->len--; ++ } ++ c->mode = START; ++ break; ++ case LIT: /* o: got literal, waiting for output space */ ++ NEEDOUT ++ OUTBYTE(c->sub.lit) ++ c->mode = START; ++ break; ++ case WASH: /* o: got eob, possibly more output */ ++ if (k > 7) /* return unused byte, if any */ ++ { ++ Assert(k < 16, "inflate_codes grabbed too many bytes") ++ k -= 8; ++ n++; ++ p--; /* can always return one */ ++ } ++ FLUSH ++ if (s->read != s->write) ++ LEAVE ++ c->mode = END; ++ case END: ++ r = Z_STREAM_END; ++ LEAVE ++ case BADCODE: /* x: got error */ ++ r = Z_DATA_ERROR; ++ LEAVE ++ default: ++ r = Z_STREAM_ERROR; ++ LEAVE ++ } ++#ifdef NEED_DUMMY_RETURN ++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ ++#endif ++} ++ ++ ++void inflate_codes_free(c, z) ++inflate_codes_statef *c; ++z_streamp z; ++{ ++ ZFREE(z, c); ++ Tracev((stderr, "inflate: codes free\n")); ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infcodes.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,31 @@ ++/* infcodes.h -- header to use infcodes.c ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++#ifndef _INFCODES_H ++#define _INFCODES_H ++ ++struct inflate_codes_state; ++typedef struct inflate_codes_state FAR inflate_codes_statef; ++ ++extern inflate_codes_statef *inflate_codes_new OF(( ++ uInt, uInt, ++ inflate_huft *, inflate_huft *, ++ z_streamp )); ++ ++extern int inflate_codes OF(( ++ inflate_blocks_statef *, ++ z_streamp , ++ int)); ++ ++extern void inflate_codes_free OF(( ++ inflate_codes_statef *, ++ z_streamp )); ++ ++#endif /* _INFCODES_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inffast.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,183 @@ ++/* inffast.c -- process literals and length/distance pairs fast ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "inftrees.h" ++#include "infblock.h" ++#include "infcodes.h" ++#include "infutil.h" ++#include "inffast.h" ++ ++struct inflate_codes_state {int dummy;}; /* for buggy compilers */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++/* macros for bit input with no checking and for returning unused bytes */ ++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} ++ ++/* Called with number of bytes left to write in window at least 258 ++ (the maximum string length) and number of input bytes available ++ at least ten. The ten bytes are six bytes for the longest length/ ++ distance pair plus four bytes for overloading the bit buffer. */ ++ ++int inflate_fast(bl, bd, tl, td, s, z) ++uInt bl, bd; ++inflate_huft *tl; ++inflate_huft *td; /* need separate declaration for Borland C++ */ ++inflate_blocks_statef *s; ++z_streamp z; ++{ ++ inflate_huft *t; /* temporary pointer */ ++ uInt e; /* extra bits or operation */ ++ uLong b; /* bit buffer */ ++ uInt k; /* bits in bit buffer */ ++ Bytef *p; /* input data pointer */ ++ uInt n; /* bytes available there */ ++ Bytef *q; /* output window write pointer */ ++ uInt m; /* bytes to end of window or read pointer */ ++ uInt ml; /* mask for literal/length tree */ ++ uInt md; /* mask for distance tree */ ++ uInt c; /* bytes to copy */ ++ uInt d; /* distance back to copy from */ ++ Bytef *r; /* copy source pointer */ ++ ++ /* load input, output, bit values */ ++ LOAD ++ ++ /* initialize masks */ ++ ml = inflate_mask[bl]; ++ md = inflate_mask[bd]; ++ ++ /* do until not enough input or output space for fast loop */ ++ do { /* assume called with m >= 258 && n >= 10 */ ++ /* get literal/length code */ ++ GRABBITS(20) /* max bits for literal/length code */ ++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) ++ { ++ DUMPBITS(t->bits) ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: * literal '%c'\n" : ++ "inflate: * literal 0x%02x\n", t->base)); ++ *q++ = (Byte)t->base; ++ m--; ++ continue; ++ } ++ do { ++ DUMPBITS(t->bits) ++ if (e & 16) ++ { ++ /* get extra bits for length */ ++ e &= 15; ++ c = t->base + ((uInt)b & inflate_mask[e]); ++ DUMPBITS(e) ++ Tracevv((stderr, "inflate: * length %u\n", c)); ++ ++ /* decode distance base of block to copy */ ++ GRABBITS(15); /* max bits for distance code */ ++ e = (t = td + ((uInt)b & md))->exop; ++ do { ++ DUMPBITS(t->bits) ++ if (e & 16) ++ { ++ /* get extra bits to add to distance base */ ++ e &= 15; ++ GRABBITS(e) /* get extra bits (up to 13) */ ++ d = t->base + ((uInt)b & inflate_mask[e]); ++ DUMPBITS(e) ++ Tracevv((stderr, "inflate: * distance %u\n", d)); ++ ++ /* do the copy */ ++ m -= c; ++ r = q - d; ++ if (r < s->window) /* wrap if needed */ ++ { ++ do { ++ r += s->end - s->window; /* force pointer in window */ ++ } while (r < s->window); /* covers invalid distances */ ++ e = s->end - r; ++ if (c > e) ++ { ++ c -= e; /* wrapped copy */ ++ do { ++ *q++ = *r++; ++ } while (--e); ++ r = s->window; ++ do { ++ *q++ = *r++; ++ } while (--c); ++ } ++ else /* normal copy */ ++ { ++ *q++ = *r++; c--; ++ *q++ = *r++; c--; ++ do { ++ *q++ = *r++; ++ } while (--c); ++ } ++ } ++ else /* normal copy */ ++ { ++ *q++ = *r++; c--; ++ *q++ = *r++; c--; ++ do { ++ *q++ = *r++; ++ } while (--c); ++ } ++ break; ++ } ++ else if ((e & 64) == 0) ++ { ++ t += t->base; ++ e = (t += ((uInt)b & inflate_mask[e]))->exop; ++ } ++ else ++ { ++ z->msg = (char*)"invalid distance code"; ++ UNGRAB ++ UPDATE ++ return Z_DATA_ERROR; ++ } ++ } while (1); ++ break; ++ } ++ if ((e & 64) == 0) ++ { ++ t += t->base; ++ if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) ++ { ++ DUMPBITS(t->bits) ++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? ++ "inflate: * literal '%c'\n" : ++ "inflate: * literal 0x%02x\n", t->base)); ++ *q++ = (Byte)t->base; ++ m--; ++ break; ++ } ++ } ++ else if (e & 32) ++ { ++ Tracevv((stderr, "inflate: * end of block\n")); ++ UNGRAB ++ UPDATE ++ return Z_STREAM_END; ++ } ++ else ++ { ++ z->msg = (char*)"invalid literal/length code"; ++ UNGRAB ++ UPDATE ++ return Z_DATA_ERROR; ++ } ++ } while (1); ++ } while (m >= 258 && n >= 10); ++ ++ /* not enough input or output--restore pointers and return */ ++ UNGRAB ++ UPDATE ++ return Z_OK; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inffast.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,22 @@ ++/* inffast.h -- header to use inffast.c ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++#ifndef _INFFAST_H ++#define _INFFAST_H ++ ++extern int inflate_fast OF(( ++ uInt, ++ uInt, ++ inflate_huft *, ++ inflate_huft *, ++ inflate_blocks_statef *, ++ z_streamp )); ++ ++#endif /* _INFFAST_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inffixed.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,151 @@ ++/* inffixed.h -- table for decoding fixed codes ++ * Generated automatically by the maketree.c program ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++local uInt fixed_bl = 9; ++local uInt fixed_bd = 5; ++local inflate_huft fixed_tl[] = { ++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, ++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, ++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, ++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, ++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, ++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, ++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, ++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, ++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, ++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, ++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, ++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, ++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, ++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, ++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, ++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, ++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, ++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, ++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, ++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, ++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, ++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, ++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, ++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, ++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, ++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, ++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, ++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, ++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, ++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, ++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, ++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, ++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, ++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, ++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, ++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, ++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, ++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, ++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, ++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, ++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, ++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, ++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, ++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, ++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, ++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, ++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, ++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, ++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, ++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, ++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, ++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, ++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, ++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, ++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, ++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, ++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, ++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, ++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, ++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, ++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, ++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, ++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, ++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, ++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, ++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, ++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, ++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, ++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, ++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, ++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, ++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, ++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, ++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, ++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, ++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, ++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, ++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, ++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, ++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, ++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, ++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, ++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, ++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, ++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, ++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, ++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, ++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, ++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, ++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, ++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, ++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, ++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, ++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, ++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, ++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, ++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, ++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, ++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, ++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, ++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, ++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, ++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, ++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, ++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, ++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, ++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, ++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, ++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, ++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, ++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, ++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, ++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, ++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, ++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, ++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, ++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, ++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, ++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, ++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, ++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, ++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, ++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, ++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, ++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, ++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, ++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, ++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} ++ }; ++local inflate_huft fixed_td[] = { ++ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, ++ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, ++ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, ++ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, ++ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, ++ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, ++ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, ++ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} ++ }; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inflate.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,368 @@ ++/* inflate.c -- zlib interface to inflate modules ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "infblock.h" ++ ++struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ ++ ++typedef enum { ++ METHOD, /* waiting for method byte */ ++ FLAG, /* waiting for flag byte */ ++ DICT4, /* four dictionary check bytes to go */ ++ DICT3, /* three dictionary check bytes to go */ ++ DICT2, /* two dictionary check bytes to go */ ++ DICT1, /* one dictionary check byte to go */ ++ DICT0, /* waiting for inflateSetDictionary */ ++ BLOCKS, /* decompressing blocks */ ++ CHECK4, /* four check bytes to go */ ++ CHECK3, /* three check bytes to go */ ++ CHECK2, /* two check bytes to go */ ++ CHECK1, /* one check byte to go */ ++ DONE, /* finished check, done */ ++ BAD} /* got an error--stay here */ ++inflate_mode; ++ ++/* inflate private state */ ++struct internal_state { ++ ++ /* mode */ ++ inflate_mode mode; /* current inflate mode */ ++ ++ /* mode dependent information */ ++ union { ++ uInt method; /* if FLAGS, method byte */ ++ struct { ++ uLong was; /* computed check value */ ++ uLong need; /* stream check value */ ++ } check; /* if CHECK, check values to compare */ ++ uInt marker; /* if BAD, inflateSync's marker bytes count */ ++ } sub; /* submode */ ++ ++ /* mode independent information */ ++ int nowrap; /* flag for no wrapper */ ++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */ ++ inflate_blocks_statef ++ *blocks; /* current inflate_blocks state */ ++ ++}; ++ ++ ++int ZEXPORT inflateReset(z) ++z_streamp z; ++{ ++ if (z == Z_NULL || z->state == Z_NULL) ++ return Z_STREAM_ERROR; ++ z->total_in = z->total_out = 0; ++ z->msg = Z_NULL; ++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD; ++ inflate_blocks_reset(z->state->blocks, z, Z_NULL); ++ Tracev((stderr, "inflate: reset\n")); ++ return Z_OK; ++} ++ ++ ++int ZEXPORT inflateEnd(z) ++z_streamp z; ++{ ++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) ++ return Z_STREAM_ERROR; ++ if (z->state->blocks != Z_NULL) ++ inflate_blocks_free(z->state->blocks, z); ++ ZFREE(z, z->state); ++ z->state = Z_NULL; ++ Tracev((stderr, "inflate: end\n")); ++ return Z_OK; ++} ++ ++ ++int ZEXPORT inflateInit2_(z, w, version, stream_size) ++z_streamp z; ++int w; ++const char *version; ++int stream_size; ++{ ++ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || ++ stream_size != sizeof(z_stream)) ++ return Z_VERSION_ERROR; ++ ++ /* initialize state */ ++ if (z == Z_NULL) ++ return Z_STREAM_ERROR; ++ z->msg = Z_NULL; ++ if (z->zalloc == Z_NULL) ++ { ++ return Z_STREAM_ERROR; ++/* z->zalloc = zcalloc; ++ z->opaque = (voidpf)0; ++*/ ++ } ++ if (z->zfree == Z_NULL) return Z_STREAM_ERROR; /* z->zfree = zcfree; */ ++ if ((z->state = (struct internal_state FAR *) ++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) ++ return Z_MEM_ERROR; ++ z->state->blocks = Z_NULL; ++ ++ /* handle undocumented nowrap option (no zlib header or check) */ ++ z->state->nowrap = 0; ++ if (w < 0) ++ { ++ w = - w; ++ z->state->nowrap = 1; ++ } ++ ++ /* set window size */ ++ if (w < 8 || w > 15) ++ { ++ inflateEnd(z); ++ return Z_STREAM_ERROR; ++ } ++ z->state->wbits = (uInt)w; ++ ++ /* create inflate_blocks state */ ++ if ((z->state->blocks = ++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) ++ == Z_NULL) ++ { ++ inflateEnd(z); ++ return Z_MEM_ERROR; ++ } ++ Tracev((stderr, "inflate: allocated\n")); ++ ++ /* reset state */ ++ inflateReset(z); ++ return Z_OK; ++} ++ ++ ++int ZEXPORT inflateInit_(z, version, stream_size) ++z_streamp z; ++const char *version; ++int stream_size; ++{ ++ return inflateInit2_(z, DEF_WBITS, version, stream_size); ++} ++ ++ ++#define NEEDBYTE {if(z->avail_in==0)return r;r=f;} ++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) ++ ++int ZEXPORT inflate(z, f) ++z_streamp z; ++int f; ++{ ++ int r; ++ uInt b; ++ ++ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) ++ return Z_STREAM_ERROR; ++ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; ++ r = Z_BUF_ERROR; ++ while (1) switch (z->state->mode) ++ { ++ case METHOD: ++ NEEDBYTE ++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) ++ { ++ z->state->mode = BAD; ++ z->msg = (char*)"unknown compression method"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits) ++ { ++ z->state->mode = BAD; ++ z->msg = (char*)"invalid window size"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ z->state->mode = FLAG; ++ case FLAG: ++ NEEDBYTE ++ b = NEXTBYTE; ++ if (((z->state->sub.method << 8) + b) % 31) ++ { ++ z->state->mode = BAD; ++ z->msg = (char*)"incorrect header check"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ Tracev((stderr, "inflate: zlib header ok\n")); ++ if (!(b & PRESET_DICT)) ++ { ++ z->state->mode = BLOCKS; ++ break; ++ } ++ z->state->mode = DICT4; ++ case DICT4: ++ NEEDBYTE ++ z->state->sub.check.need = (uLong)NEXTBYTE << 24; ++ z->state->mode = DICT3; ++ case DICT3: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 16; ++ z->state->mode = DICT2; ++ case DICT2: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 8; ++ z->state->mode = DICT1; ++ case DICT1: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE; ++ z->adler = z->state->sub.check.need; ++ z->state->mode = DICT0; ++ return Z_NEED_DICT; ++ case DICT0: ++ z->state->mode = BAD; ++ z->msg = (char*)"need dictionary"; ++ z->state->sub.marker = 0; /* can try inflateSync */ ++ return Z_STREAM_ERROR; ++ case BLOCKS: ++ r = inflate_blocks(z->state->blocks, z, r); ++ if (r == Z_DATA_ERROR) ++ { ++ z->state->mode = BAD; ++ z->state->sub.marker = 0; /* can try inflateSync */ ++ break; ++ } ++ if (r == Z_OK) ++ r = f; ++ if (r != Z_STREAM_END) ++ return r; ++ r = f; ++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); ++ if (z->state->nowrap) ++ { ++ z->state->mode = DONE; ++ break; ++ } ++ z->state->mode = CHECK4; ++ case CHECK4: ++ NEEDBYTE ++ z->state->sub.check.need = (uLong)NEXTBYTE << 24; ++ z->state->mode = CHECK3; ++ case CHECK3: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 16; ++ z->state->mode = CHECK2; ++ case CHECK2: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE << 8; ++ z->state->mode = CHECK1; ++ case CHECK1: ++ NEEDBYTE ++ z->state->sub.check.need += (uLong)NEXTBYTE; ++ ++ if (z->state->sub.check.was != z->state->sub.check.need) ++ { ++ z->state->mode = BAD; ++ z->msg = (char*)"incorrect data check"; ++ z->state->sub.marker = 5; /* can't try inflateSync */ ++ break; ++ } ++ Tracev((stderr, "inflate: zlib check ok\n")); ++ z->state->mode = DONE; ++ case DONE: ++ return Z_STREAM_END; ++ case BAD: ++ return Z_DATA_ERROR; ++ default: ++ return Z_STREAM_ERROR; ++ } ++#ifdef NEED_DUMMY_RETURN ++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ ++#endif ++} ++ ++ ++int ZEXPORT inflateSetDictionary(z, dictionary, dictLength) ++z_streamp z; ++const Bytef *dictionary; ++uInt dictLength; ++{ ++ uInt length = dictLength; ++ ++ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) ++ return Z_STREAM_ERROR; ++ ++ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; ++ z->adler = 1L; ++ ++ if (length >= ((uInt)1<state->wbits)) ++ { ++ length = (1<state->wbits)-1; ++ dictionary += dictLength - length; ++ } ++ inflate_set_dictionary(z->state->blocks, dictionary, length); ++ z->state->mode = BLOCKS; ++ return Z_OK; ++} ++ ++ ++int ZEXPORT inflateSync(z) ++z_streamp z; ++{ ++ uInt n; /* number of bytes to look at */ ++ Bytef *p; /* pointer to bytes */ ++ uInt m; /* number of marker bytes found in a row */ ++ uLong r, w; /* temporaries to save total_in and total_out */ ++ ++ /* set up */ ++ if (z == Z_NULL || z->state == Z_NULL) ++ return Z_STREAM_ERROR; ++ if (z->state->mode != BAD) ++ { ++ z->state->mode = BAD; ++ z->state->sub.marker = 0; ++ } ++ if ((n = z->avail_in) == 0) ++ return Z_BUF_ERROR; ++ p = z->next_in; ++ m = z->state->sub.marker; ++ ++ /* search */ ++ while (n && m < 4) ++ { ++ static const Byte mark[4] = {0, 0, 0xff, 0xff}; ++ if (*p == mark[m]) ++ m++; ++ else if (*p) ++ m = 0; ++ else ++ m = 4 - m; ++ p++, n--; ++ } ++ ++ /* restore */ ++ z->total_in += p - z->next_in; ++ z->next_in = p; ++ z->avail_in = n; ++ z->state->sub.marker = m; ++ ++ /* return no joy or set up to restart on a new block */ ++ if (m != 4) ++ return Z_DATA_ERROR; ++ r = z->total_in; w = z->total_out; ++ inflateReset(z); ++ z->total_in = r; z->total_out = w; ++ z->state->mode = BLOCKS; ++ return Z_OK; ++} ++ ++ ++/* Returns true if inflate is currently at the end of a block generated ++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP ++ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH ++ * but removes the length bytes of the resulting empty stored block. When ++ * decompressing, PPP checks that at the end of input packet, inflate is ++ * waiting for these length bytes. ++ */ ++int ZEXPORT inflateSyncPoint(z) ++z_streamp z; ++{ ++ if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) ++ return Z_STREAM_ERROR; ++ return inflate_blocks_sync_point(z->state->blocks); ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inftrees.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,454 @@ ++/* inftrees.c -- generate Huffman trees for efficient decoding ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "inftrees.h" ++ ++#if !defined(BUILDFIXED) && !defined(STDC) ++# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */ ++#endif ++ ++local const char inflate_copyright[] = ++ " inflate 1.1.4 Copyright 1995-2002 Mark Adler "; ++/* ++ If you use the zlib library in a product, an acknowledgment is welcome ++ in the documentation of your product. If for some reason you cannot ++ include such an acknowledgment, I would appreciate that you keep this ++ copyright string in the executable of your product. ++ */ ++struct internal_state {int dummy;}; /* for buggy compilers */ ++ ++/* simplify the use of the inflate_huft type with some defines */ ++#define exop word.what.Exop ++#define bits word.what.Bits ++ ++ ++local int huft_build OF(( ++ uIntf *, /* code lengths in bits */ ++ uInt, /* number of codes */ ++ uInt, /* number of "simple" codes */ ++ const uIntf *, /* list of base values for non-simple codes */ ++ const uIntf *, /* list of extra bits for non-simple codes */ ++ inflate_huft * FAR*,/* result: starting table */ ++ uIntf *, /* maximum lookup bits (returns actual) */ ++ inflate_huft *, /* space for trees */ ++ uInt *, /* hufts used in space */ ++ uIntf * )); /* space for values */ ++ ++/* Tables for deflate from PKZIP's appnote.txt. */ ++local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ ++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, ++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; ++ /* see note #13 above about 258 */ ++local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, ++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ ++local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ ++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, ++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, ++ 8193, 12289, 16385, 24577}; ++local const uInt cpdext[30] = { /* Extra bits for distance codes */ ++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, ++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, ++ 12, 12, 13, 13}; ++ ++/* ++ Huffman code decoding is performed using a multi-level table lookup. ++ The fastest way to decode is to simply build a lookup table whose ++ size is determined by the longest code. However, the time it takes ++ to build this table can also be a factor if the data being decoded ++ is not very long. The most common codes are necessarily the ++ shortest codes, so those codes dominate the decoding time, and hence ++ the speed. The idea is you can have a shorter table that decodes the ++ shorter, more probable codes, and then point to subsidiary tables for ++ the longer codes. The time it costs to decode the longer codes is ++ then traded against the time it takes to make longer tables. ++ ++ This results of this trade are in the variables lbits and dbits ++ below. lbits is the number of bits the first level table for literal/ ++ length codes can decode in one step, and dbits is the same thing for ++ the distance codes. Subsequent tables are also less than or equal to ++ those sizes. These values may be adjusted either when all of the ++ codes are shorter than that, in which case the longest code length in ++ bits is used, or when the shortest code is *longer* than the requested ++ table size, in which case the length of the shortest code in bits is ++ used. ++ ++ There are two different values for the two tables, since they code a ++ different number of possibilities each. The literal/length table ++ codes 286 possible values, or in a flat code, a little over eight ++ bits. The distance table codes 30 possible values, or a little less ++ than five bits, flat. The optimum values for speed end up being ++ about one bit more than those, so lbits is 8+1 and dbits is 5+1. ++ The optimum values may differ though from machine to machine, and ++ possibly even between compilers. Your mileage may vary. ++ */ ++ ++ ++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ ++#define BMAX 15 /* maximum bit length of any code */ ++ ++local int huft_build(b, n, s, d, e, t, m, hp, hn, v) ++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ ++uInt n; /* number of codes (assumed <= 288) */ ++uInt s; /* number of simple-valued codes (0..s-1) */ ++const uIntf *d; /* list of base values for non-simple codes */ ++const uIntf *e; /* list of extra bits for non-simple codes */ ++inflate_huft * FAR *t; /* result: starting table */ ++uIntf *m; /* maximum lookup bits, returns actual */ ++inflate_huft *hp; /* space for trees */ ++uInt *hn; /* hufts used in space */ ++uIntf *v; /* working area: values in order of bit length */ ++/* Given a list of code lengths and a maximum table size, make a set of ++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR ++ if the given code set is incomplete (the tables are still built in this ++ case), or Z_DATA_ERROR if the input is invalid. */ ++{ ++ ++ uInt a; /* counter for codes of length k */ ++ uInt c[BMAX+1]; /* bit length count table */ ++ uInt f; /* i repeats in table every f entries */ ++ int g; /* maximum code length */ ++ int h; /* table level */ ++ register uInt i; /* counter, current code */ ++ register uInt j; /* counter */ ++ register int k; /* number of bits in current code */ ++ int l; /* bits per table (returned in m) */ ++ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ ++ register uIntf *p; /* pointer into c[], b[], or v[] */ ++ inflate_huft *q; /* points to current table */ ++ struct inflate_huft_s r; /* table entry for structure assignment */ ++ inflate_huft *u[BMAX]; /* table stack */ ++ register int w; /* bits before this table == (l * h) */ ++ uInt x[BMAX+1]; /* bit offsets, then code stack */ ++ uIntf *xp; /* pointer into x */ ++ int y; /* number of dummy codes added */ ++ uInt z; /* number of entries in current table */ ++ ++ ++ /* Generate counts for each bit length */ ++ p = c; ++#define C0 *p++ = 0; ++#define C2 C0 C0 C0 C0 ++#define C4 C2 C2 C2 C2 ++ C4 /* clear c[]--assume BMAX+1 is 16 */ ++ p = b; i = n; ++ do { ++ c[*p++]++; /* assume all entries <= BMAX */ ++ } while (--i); ++ if (c[0] == n) /* null input--all zero length codes */ ++ { ++ *t = (inflate_huft *)Z_NULL; ++ *m = 0; ++ return Z_OK; ++ } ++ ++ ++ /* Find minimum and maximum length, bound *m by those */ ++ l = *m; ++ for (j = 1; j <= BMAX; j++) ++ if (c[j]) ++ break; ++ k = j; /* minimum code length */ ++ if ((uInt)l < j) ++ l = j; ++ for (i = BMAX; i; i--) ++ if (c[i]) ++ break; ++ g = i; /* maximum code length */ ++ if ((uInt)l > i) ++ l = i; ++ *m = l; ++ ++ ++ /* Adjust last length count to fill out codes, if needed */ ++ for (y = 1 << j; j < i; j++, y <<= 1) ++ if ((y -= c[j]) < 0) ++ return Z_DATA_ERROR; ++ if ((y -= c[i]) < 0) ++ return Z_DATA_ERROR; ++ c[i] += y; ++ ++ ++ /* Generate starting offsets into the value table for each length */ ++ x[1] = j = 0; ++ p = c + 1; xp = x + 2; ++ while (--i) { /* note that i == g from above */ ++ *xp++ = (j += *p++); ++ } ++ ++ ++ /* Make a table of values in order of bit lengths */ ++ p = b; i = 0; ++ do { ++ if ((j = *p++) != 0) ++ v[x[j]++] = i; ++ } while (++i < n); ++ n = x[g]; /* set n to length of v */ ++ ++ ++ /* Generate the Huffman codes and for each, make the table entries */ ++ x[0] = i = 0; /* first Huffman code is zero */ ++ p = v; /* grab values in bit order */ ++ h = -1; /* no tables yet--level -1 */ ++ w = -l; /* bits decoded == (l * h) */ ++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ ++ q = (inflate_huft *)Z_NULL; /* ditto */ ++ z = 0; /* ditto */ ++ ++ /* go through the bit lengths (k already is bits in shortest code) */ ++ for (; k <= g; k++) ++ { ++ a = c[k]; ++ while (a--) ++ { ++ /* here i is the Huffman code of length k bits for value *p */ ++ /* make tables up to required level */ ++ while (k > w + l) ++ { ++ h++; ++ w += l; /* previous table always l bits */ ++ ++ /* compute minimum size table less than or equal to l bits */ ++ z = g - w; ++ z = z > (uInt)l ? l : z; /* table size upper limit */ ++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ ++ { /* too few codes for k-w bit table */ ++ f -= a + 1; /* deduct codes from patterns left */ ++ xp = c + k; ++ if (j < z) ++ while (++j < z) /* try smaller tables up to z bits */ ++ { ++ if ((f <<= 1) <= *++xp) ++ break; /* enough codes to use up j bits */ ++ f -= *xp; /* else deduct codes from patterns */ ++ } ++ } ++ z = 1 << j; /* table entries for j-bit table */ ++ ++ /* allocate new table */ ++ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ ++ return Z_DATA_ERROR; /* overflow of MANY */ ++ u[h] = q = hp + *hn; ++ *hn += z; ++ ++ /* connect to last table, if there is one */ ++ if (h) ++ { ++ x[h] = i; /* save pattern for backing up */ ++ r.bits = (Byte)l; /* bits to dump before this table */ ++ r.exop = (Byte)j; /* bits in this table */ ++ j = i >> (w - l); ++ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ ++ u[h-1][j] = r; /* connect to last table */ ++ } ++ else ++ *t = q; /* first table is returned result */ ++ } ++ ++ /* set up table entry in r */ ++ r.bits = (Byte)(k - w); ++ if (p >= v + n) ++ r.exop = 128 + 64; /* out of values--invalid code */ ++ else if (*p < s) ++ { ++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ ++ r.base = *p++; /* simple code is just the value */ ++ } ++ else ++ { ++ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ ++ r.base = d[*p++ - s]; ++ } ++ ++ /* fill code-like entries with r */ ++ f = 1 << (k - w); ++ for (j = i >> w; j < z; j += f) ++ q[j] = r; ++ ++ /* backwards increment the k-bit code i */ ++ for (j = 1 << (k - 1); i & j; j >>= 1) ++ i ^= j; ++ i ^= j; ++ ++ /* backup over finished tables */ ++ mask = (1 << w) - 1; /* needed on HP, cc -O bug */ ++ while ((i & mask) != x[h]) ++ { ++ h--; /* don't need to update q */ ++ w -= l; ++ mask = (1 << w) - 1; ++ } ++ } ++ } ++ ++ ++ /* Return Z_BUF_ERROR if we were given an incomplete table */ ++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; ++} ++ ++ ++int inflate_trees_bits(c, bb, tb, hp, z) ++uIntf *c; /* 19 code lengths */ ++uIntf *bb; /* bits tree desired/actual depth */ ++inflate_huft * FAR *tb; /* bits tree result */ ++inflate_huft *hp; /* space for trees */ ++z_streamp z; /* for messages */ ++{ ++ int r; ++ uInt hn = 0; /* hufts used in space */ ++ uIntf *v; /* work area for huft_build */ ++ ++ if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) ++ return Z_MEM_ERROR; ++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, ++ tb, bb, hp, &hn, v); ++ if (r == Z_DATA_ERROR) ++ z->msg = (char*)"oversubscribed dynamic bit lengths tree"; ++ else if (r == Z_BUF_ERROR || *bb == 0) ++ { ++ z->msg = (char*)"incomplete dynamic bit lengths tree"; ++ r = Z_DATA_ERROR; ++ } ++ ZFREE(z, v); ++ return r; ++} ++ ++ ++int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z) ++uInt nl; /* number of literal/length codes */ ++uInt nd; /* number of distance codes */ ++uIntf *c; /* that many (total) code lengths */ ++uIntf *bl; /* literal desired/actual bit depth */ ++uIntf *bd; /* distance desired/actual bit depth */ ++inflate_huft * FAR *tl; /* literal/length tree result */ ++inflate_huft * FAR *td; /* distance tree result */ ++inflate_huft *hp; /* space for trees */ ++z_streamp z; /* for messages */ ++{ ++ int r; ++ uInt hn = 0; /* hufts used in space */ ++ uIntf *v; /* work area for huft_build */ ++ ++ /* allocate work area */ ++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) ++ return Z_MEM_ERROR; ++ ++ /* build literal/length tree */ ++ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); ++ if (r != Z_OK || *bl == 0) ++ { ++ if (r == Z_DATA_ERROR) ++ z->msg = (char*)"oversubscribed literal/length tree"; ++ else if (r != Z_MEM_ERROR) ++ { ++ z->msg = (char*)"incomplete literal/length tree"; ++ r = Z_DATA_ERROR; ++ } ++ ZFREE(z, v); ++ return r; ++ } ++ ++ /* build distance tree */ ++ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); ++ if (r != Z_OK || (*bd == 0 && nl > 257)) ++ { ++ if (r == Z_DATA_ERROR) ++ z->msg = (char*)"oversubscribed distance tree"; ++ else if (r == Z_BUF_ERROR) { ++#ifdef PKZIP_BUG_WORKAROUND ++ r = Z_OK; ++ } ++#else ++ z->msg = (char*)"incomplete distance tree"; ++ r = Z_DATA_ERROR; ++ } ++ else if (r != Z_MEM_ERROR) ++ { ++ z->msg = (char*)"empty distance tree with lengths"; ++ r = Z_DATA_ERROR; ++ } ++ ZFREE(z, v); ++ return r; ++#endif ++ } ++ ++ /* done */ ++ ZFREE(z, v); ++ return Z_OK; ++} ++ ++ ++/* build fixed tables only once--keep them here */ ++#ifdef BUILDFIXED ++local int fixed_built = 0; ++#define FIXEDH 544 /* number of hufts used by fixed tables */ ++local inflate_huft fixed_mem[FIXEDH]; ++local uInt fixed_bl; ++local uInt fixed_bd; ++local inflate_huft *fixed_tl; ++local inflate_huft *fixed_td; ++#else ++#include "inffixed.h" ++#endif ++ ++ ++int inflate_trees_fixed(bl, bd, tl, td, z) ++uIntf *bl; /* literal desired/actual bit depth */ ++uIntf *bd; /* distance desired/actual bit depth */ ++inflate_huft * FAR *tl; /* literal/length tree result */ ++inflate_huft * FAR *td; /* distance tree result */ ++z_streamp z; /* for memory allocation */ ++{ ++#ifdef BUILDFIXED ++ /* build fixed tables if not already */ ++ if (!fixed_built) ++ { ++ int k; /* temporary variable */ ++ uInt f = 0; /* number of hufts used in fixed_mem */ ++ uIntf *c; /* length list for huft_build */ ++ uIntf *v; /* work area for huft_build */ ++ ++ /* allocate memory */ ++ if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) ++ return Z_MEM_ERROR; ++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) ++ { ++ ZFREE(z, c); ++ return Z_MEM_ERROR; ++ } ++ ++ /* literal table */ ++ for (k = 0; k < 144; k++) ++ c[k] = 8; ++ for (; k < 256; k++) ++ c[k] = 9; ++ for (; k < 280; k++) ++ c[k] = 7; ++ for (; k < 288; k++) ++ c[k] = 8; ++ fixed_bl = 9; ++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, ++ fixed_mem, &f, v); ++ ++ /* distance table */ ++ for (k = 0; k < 30; k++) ++ c[k] = 5; ++ fixed_bd = 5; ++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, ++ fixed_mem, &f, v); ++ ++ /* done */ ++ ZFREE(z, v); ++ ZFREE(z, c); ++ fixed_built = 1; ++ } ++#endif ++ *bl = fixed_bl; ++ *bd = fixed_bd; ++ *tl = fixed_tl; ++ *td = fixed_td; ++ return Z_OK; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/inftrees.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,63 @@ ++/* inftrees.h -- header to use inftrees.c ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++/* Huffman code lookup table entry--this entry is four bytes for machines ++ that have 16-bit pointers (e.g. PC's in the small or medium model). */ ++ ++#ifndef _INFTREES_H ++#define _INFTREES_H ++ ++typedef struct inflate_huft_s FAR inflate_huft; ++ ++struct inflate_huft_s { ++ union { ++ struct { ++ Byte Exop; /* number of extra bits or operation */ ++ Byte Bits; /* number of bits in this code or subcode */ ++ } what; ++ uInt pad; /* pad structure to a power of 2 (4 bytes for */ ++ } word; /* 16-bit, 8 bytes for 32-bit int's) */ ++ uInt base; /* literal, length base, distance base, ++ or table offset */ ++}; ++ ++/* Maximum size of dynamic tree. The maximum found in a long but non- ++ exhaustive search was 1004 huft structures (850 for length/literals ++ and 154 for distances, the latter actually the result of an ++ exhaustive search). The actual maximum is not known, but the ++ value below is more than safe. */ ++#define MANY 1440 ++ ++extern int inflate_trees_bits OF(( ++ uIntf *, /* 19 code lengths */ ++ uIntf *, /* bits tree desired/actual depth */ ++ inflate_huft * FAR *, /* bits tree result */ ++ inflate_huft *, /* space for trees */ ++ z_streamp)); /* for messages */ ++ ++extern int inflate_trees_dynamic OF(( ++ uInt, /* number of literal/length codes */ ++ uInt, /* number of distance codes */ ++ uIntf *, /* that many (total) code lengths */ ++ uIntf *, /* literal desired/actual bit depth */ ++ uIntf *, /* distance desired/actual bit depth */ ++ inflate_huft * FAR *, /* literal/length tree result */ ++ inflate_huft * FAR *, /* distance tree result */ ++ inflate_huft *, /* space for trees */ ++ z_streamp)); /* for messages */ ++ ++extern int inflate_trees_fixed OF(( ++ uIntf *, /* literal desired/actual bit depth */ ++ uIntf *, /* distance desired/actual bit depth */ ++ inflate_huft * FAR *, /* literal/length tree result */ ++ inflate_huft * FAR *, /* distance tree result */ ++ z_streamp)); /* for memory allocation */ ++ ++#endif /* _INFTREES_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infutil.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,87 @@ ++/* inflate_util.c -- data and routines common to blocks and codes ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++#include ++#include "infblock.h" ++#include "inftrees.h" ++#include "infcodes.h" ++#include "infutil.h" ++ ++struct inflate_codes_state {int dummy;}; /* for buggy compilers */ ++ ++/* And'ing with mask[n] masks the lower n bits */ ++uInt inflate_mask[17] = { ++ 0x0000, ++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, ++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff ++}; ++ ++ ++/* copy as much as possible from the sliding window to the output area */ ++int inflate_flush(s, z, r) ++inflate_blocks_statef *s; ++z_streamp z; ++int r; ++{ ++ uInt n; ++ Bytef *p; ++ Bytef *q; ++ ++ /* local copies of source and destination pointers */ ++ p = z->next_out; ++ q = s->read; ++ ++ /* compute number of bytes to copy as far as end of window */ ++ n = (uInt)((q <= s->write ? s->write : s->end) - q); ++ if (n > z->avail_out) n = z->avail_out; ++ if (n && r == Z_BUF_ERROR) r = Z_OK; ++ ++ /* update counters */ ++ z->avail_out -= n; ++ z->total_out += n; ++ ++ /* update check information */ ++ if (s->checkfn != Z_NULL) ++ z->adler = s->check = (*s->checkfn)(s->check, q, n); ++ ++ /* copy as far as end of window */ ++ zmemcpy(p, q, n); ++ p += n; ++ q += n; ++ ++ /* see if more to copy at beginning of window */ ++ if (q == s->end) ++ { ++ /* wrap pointers */ ++ q = s->window; ++ if (s->write == s->end) ++ s->write = s->window; ++ ++ /* compute bytes to copy */ ++ n = (uInt)(s->write - q); ++ if (n > z->avail_out) n = z->avail_out; ++ if (n && r == Z_BUF_ERROR) r = Z_OK; ++ ++ /* update counters */ ++ z->avail_out -= n; ++ z->total_out += n; ++ ++ /* update check information */ ++ if (s->checkfn != Z_NULL) ++ z->adler = s->check = (*s->checkfn)(s->check, q, n); ++ ++ /* copy */ ++ zmemcpy(p, q, n); ++ p += n; ++ q += n; ++ } ++ ++ /* update pointers */ ++ z->next_out = p; ++ s->read = q; ++ ++ /* done */ ++ return r; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/infutil.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,98 @@ ++/* infutil.h -- types and macros common to blocks and codes ++ * Copyright (C) 1995-2002 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* WARNING: this file should *not* be used by applications. It is ++ part of the implementation of the compression library and is ++ subject to change. Applications should only use zlib.h. ++ */ ++ ++#ifndef _INFUTIL_H ++#define _INFUTIL_H ++ ++typedef enum { ++ TYPE, /* get type bits (3, including end bit) */ ++ LENS, /* get lengths for stored */ ++ STORED, /* processing stored block */ ++ TABLE, /* get table lengths */ ++ BTREE, /* get bit lengths tree for a dynamic block */ ++ DTREE, /* get length, distance trees for a dynamic block */ ++ CODES, /* processing fixed or dynamic block */ ++ DRY, /* output remaining window bytes */ ++ DONE, /* finished last block, done */ ++ BAD} /* got a data error--stuck here */ ++inflate_block_mode; ++ ++/* inflate blocks semi-private state */ ++struct inflate_blocks_state { ++ ++ /* mode */ ++ inflate_block_mode mode; /* current inflate_block mode */ ++ ++ /* mode dependent information */ ++ union { ++ uInt left; /* if STORED, bytes left to copy */ ++ struct { ++ uInt table; /* table lengths (14 bits) */ ++ uInt index; /* index into blens (or border) */ ++ uIntf *blens; /* bit lengths of codes */ ++ uInt bb; /* bit length tree depth */ ++ inflate_huft *tb; /* bit length decoding tree */ ++ } trees; /* if DTREE, decoding info for trees */ ++ struct { ++ inflate_codes_statef ++ *codes; ++ } decode; /* if CODES, current state */ ++ } sub; /* submode */ ++ uInt last; /* true if this block is the last block */ ++ ++ /* mode independent information */ ++ uInt bitk; /* bits in bit buffer */ ++ uLong bitb; /* bit buffer */ ++ inflate_huft *hufts; /* single malloc for tree space */ ++ Bytef *window; /* sliding window */ ++ Bytef *end; /* one byte after sliding window */ ++ Bytef *read; /* window read pointer */ ++ Bytef *write; /* window write pointer */ ++ check_func checkfn; /* check function */ ++ uLong check; /* check on output */ ++ ++}; ++ ++ ++/* defines for inflate input/output */ ++/* update pointers and return */ ++#define UPDBITS {s->bitb=b;s->bitk=k;} ++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} ++#define UPDOUT {s->write=q;} ++#define UPDATE {UPDBITS UPDIN UPDOUT} ++#define LEAVE {UPDATE return inflate_flush(s,z,r);} ++/* get bytes and bits */ ++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} ++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} ++#define NEXTBYTE (n--,*p++) ++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} ++/* output bytes */ ++#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) ++#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} ++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} ++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} ++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} ++#define OUTBYTE(a) {*q++=(Byte)(a);m--;} ++/* load local pointers */ ++#define LOAD {LOADIN LOADOUT} ++ ++/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ ++extern uInt inflate_mask[17]; ++ ++/* copy as much as possible from the sliding window to the output area */ ++extern int inflate_flush OF(( ++ inflate_blocks_statef *, ++ z_streamp , ++ int)); ++ ++struct internal_state {int dummy;}; /* for buggy compilers */ ++ ++#endif /* _INFUTIL_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/match586.S Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,357 @@ ++/* match.s -- Pentium-optimized version of longest_match() ++ * Written for zlib 1.1.2 ++ * Copyright (C) 1998 Brian Raiter ++ * ++ * This is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License. ++ */ ++ ++#ifndef NO_UNDERLINE ++#define match_init _ipcomp_match_init ++#define longest_match _ipcomp_longest_match ++#else ++#define match_init ipcomp_match_init ++#define longest_match ipcomp_longest_match ++#endif ++ ++#define MAX_MATCH (258) ++#define MIN_MATCH (3) ++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1) ++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7) ++ ++/* stack frame offsets */ ++ ++#define wmask 0 /* local copy of s->wmask */ ++#define window 4 /* local copy of s->window */ ++#define windowbestlen 8 /* s->window + bestlen */ ++#define chainlenscanend 12 /* high word: current chain len */ ++ /* low word: last bytes sought */ ++#define scanstart 16 /* first two bytes of string */ ++#define scanalign 20 /* dword-misalignment of string */ ++#define nicematch 24 /* a good enough match size */ ++#define bestlen 28 /* size of best match so far */ ++#define scan 32 /* ptr to string wanting match */ ++ ++#define LocalVarsSize (36) ++/* saved ebx 36 */ ++/* saved edi 40 */ ++/* saved esi 44 */ ++/* saved ebp 48 */ ++/* return address 52 */ ++#define deflatestate 56 /* the function arguments */ ++#define curmatch 60 ++ ++/* Offsets for fields in the deflate_state structure. These numbers ++ * are calculated from the definition of deflate_state, with the ++ * assumption that the compiler will dword-align the fields. (Thus, ++ * changing the definition of deflate_state could easily cause this ++ * program to crash horribly, without so much as a warning at ++ * compile time. Sigh.) ++ */ ++#define dsWSize 36 ++#define dsWMask 44 ++#define dsWindow 48 ++#define dsPrev 56 ++#define dsMatchLen 88 ++#define dsPrevMatch 92 ++#define dsStrStart 100 ++#define dsMatchStart 104 ++#define dsLookahead 108 ++#define dsPrevLen 112 ++#define dsMaxChainLen 116 ++#define dsGoodMatch 132 ++#define dsNiceMatch 136 ++ ++ ++.file "match.S" ++ ++.globl match_init, longest_match ++ ++.text ++ ++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */ ++ ++longest_match: ++ ++/* Save registers that the compiler may be using, and adjust %esp to */ ++/* make room for our stack frame. */ ++ ++ pushl %ebp ++ pushl %edi ++ pushl %esi ++ pushl %ebx ++ subl $LocalVarsSize, %esp ++ ++/* Retrieve the function arguments. %ecx will hold cur_match */ ++/* throughout the entire function. %edx will hold the pointer to the */ ++/* deflate_state structure during the function's setup (before */ ++/* entering the main loop). */ ++ ++ movl deflatestate(%esp), %edx ++ movl curmatch(%esp), %ecx ++ ++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */ ++ ++ movl dsNiceMatch(%edx), %eax ++ movl dsLookahead(%edx), %ebx ++ cmpl %eax, %ebx ++ jl LookaheadLess ++ movl %eax, %ebx ++LookaheadLess: movl %ebx, nicematch(%esp) ++ ++/* register Bytef *scan = s->window + s->strstart; */ ++ ++ movl dsWindow(%edx), %esi ++ movl %esi, window(%esp) ++ movl dsStrStart(%edx), %ebp ++ lea (%esi,%ebp), %edi ++ movl %edi, scan(%esp) ++ ++/* Determine how many bytes the scan ptr is off from being */ ++/* dword-aligned. */ ++ ++ movl %edi, %eax ++ negl %eax ++ andl $3, %eax ++ movl %eax, scanalign(%esp) ++ ++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */ ++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */ ++ ++ movl dsWSize(%edx), %eax ++ subl $MIN_LOOKAHEAD, %eax ++ subl %eax, %ebp ++ jg LimitPositive ++ xorl %ebp, %ebp ++LimitPositive: ++ ++/* unsigned chain_length = s->max_chain_length; */ ++/* if (s->prev_length >= s->good_match) { */ ++/* chain_length >>= 2; */ ++/* } */ ++ ++ movl dsPrevLen(%edx), %eax ++ movl dsGoodMatch(%edx), %ebx ++ cmpl %ebx, %eax ++ movl dsMaxChainLen(%edx), %ebx ++ jl LastMatchGood ++ shrl $2, %ebx ++LastMatchGood: ++ ++/* chainlen is decremented once beforehand so that the function can */ ++/* use the sign flag instead of the zero flag for the exit test. */ ++/* It is then shifted into the high word, to make room for the scanend */ ++/* scanend value, which it will always accompany. */ ++ ++ decl %ebx ++ shll $16, %ebx ++ ++/* int best_len = s->prev_length; */ ++ ++ movl dsPrevLen(%edx), %eax ++ movl %eax, bestlen(%esp) ++ ++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */ ++ ++ addl %eax, %esi ++ movl %esi, windowbestlen(%esp) ++ ++/* register ush scan_start = *(ushf*)scan; */ ++/* register ush scan_end = *(ushf*)(scan+best_len-1); */ ++ ++ movw (%edi), %bx ++ movw %bx, scanstart(%esp) ++ movw -1(%edi,%eax), %bx ++ movl %ebx, chainlenscanend(%esp) ++ ++/* Posf *prev = s->prev; */ ++/* uInt wmask = s->w_mask; */ ++ ++ movl dsPrev(%edx), %edi ++ movl dsWMask(%edx), %edx ++ mov %edx, wmask(%esp) ++ ++/* Jump into the main loop. */ ++ ++ jmp LoopEntry ++ ++.balign 16 ++ ++/* do { ++ * match = s->window + cur_match; ++ * if (*(ushf*)(match+best_len-1) != scan_end || ++ * *(ushf*)match != scan_start) continue; ++ * [...] ++ * } while ((cur_match = prev[cur_match & wmask]) > limit ++ * && --chain_length != 0); ++ * ++ * Here is the inner loop of the function. The function will spend the ++ * majority of its time in this loop, and majority of that time will ++ * be spent in the first ten instructions. ++ * ++ * Within this loop: ++ * %ebx = chainlenscanend - i.e., ((chainlen << 16) | scanend) ++ * %ecx = curmatch ++ * %edx = curmatch & wmask ++ * %esi = windowbestlen - i.e., (window + bestlen) ++ * %edi = prev ++ * %ebp = limit ++ * ++ * Two optimization notes on the choice of instructions: ++ * ++ * The first instruction uses a 16-bit address, which costs an extra, ++ * unpairable cycle. This is cheaper than doing a 32-bit access and ++ * zeroing the high word, due to the 3-cycle misalignment penalty which ++ * would occur half the time. This also turns out to be cheaper than ++ * doing two separate 8-bit accesses, as the memory is so rarely in the ++ * L1 cache. ++ * ++ * The window buffer, however, apparently spends a lot of time in the ++ * cache, and so it is faster to retrieve the word at the end of the ++ * match string with two 8-bit loads. The instructions that test the ++ * word at the beginning of the match string, however, are executed ++ * much less frequently, and there it was cheaper to use 16-bit ++ * instructions, which avoided the necessity of saving off and ++ * subsequently reloading one of the other registers. ++ */ ++LookupLoop: ++ /* 1 U & V */ ++ movw (%edi,%edx,2), %cx /* 2 U pipe */ ++ movl wmask(%esp), %edx /* 2 V pipe */ ++ cmpl %ebp, %ecx /* 3 U pipe */ ++ jbe LeaveNow /* 3 V pipe */ ++ subl $0x00010000, %ebx /* 4 U pipe */ ++ js LeaveNow /* 4 V pipe */ ++LoopEntry: movb -1(%esi,%ecx), %al /* 5 U pipe */ ++ andl %ecx, %edx /* 5 V pipe */ ++ cmpb %bl, %al /* 6 U pipe */ ++ jnz LookupLoop /* 6 V pipe */ ++ movb (%esi,%ecx), %ah ++ cmpb %bh, %ah ++ jnz LookupLoop ++ movl window(%esp), %eax ++ movw (%eax,%ecx), %ax ++ cmpw scanstart(%esp), %ax ++ jnz LookupLoop ++ ++/* Store the current value of chainlen. */ ++ ++ movl %ebx, chainlenscanend(%esp) ++ ++/* Point %edi to the string under scrutiny, and %esi to the string we */ ++/* are hoping to match it up with. In actuality, %esi and %edi are */ ++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */ ++/* initialized to -(MAX_MATCH_8 - scanalign). */ ++ ++ movl window(%esp), %esi ++ movl scan(%esp), %edi ++ addl %ecx, %esi ++ movl scanalign(%esp), %eax ++ movl $(-MAX_MATCH_8), %edx ++ lea MAX_MATCH_8(%edi,%eax), %edi ++ lea MAX_MATCH_8(%esi,%eax), %esi ++ ++/* Test the strings for equality, 8 bytes at a time. At the end, ++ * adjust %edx so that it is offset to the exact byte that mismatched. ++ * ++ * We already know at this point that the first three bytes of the ++ * strings match each other, and they can be safely passed over before ++ * starting the compare loop. So what this code does is skip over 0-3 ++ * bytes, as much as necessary in order to dword-align the %edi ++ * pointer. (%esi will still be misaligned three times out of four.) ++ * ++ * It should be confessed that this loop usually does not represent ++ * much of the total running time. Replacing it with a more ++ * straightforward "rep cmpsb" would not drastically degrade ++ * performance. ++ */ ++LoopCmps: ++ movl (%esi,%edx), %eax ++ movl (%edi,%edx), %ebx ++ xorl %ebx, %eax ++ jnz LeaveLoopCmps ++ movl 4(%esi,%edx), %eax ++ movl 4(%edi,%edx), %ebx ++ xorl %ebx, %eax ++ jnz LeaveLoopCmps4 ++ addl $8, %edx ++ jnz LoopCmps ++ jmp LenMaximum ++LeaveLoopCmps4: addl $4, %edx ++LeaveLoopCmps: testl $0x0000FFFF, %eax ++ jnz LenLower ++ addl $2, %edx ++ shrl $16, %eax ++LenLower: subb $1, %al ++ adcl $0, %edx ++ ++/* Calculate the length of the match. If it is longer than MAX_MATCH, */ ++/* then automatically accept it as the best possible match and leave. */ ++ ++ lea (%edi,%edx), %eax ++ movl scan(%esp), %edi ++ subl %edi, %eax ++ cmpl $MAX_MATCH, %eax ++ jge LenMaximum ++ ++/* If the length of the match is not longer than the best match we */ ++/* have so far, then forget it and return to the lookup loop. */ ++ ++ movl deflatestate(%esp), %edx ++ movl bestlen(%esp), %ebx ++ cmpl %ebx, %eax ++ jg LongerMatch ++ movl chainlenscanend(%esp), %ebx ++ movl windowbestlen(%esp), %esi ++ movl dsPrev(%edx), %edi ++ movl wmask(%esp), %edx ++ andl %ecx, %edx ++ jmp LookupLoop ++ ++/* s->match_start = cur_match; */ ++/* best_len = len; */ ++/* if (len >= nice_match) break; */ ++/* scan_end = *(ushf*)(scan+best_len-1); */ ++ ++LongerMatch: movl nicematch(%esp), %ebx ++ movl %eax, bestlen(%esp) ++ movl %ecx, dsMatchStart(%edx) ++ cmpl %ebx, %eax ++ jge LeaveNow ++ movl window(%esp), %esi ++ addl %eax, %esi ++ movl %esi, windowbestlen(%esp) ++ movl chainlenscanend(%esp), %ebx ++ movw -1(%edi,%eax), %bx ++ movl dsPrev(%edx), %edi ++ movl %ebx, chainlenscanend(%esp) ++ movl wmask(%esp), %edx ++ andl %ecx, %edx ++ jmp LookupLoop ++ ++/* Accept the current string, with the maximum possible length. */ ++ ++LenMaximum: movl deflatestate(%esp), %edx ++ movl $MAX_MATCH, bestlen(%esp) ++ movl %ecx, dsMatchStart(%edx) ++ ++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */ ++/* return s->lookahead; */ ++ ++LeaveNow: ++ movl deflatestate(%esp), %edx ++ movl bestlen(%esp), %ebx ++ movl dsLookahead(%edx), %eax ++ cmpl %eax, %ebx ++ jg LookaheadRet ++ movl %ebx, %eax ++LookaheadRet: ++ ++/* Restore the stack and return from whence we came. */ ++ ++ addl $LocalVarsSize, %esp ++ popl %ebx ++ popl %esi ++ popl %edi ++ popl %ebp ++match_init: ret +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/match686.S Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,330 @@ ++/* match.s -- Pentium-Pro-optimized version of longest_match() ++ * Written for zlib 1.1.2 ++ * Copyright (C) 1998 Brian Raiter ++ * ++ * This is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License. ++ */ ++ ++#ifndef NO_UNDERLINE ++#define match_init _ipcomp_match_init ++#define longest_match _ipcomp_longest_match ++#else ++#define match_init ipcomp_match_init ++#define longest_match ipcomp_longest_match ++#endif ++ ++#define MAX_MATCH (258) ++#define MIN_MATCH (3) ++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1) ++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7) ++ ++/* stack frame offsets */ ++ ++#define chainlenwmask 0 /* high word: current chain len */ ++ /* low word: s->wmask */ ++#define window 4 /* local copy of s->window */ ++#define windowbestlen 8 /* s->window + bestlen */ ++#define scanstart 16 /* first two bytes of string */ ++#define scanend 12 /* last two bytes of string */ ++#define scanalign 20 /* dword-misalignment of string */ ++#define nicematch 24 /* a good enough match size */ ++#define bestlen 28 /* size of best match so far */ ++#define scan 32 /* ptr to string wanting match */ ++ ++#define LocalVarsSize (36) ++/* saved ebx 36 */ ++/* saved edi 40 */ ++/* saved esi 44 */ ++/* saved ebp 48 */ ++/* return address 52 */ ++#define deflatestate 56 /* the function arguments */ ++#define curmatch 60 ++ ++/* Offsets for fields in the deflate_state structure. These numbers ++ * are calculated from the definition of deflate_state, with the ++ * assumption that the compiler will dword-align the fields. (Thus, ++ * changing the definition of deflate_state could easily cause this ++ * program to crash horribly, without so much as a warning at ++ * compile time. Sigh.) ++ */ ++#define dsWSize 36 ++#define dsWMask 44 ++#define dsWindow 48 ++#define dsPrev 56 ++#define dsMatchLen 88 ++#define dsPrevMatch 92 ++#define dsStrStart 100 ++#define dsMatchStart 104 ++#define dsLookahead 108 ++#define dsPrevLen 112 ++#define dsMaxChainLen 116 ++#define dsGoodMatch 132 ++#define dsNiceMatch 136 ++ ++ ++.file "match.S" ++ ++.globl match_init, longest_match ++ ++.text ++ ++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */ ++ ++longest_match: ++ ++/* Save registers that the compiler may be using, and adjust %esp to */ ++/* make room for our stack frame. */ ++ ++ pushl %ebp ++ pushl %edi ++ pushl %esi ++ pushl %ebx ++ subl $LocalVarsSize, %esp ++ ++/* Retrieve the function arguments. %ecx will hold cur_match */ ++/* throughout the entire function. %edx will hold the pointer to the */ ++/* deflate_state structure during the function's setup (before */ ++/* entering the main loop). */ ++ ++ movl deflatestate(%esp), %edx ++ movl curmatch(%esp), %ecx ++ ++/* uInt wmask = s->w_mask; */ ++/* unsigned chain_length = s->max_chain_length; */ ++/* if (s->prev_length >= s->good_match) { */ ++/* chain_length >>= 2; */ ++/* } */ ++ ++ movl dsPrevLen(%edx), %eax ++ movl dsGoodMatch(%edx), %ebx ++ cmpl %ebx, %eax ++ movl dsWMask(%edx), %eax ++ movl dsMaxChainLen(%edx), %ebx ++ jl LastMatchGood ++ shrl $2, %ebx ++LastMatchGood: ++ ++/* chainlen is decremented once beforehand so that the function can */ ++/* use the sign flag instead of the zero flag for the exit test. */ ++/* It is then shifted into the high word, to make room for the wmask */ ++/* value, which it will always accompany. */ ++ ++ decl %ebx ++ shll $16, %ebx ++ orl %eax, %ebx ++ movl %ebx, chainlenwmask(%esp) ++ ++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */ ++ ++ movl dsNiceMatch(%edx), %eax ++ movl dsLookahead(%edx), %ebx ++ cmpl %eax, %ebx ++ jl LookaheadLess ++ movl %eax, %ebx ++LookaheadLess: movl %ebx, nicematch(%esp) ++ ++/* register Bytef *scan = s->window + s->strstart; */ ++ ++ movl dsWindow(%edx), %esi ++ movl %esi, window(%esp) ++ movl dsStrStart(%edx), %ebp ++ lea (%esi,%ebp), %edi ++ movl %edi, scan(%esp) ++ ++/* Determine how many bytes the scan ptr is off from being */ ++/* dword-aligned. */ ++ ++ movl %edi, %eax ++ negl %eax ++ andl $3, %eax ++ movl %eax, scanalign(%esp) ++ ++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */ ++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */ ++ ++ movl dsWSize(%edx), %eax ++ subl $MIN_LOOKAHEAD, %eax ++ subl %eax, %ebp ++ jg LimitPositive ++ xorl %ebp, %ebp ++LimitPositive: ++ ++/* int best_len = s->prev_length; */ ++ ++ movl dsPrevLen(%edx), %eax ++ movl %eax, bestlen(%esp) ++ ++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */ ++ ++ addl %eax, %esi ++ movl %esi, windowbestlen(%esp) ++ ++/* register ush scan_start = *(ushf*)scan; */ ++/* register ush scan_end = *(ushf*)(scan+best_len-1); */ ++/* Posf *prev = s->prev; */ ++ ++ movzwl (%edi), %ebx ++ movl %ebx, scanstart(%esp) ++ movzwl -1(%edi,%eax), %ebx ++ movl %ebx, scanend(%esp) ++ movl dsPrev(%edx), %edi ++ ++/* Jump into the main loop. */ ++ ++ movl chainlenwmask(%esp), %edx ++ jmp LoopEntry ++ ++.balign 16 ++ ++/* do { ++ * match = s->window + cur_match; ++ * if (*(ushf*)(match+best_len-1) != scan_end || ++ * *(ushf*)match != scan_start) continue; ++ * [...] ++ * } while ((cur_match = prev[cur_match & wmask]) > limit ++ * && --chain_length != 0); ++ * ++ * Here is the inner loop of the function. The function will spend the ++ * majority of its time in this loop, and majority of that time will ++ * be spent in the first ten instructions. ++ * ++ * Within this loop: ++ * %ebx = scanend ++ * %ecx = curmatch ++ * %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) ++ * %esi = windowbestlen - i.e., (window + bestlen) ++ * %edi = prev ++ * %ebp = limit ++ */ ++LookupLoop: ++ andl %edx, %ecx ++ movzwl (%edi,%ecx,2), %ecx ++ cmpl %ebp, %ecx ++ jbe LeaveNow ++ subl $0x00010000, %edx ++ js LeaveNow ++LoopEntry: movzwl -1(%esi,%ecx), %eax ++ cmpl %ebx, %eax ++ jnz LookupLoop ++ movl window(%esp), %eax ++ movzwl (%eax,%ecx), %eax ++ cmpl scanstart(%esp), %eax ++ jnz LookupLoop ++ ++/* Store the current value of chainlen. */ ++ ++ movl %edx, chainlenwmask(%esp) ++ ++/* Point %edi to the string under scrutiny, and %esi to the string we */ ++/* are hoping to match it up with. In actuality, %esi and %edi are */ ++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */ ++/* initialized to -(MAX_MATCH_8 - scanalign). */ ++ ++ movl window(%esp), %esi ++ movl scan(%esp), %edi ++ addl %ecx, %esi ++ movl scanalign(%esp), %eax ++ movl $(-MAX_MATCH_8), %edx ++ lea MAX_MATCH_8(%edi,%eax), %edi ++ lea MAX_MATCH_8(%esi,%eax), %esi ++ ++/* Test the strings for equality, 8 bytes at a time. At the end, ++ * adjust %edx so that it is offset to the exact byte that mismatched. ++ * ++ * We already know at this point that the first three bytes of the ++ * strings match each other, and they can be safely passed over before ++ * starting the compare loop. So what this code does is skip over 0-3 ++ * bytes, as much as necessary in order to dword-align the %edi ++ * pointer. (%esi will still be misaligned three times out of four.) ++ * ++ * It should be confessed that this loop usually does not represent ++ * much of the total running time. Replacing it with a more ++ * straightforward "rep cmpsb" would not drastically degrade ++ * performance. ++ */ ++LoopCmps: ++ movl (%esi,%edx), %eax ++ xorl (%edi,%edx), %eax ++ jnz LeaveLoopCmps ++ movl 4(%esi,%edx), %eax ++ xorl 4(%edi,%edx), %eax ++ jnz LeaveLoopCmps4 ++ addl $8, %edx ++ jnz LoopCmps ++ jmp LenMaximum ++LeaveLoopCmps4: addl $4, %edx ++LeaveLoopCmps: testl $0x0000FFFF, %eax ++ jnz LenLower ++ addl $2, %edx ++ shrl $16, %eax ++LenLower: subb $1, %al ++ adcl $0, %edx ++ ++/* Calculate the length of the match. If it is longer than MAX_MATCH, */ ++/* then automatically accept it as the best possible match and leave. */ ++ ++ lea (%edi,%edx), %eax ++ movl scan(%esp), %edi ++ subl %edi, %eax ++ cmpl $MAX_MATCH, %eax ++ jge LenMaximum ++ ++/* If the length of the match is not longer than the best match we */ ++/* have so far, then forget it and return to the lookup loop. */ ++ ++ movl deflatestate(%esp), %edx ++ movl bestlen(%esp), %ebx ++ cmpl %ebx, %eax ++ jg LongerMatch ++ movl windowbestlen(%esp), %esi ++ movl dsPrev(%edx), %edi ++ movl scanend(%esp), %ebx ++ movl chainlenwmask(%esp), %edx ++ jmp LookupLoop ++ ++/* s->match_start = cur_match; */ ++/* best_len = len; */ ++/* if (len >= nice_match) break; */ ++/* scan_end = *(ushf*)(scan+best_len-1); */ ++ ++LongerMatch: movl nicematch(%esp), %ebx ++ movl %eax, bestlen(%esp) ++ movl %ecx, dsMatchStart(%edx) ++ cmpl %ebx, %eax ++ jge LeaveNow ++ movl window(%esp), %esi ++ addl %eax, %esi ++ movl %esi, windowbestlen(%esp) ++ movzwl -1(%edi,%eax), %ebx ++ movl dsPrev(%edx), %edi ++ movl %ebx, scanend(%esp) ++ movl chainlenwmask(%esp), %edx ++ jmp LookupLoop ++ ++/* Accept the current string, with the maximum possible length. */ ++ ++LenMaximum: movl deflatestate(%esp), %edx ++ movl $MAX_MATCH, bestlen(%esp) ++ movl %ecx, dsMatchStart(%edx) ++ ++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */ ++/* return s->lookahead; */ ++ ++LeaveNow: ++ movl deflatestate(%esp), %edx ++ movl bestlen(%esp), %ebx ++ movl dsLookahead(%edx), %eax ++ cmpl %eax, %ebx ++ jg LookaheadRet ++ movl %ebx, %eax ++LookaheadRet: ++ ++/* Restore the stack and return from whence we came. */ ++ ++ addl $LocalVarsSize, %esp ++ popl %ebx ++ popl %esi ++ popl %edi ++ popl %ebp ++match_init: ret +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/trees.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1214 @@ ++/* trees.c -- output deflated data using Huffman coding ++ * Copyright (C) 1995-2002 Jean-loup Gailly ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* ++ * ALGORITHM ++ * ++ * The "deflation" process uses several Huffman trees. The more ++ * common source values are represented by shorter bit sequences. ++ * ++ * Each code tree is stored in a compressed form which is itself ++ * a Huffman encoding of the lengths of all the code strings (in ++ * ascending order by source values). The actual code strings are ++ * reconstructed from the lengths in the inflate process, as described ++ * in the deflate specification. ++ * ++ * REFERENCES ++ * ++ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". ++ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc ++ * ++ * Storer, James A. ++ * Data Compression: Methods and Theory, pp. 49-50. ++ * Computer Science Press, 1988. ISBN 0-7167-8156-5. ++ * ++ * Sedgewick, R. ++ * Algorithms, p290. ++ * Addison-Wesley, 1983. ISBN 0-201-06672-6. ++ */ ++ ++/* @(#) $Id: trees.c,v 1.3 2002/04/24 07:36:45 mcr Exp $ */ ++ ++/* #define GEN_TREES_H */ ++ ++#include "deflate.h" ++ ++#ifdef DEBUG ++# include ++#endif ++ ++/* =========================================================================== ++ * Constants ++ */ ++ ++#define MAX_BL_BITS 7 ++/* Bit length codes must not exceed MAX_BL_BITS bits */ ++ ++#define END_BLOCK 256 ++/* end of block literal code */ ++ ++#define REP_3_6 16 ++/* repeat previous bit length 3-6 times (2 bits of repeat count) */ ++ ++#define REPZ_3_10 17 ++/* repeat a zero length 3-10 times (3 bits of repeat count) */ ++ ++#define REPZ_11_138 18 ++/* repeat a zero length 11-138 times (7 bits of repeat count) */ ++ ++local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ ++ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; ++ ++local const int extra_dbits[D_CODES] /* extra bits for each distance code */ ++ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; ++ ++local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ ++ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; ++ ++local const uch bl_order[BL_CODES] ++ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; ++/* The lengths of the bit length codes are sent in order of decreasing ++ * probability, to avoid transmitting the lengths for unused bit length codes. ++ */ ++ ++#define Buf_size (8 * 2*sizeof(char)) ++/* Number of bits used within bi_buf. (bi_buf might be implemented on ++ * more than 16 bits on some systems.) ++ */ ++ ++/* =========================================================================== ++ * Local data. These are initialized only once. ++ */ ++ ++#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ ++ ++#if defined(GEN_TREES_H) || !defined(STDC) ++/* non ANSI compilers may not accept trees.h */ ++ ++local ct_data static_ltree[L_CODES+2]; ++/* The static literal tree. Since the bit lengths are imposed, there is no ++ * need for the L_CODES extra codes used during heap construction. However ++ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init ++ * below). ++ */ ++ ++local ct_data static_dtree[D_CODES]; ++/* The static distance tree. (Actually a trivial tree since all codes use ++ * 5 bits.) ++ */ ++ ++uch _dist_code[DIST_CODE_LEN]; ++/* Distance codes. The first 256 values correspond to the distances ++ * 3 .. 258, the last 256 values correspond to the top 8 bits of ++ * the 15 bit distances. ++ */ ++ ++uch _length_code[MAX_MATCH-MIN_MATCH+1]; ++/* length code for each normalized match length (0 == MIN_MATCH) */ ++ ++local int base_length[LENGTH_CODES]; ++/* First normalized length for each code (0 = MIN_MATCH) */ ++ ++local int base_dist[D_CODES]; ++/* First normalized distance for each code (0 = distance of 1) */ ++ ++#else ++# include "trees.h" ++#endif /* GEN_TREES_H */ ++ ++struct static_tree_desc_s { ++ const ct_data *static_tree; /* static tree or NULL */ ++ const intf *extra_bits; /* extra bits for each code or NULL */ ++ int extra_base; /* base index for extra_bits */ ++ int elems; /* max number of elements in the tree */ ++ int max_length; /* max bit length for the codes */ ++}; ++ ++local static_tree_desc static_l_desc = ++{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; ++ ++local static_tree_desc static_d_desc = ++{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; ++ ++local static_tree_desc static_bl_desc = ++{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; ++ ++/* =========================================================================== ++ * Local (static) routines in this file. ++ */ ++ ++local void tr_static_init OF((void)); ++local void init_block OF((deflate_state *s)); ++local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); ++local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); ++local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); ++local void build_tree OF((deflate_state *s, tree_desc *desc)); ++local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); ++local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); ++local int build_bl_tree OF((deflate_state *s)); ++local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, ++ int blcodes)); ++local void compress_block OF((deflate_state *s, const ct_data *ltree, ++ const ct_data *dtree)); ++local void set_data_type OF((deflate_state *s)); ++local unsigned bi_reverse OF((unsigned value, int length)); ++local void bi_windup OF((deflate_state *s)); ++local void bi_flush OF((deflate_state *s)); ++local void copy_block OF((deflate_state *s, charf *buf, unsigned len, ++ int header)); ++ ++#ifdef GEN_TREES_H ++local void gen_trees_header OF((void)); ++#endif ++ ++#ifndef DEBUG ++# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) ++ /* Send a code of the given tree. c and tree must not have side effects */ ++ ++#else /* DEBUG */ ++# define send_code(s, c, tree) \ ++ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ ++ send_bits(s, tree[c].Code, tree[c].Len); } ++#endif ++ ++/* =========================================================================== ++ * Output a short LSB first on the stream. ++ * IN assertion: there is enough room in pendingBuf. ++ */ ++#define put_short(s, w) { \ ++ put_byte(s, (uch)((w) & 0xff)); \ ++ put_byte(s, (uch)((ush)(w) >> 8)); \ ++} ++ ++/* =========================================================================== ++ * Send a value on a given number of bits. ++ * IN assertion: length <= 16 and value fits in length bits. ++ */ ++#ifdef DEBUG ++local void send_bits OF((deflate_state *s, int value, int length)); ++ ++local void send_bits(s, value, length) ++ deflate_state *s; ++ int value; /* value to send */ ++ int length; /* number of bits */ ++{ ++ Tracevv((stderr," l %2d v %4x ", length, value)); ++ Assert(length > 0 && length <= 15, "invalid length"); ++ s->bits_sent += (ulg)length; ++ ++ /* If not enough room in bi_buf, use (valid) bits from bi_buf and ++ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) ++ * unused bits in value. ++ */ ++ if (s->bi_valid > (int)Buf_size - length) { ++ s->bi_buf |= (value << s->bi_valid); ++ put_short(s, s->bi_buf); ++ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); ++ s->bi_valid += length - Buf_size; ++ } else { ++ s->bi_buf |= value << s->bi_valid; ++ s->bi_valid += length; ++ } ++} ++#else /* !DEBUG */ ++ ++#define send_bits(s, value, length) \ ++{ int len = length;\ ++ if (s->bi_valid > (int)Buf_size - len) {\ ++ int val = value;\ ++ s->bi_buf |= (val << s->bi_valid);\ ++ put_short(s, s->bi_buf);\ ++ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ ++ s->bi_valid += len - Buf_size;\ ++ } else {\ ++ s->bi_buf |= (value) << s->bi_valid;\ ++ s->bi_valid += len;\ ++ }\ ++} ++#endif /* DEBUG */ ++ ++ ++#define MAX(a,b) (a >= b ? a : b) ++/* the arguments must not have side effects */ ++ ++/* =========================================================================== ++ * Initialize the various 'constant' tables. ++ */ ++local void tr_static_init() ++{ ++#if defined(GEN_TREES_H) || !defined(STDC) ++ static int static_init_done = 0; ++ int n; /* iterates over tree elements */ ++ int bits; /* bit counter */ ++ int length; /* length value */ ++ int code; /* code value */ ++ int dist; /* distance index */ ++ ush bl_count[MAX_BITS+1]; ++ /* number of codes at each bit length for an optimal tree */ ++ ++ if (static_init_done) return; ++ ++ /* For some embedded targets, global variables are not initialized: */ ++ static_l_desc.static_tree = static_ltree; ++ static_l_desc.extra_bits = extra_lbits; ++ static_d_desc.static_tree = static_dtree; ++ static_d_desc.extra_bits = extra_dbits; ++ static_bl_desc.extra_bits = extra_blbits; ++ ++ /* Initialize the mapping length (0..255) -> length code (0..28) */ ++ length = 0; ++ for (code = 0; code < LENGTH_CODES-1; code++) { ++ base_length[code] = length; ++ for (n = 0; n < (1< dist code (0..29) */ ++ dist = 0; ++ for (code = 0 ; code < 16; code++) { ++ base_dist[code] = dist; ++ for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ ++ for ( ; code < D_CODES; code++) { ++ base_dist[code] = dist << 7; ++ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { ++ _dist_code[256 + dist++] = (uch)code; ++ } ++ } ++ Assert (dist == 256, "tr_static_init: 256+dist != 512"); ++ ++ /* Construct the codes of the static literal tree */ ++ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; ++ n = 0; ++ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; ++ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; ++ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; ++ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; ++ /* Codes 286 and 287 do not exist, but we must include them in the ++ * tree construction to get a canonical Huffman tree (longest code ++ * all ones) ++ */ ++ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); ++ ++ /* The static distance tree is trivial: */ ++ for (n = 0; n < D_CODES; n++) { ++ static_dtree[n].Len = 5; ++ static_dtree[n].Code = bi_reverse((unsigned)n, 5); ++ } ++ static_init_done = 1; ++ ++# ifdef GEN_TREES_H ++ gen_trees_header(); ++# endif ++#endif /* defined(GEN_TREES_H) || !defined(STDC) */ ++} ++ ++/* =========================================================================== ++ * Genererate the file trees.h describing the static trees. ++ */ ++#ifdef GEN_TREES_H ++# ifndef DEBUG ++# include ++# endif ++ ++# define SEPARATOR(i, last, width) \ ++ ((i) == (last)? "\n};\n\n" : \ ++ ((i) % (width) == (width)-1 ? ",\n" : ", ")) ++ ++void gen_trees_header() ++{ ++ FILE *header = fopen("trees.h", "w"); ++ int i; ++ ++ Assert (header != NULL, "Can't open trees.h"); ++ fprintf(header, ++ "/* header created automatically with -DGEN_TREES_H */\n\n"); ++ ++ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); ++ for (i = 0; i < L_CODES+2; i++) { ++ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, ++ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); ++ } ++ ++ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); ++ for (i = 0; i < D_CODES; i++) { ++ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, ++ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); ++ } ++ ++ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); ++ for (i = 0; i < DIST_CODE_LEN; i++) { ++ fprintf(header, "%2u%s", _dist_code[i], ++ SEPARATOR(i, DIST_CODE_LEN-1, 20)); ++ } ++ ++ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); ++ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { ++ fprintf(header, "%2u%s", _length_code[i], ++ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); ++ } ++ ++ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); ++ for (i = 0; i < LENGTH_CODES; i++) { ++ fprintf(header, "%1u%s", base_length[i], ++ SEPARATOR(i, LENGTH_CODES-1, 20)); ++ } ++ ++ fprintf(header, "local const int base_dist[D_CODES] = {\n"); ++ for (i = 0; i < D_CODES; i++) { ++ fprintf(header, "%5u%s", base_dist[i], ++ SEPARATOR(i, D_CODES-1, 10)); ++ } ++ ++ fclose(header); ++} ++#endif /* GEN_TREES_H */ ++ ++/* =========================================================================== ++ * Initialize the tree data structures for a new zlib stream. ++ */ ++void _tr_init(s) ++ deflate_state *s; ++{ ++ tr_static_init(); ++ ++ s->l_desc.dyn_tree = s->dyn_ltree; ++ s->l_desc.stat_desc = &static_l_desc; ++ ++ s->d_desc.dyn_tree = s->dyn_dtree; ++ s->d_desc.stat_desc = &static_d_desc; ++ ++ s->bl_desc.dyn_tree = s->bl_tree; ++ s->bl_desc.stat_desc = &static_bl_desc; ++ ++ s->bi_buf = 0; ++ s->bi_valid = 0; ++ s->last_eob_len = 8; /* enough lookahead for inflate */ ++#ifdef DEBUG ++ s->compressed_len = 0L; ++ s->bits_sent = 0L; ++#endif ++ ++ /* Initialize the first block of the first file: */ ++ init_block(s); ++} ++ ++/* =========================================================================== ++ * Initialize a new block. ++ */ ++local void init_block(s) ++ deflate_state *s; ++{ ++ int n; /* iterates over tree elements */ ++ ++ /* Initialize the trees. */ ++ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; ++ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; ++ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; ++ ++ s->dyn_ltree[END_BLOCK].Freq = 1; ++ s->opt_len = s->static_len = 0L; ++ s->last_lit = s->matches = 0; ++} ++ ++#define SMALLEST 1 ++/* Index within the heap array of least frequent node in the Huffman tree */ ++ ++ ++/* =========================================================================== ++ * Remove the smallest element from the heap and recreate the heap with ++ * one less element. Updates heap and heap_len. ++ */ ++#define pqremove(s, tree, top) \ ++{\ ++ top = s->heap[SMALLEST]; \ ++ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ ++ pqdownheap(s, tree, SMALLEST); \ ++} ++ ++/* =========================================================================== ++ * Compares to subtrees, using the tree depth as tie breaker when ++ * the subtrees have equal frequency. This minimizes the worst case length. ++ */ ++#define smaller(tree, n, m, depth) \ ++ (tree[n].Freq < tree[m].Freq || \ ++ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) ++ ++/* =========================================================================== ++ * Restore the heap property by moving down the tree starting at node k, ++ * exchanging a node with the smallest of its two sons if necessary, stopping ++ * when the heap property is re-established (each father smaller than its ++ * two sons). ++ */ ++local void pqdownheap(s, tree, k) ++ deflate_state *s; ++ ct_data *tree; /* the tree to restore */ ++ int k; /* node to move down */ ++{ ++ int v = s->heap[k]; ++ int j = k << 1; /* left son of k */ ++ while (j <= s->heap_len) { ++ /* Set j to the smallest of the two sons: */ ++ if (j < s->heap_len && ++ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { ++ j++; ++ } ++ /* Exit if v is smaller than both sons */ ++ if (smaller(tree, v, s->heap[j], s->depth)) break; ++ ++ /* Exchange v with the smallest son */ ++ s->heap[k] = s->heap[j]; k = j; ++ ++ /* And continue down the tree, setting j to the left son of k */ ++ j <<= 1; ++ } ++ s->heap[k] = v; ++} ++ ++/* =========================================================================== ++ * Compute the optimal bit lengths for a tree and update the total bit length ++ * for the current block. ++ * IN assertion: the fields freq and dad are set, heap[heap_max] and ++ * above are the tree nodes sorted by increasing frequency. ++ * OUT assertions: the field len is set to the optimal bit length, the ++ * array bl_count contains the frequencies for each bit length. ++ * The length opt_len is updated; static_len is also updated if stree is ++ * not null. ++ */ ++local void gen_bitlen(s, desc) ++ deflate_state *s; ++ tree_desc *desc; /* the tree descriptor */ ++{ ++ ct_data *tree = desc->dyn_tree; ++ int max_code = desc->max_code; ++ const ct_data *stree = desc->stat_desc->static_tree; ++ const intf *extra = desc->stat_desc->extra_bits; ++ int base = desc->stat_desc->extra_base; ++ int max_length = desc->stat_desc->max_length; ++ int h; /* heap index */ ++ int n, m; /* iterate over the tree elements */ ++ int bits; /* bit length */ ++ int xbits; /* extra bits */ ++ ush f; /* frequency */ ++ int overflow = 0; /* number of elements with bit length too large */ ++ ++ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; ++ ++ /* In a first pass, compute the optimal bit lengths (which may ++ * overflow in the case of the bit length tree). ++ */ ++ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ ++ ++ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { ++ n = s->heap[h]; ++ bits = tree[tree[n].Dad].Len + 1; ++ if (bits > max_length) bits = max_length, overflow++; ++ tree[n].Len = (ush)bits; ++ /* We overwrite tree[n].Dad which is no longer needed */ ++ ++ if (n > max_code) continue; /* not a leaf node */ ++ ++ s->bl_count[bits]++; ++ xbits = 0; ++ if (n >= base) xbits = extra[n-base]; ++ f = tree[n].Freq; ++ s->opt_len += (ulg)f * (bits + xbits); ++ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); ++ } ++ if (overflow == 0) return; ++ ++ Trace((stderr,"\nbit length overflow\n")); ++ /* This happens for example on obj2 and pic of the Calgary corpus */ ++ ++ /* Find the first bit length which could increase: */ ++ do { ++ bits = max_length-1; ++ while (s->bl_count[bits] == 0) bits--; ++ s->bl_count[bits]--; /* move one leaf down the tree */ ++ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ ++ s->bl_count[max_length]--; ++ /* The brother of the overflow item also moves one step up, ++ * but this does not affect bl_count[max_length] ++ */ ++ overflow -= 2; ++ } while (overflow > 0); ++ ++ /* Now recompute all bit lengths, scanning in increasing frequency. ++ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all ++ * lengths instead of fixing only the wrong ones. This idea is taken ++ * from 'ar' written by Haruhiko Okumura.) ++ */ ++ for (bits = max_length; bits != 0; bits--) { ++ n = s->bl_count[bits]; ++ while (n != 0) { ++ m = s->heap[--h]; ++ if (m > max_code) continue; ++ if (tree[m].Len != (unsigned) bits) { ++ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); ++ s->opt_len += ((long)bits - (long)tree[m].Len) ++ *(long)tree[m].Freq; ++ tree[m].Len = (ush)bits; ++ } ++ n--; ++ } ++ } ++} ++ ++/* =========================================================================== ++ * Generate the codes for a given tree and bit counts (which need not be ++ * optimal). ++ * IN assertion: the array bl_count contains the bit length statistics for ++ * the given tree and the field len is set for all tree elements. ++ * OUT assertion: the field code is set for all tree elements of non ++ * zero code length. ++ */ ++local void gen_codes (tree, max_code, bl_count) ++ ct_data *tree; /* the tree to decorate */ ++ int max_code; /* largest code with non zero frequency */ ++ ushf *bl_count; /* number of codes at each bit length */ ++{ ++ ush next_code[MAX_BITS+1]; /* next code value for each bit length */ ++ ush code = 0; /* running code value */ ++ int bits; /* bit index */ ++ int n; /* code index */ ++ ++ /* The distribution counts are first used to generate the code values ++ * without bit reversal. ++ */ ++ for (bits = 1; bits <= MAX_BITS; bits++) { ++ next_code[bits] = code = (code + bl_count[bits-1]) << 1; ++ } ++ /* Check that the bit counts in bl_count are consistent. The last code ++ * must be all ones. ++ */ ++ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; ++ const ct_data *stree = desc->stat_desc->static_tree; ++ int elems = desc->stat_desc->elems; ++ int n, m; /* iterate over heap elements */ ++ int max_code = -1; /* largest code with non zero frequency */ ++ int node; /* new node being created */ ++ ++ /* Construct the initial heap, with least frequent element in ++ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. ++ * heap[0] is not used. ++ */ ++ s->heap_len = 0, s->heap_max = HEAP_SIZE; ++ ++ for (n = 0; n < elems; n++) { ++ if (tree[n].Freq != 0) { ++ s->heap[++(s->heap_len)] = max_code = n; ++ s->depth[n] = 0; ++ } else { ++ tree[n].Len = 0; ++ } ++ } ++ ++ /* The pkzip format requires that at least one distance code exists, ++ * and that at least one bit should be sent even if there is only one ++ * possible code. So to avoid special checks later on we force at least ++ * two codes of non zero frequency. ++ */ ++ while (s->heap_len < 2) { ++ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); ++ tree[node].Freq = 1; ++ s->depth[node] = 0; ++ s->opt_len--; if (stree) s->static_len -= stree[node].Len; ++ /* node is 0 or 1 so it does not have extra bits */ ++ } ++ desc->max_code = max_code; ++ ++ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, ++ * establish sub-heaps of increasing lengths: ++ */ ++ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); ++ ++ /* Construct the Huffman tree by repeatedly combining the least two ++ * frequent nodes. ++ */ ++ node = elems; /* next internal node of the tree */ ++ do { ++ pqremove(s, tree, n); /* n = node of least frequency */ ++ m = s->heap[SMALLEST]; /* m = node of next least frequency */ ++ ++ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ ++ s->heap[--(s->heap_max)] = m; ++ ++ /* Create a new node father of n and m */ ++ tree[node].Freq = tree[n].Freq + tree[m].Freq; ++ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); ++ tree[n].Dad = tree[m].Dad = (ush)node; ++#ifdef DUMP_BL_TREE ++ if (tree == s->bl_tree) { ++ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", ++ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); ++ } ++#endif ++ /* and insert the new node in the heap */ ++ s->heap[SMALLEST] = node++; ++ pqdownheap(s, tree, SMALLEST); ++ ++ } while (s->heap_len >= 2); ++ ++ s->heap[--(s->heap_max)] = s->heap[SMALLEST]; ++ ++ /* At this point, the fields freq and dad are set. We can now ++ * generate the bit lengths. ++ */ ++ gen_bitlen(s, (tree_desc *)desc); ++ ++ /* The field len is now set, we can generate the bit codes */ ++ gen_codes ((ct_data *)tree, max_code, s->bl_count); ++} ++ ++/* =========================================================================== ++ * Scan a literal or distance tree to determine the frequencies of the codes ++ * in the bit length tree. ++ */ ++local void scan_tree (s, tree, max_code) ++ deflate_state *s; ++ ct_data *tree; /* the tree to be scanned */ ++ int max_code; /* and its largest code of non zero frequency */ ++{ ++ int n; /* iterates over all tree elements */ ++ int prevlen = -1; /* last emitted length */ ++ int curlen; /* length of current code */ ++ int nextlen = tree[0].Len; /* length of next code */ ++ int count = 0; /* repeat count of the current code */ ++ int max_count = 7; /* max repeat count */ ++ int min_count = 4; /* min repeat count */ ++ ++ if (nextlen == 0) max_count = 138, min_count = 3; ++ tree[max_code+1].Len = (ush)0xffff; /* guard */ ++ ++ for (n = 0; n <= max_code; n++) { ++ curlen = nextlen; nextlen = tree[n+1].Len; ++ if (++count < max_count && curlen == nextlen) { ++ continue; ++ } else if (count < min_count) { ++ s->bl_tree[curlen].Freq += count; ++ } else if (curlen != 0) { ++ if (curlen != prevlen) s->bl_tree[curlen].Freq++; ++ s->bl_tree[REP_3_6].Freq++; ++ } else if (count <= 10) { ++ s->bl_tree[REPZ_3_10].Freq++; ++ } else { ++ s->bl_tree[REPZ_11_138].Freq++; ++ } ++ count = 0; prevlen = curlen; ++ if (nextlen == 0) { ++ max_count = 138, min_count = 3; ++ } else if (curlen == nextlen) { ++ max_count = 6, min_count = 3; ++ } else { ++ max_count = 7, min_count = 4; ++ } ++ } ++} ++ ++/* =========================================================================== ++ * Send a literal or distance tree in compressed form, using the codes in ++ * bl_tree. ++ */ ++local void send_tree (s, tree, max_code) ++ deflate_state *s; ++ ct_data *tree; /* the tree to be scanned */ ++ int max_code; /* and its largest code of non zero frequency */ ++{ ++ int n; /* iterates over all tree elements */ ++ int prevlen = -1; /* last emitted length */ ++ int curlen; /* length of current code */ ++ int nextlen = tree[0].Len; /* length of next code */ ++ int count = 0; /* repeat count of the current code */ ++ int max_count = 7; /* max repeat count */ ++ int min_count = 4; /* min repeat count */ ++ ++ /* tree[max_code+1].Len = -1; */ /* guard already set */ ++ if (nextlen == 0) max_count = 138, min_count = 3; ++ ++ for (n = 0; n <= max_code; n++) { ++ curlen = nextlen; nextlen = tree[n+1].Len; ++ if (++count < max_count && curlen == nextlen) { ++ continue; ++ } else if (count < min_count) { ++ do { send_code(s, curlen, s->bl_tree); } while (--count != 0); ++ ++ } else if (curlen != 0) { ++ if (curlen != prevlen) { ++ send_code(s, curlen, s->bl_tree); count--; ++ } ++ Assert(count >= 3 && count <= 6, " 3_6?"); ++ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); ++ ++ } else if (count <= 10) { ++ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); ++ ++ } else { ++ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); ++ } ++ count = 0; prevlen = curlen; ++ if (nextlen == 0) { ++ max_count = 138, min_count = 3; ++ } else if (curlen == nextlen) { ++ max_count = 6, min_count = 3; ++ } else { ++ max_count = 7, min_count = 4; ++ } ++ } ++} ++ ++/* =========================================================================== ++ * Construct the Huffman tree for the bit lengths and return the index in ++ * bl_order of the last bit length code to send. ++ */ ++local int build_bl_tree(s) ++ deflate_state *s; ++{ ++ int max_blindex; /* index of last bit length code of non zero freq */ ++ ++ /* Determine the bit length frequencies for literal and distance trees */ ++ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); ++ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); ++ ++ /* Build the bit length tree: */ ++ build_tree(s, (tree_desc *)(&(s->bl_desc))); ++ /* opt_len now includes the length of the tree representations, except ++ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. ++ */ ++ ++ /* Determine the number of bit length codes to send. The pkzip format ++ * requires that at least 4 bit length codes be sent. (appnote.txt says ++ * 3 but the actual value used is 4.) ++ */ ++ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { ++ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; ++ } ++ /* Update opt_len to include the bit length tree and counts */ ++ s->opt_len += 3*(max_blindex+1) + 5+5+4; ++ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", ++ s->opt_len, s->static_len)); ++ ++ return max_blindex; ++} ++ ++/* =========================================================================== ++ * Send the header for a block using dynamic Huffman trees: the counts, the ++ * lengths of the bit length codes, the literal tree and the distance tree. ++ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. ++ */ ++local void send_all_trees(s, lcodes, dcodes, blcodes) ++ deflate_state *s; ++ int lcodes, dcodes, blcodes; /* number of codes for each tree */ ++{ ++ int rank; /* index in bl_order */ ++ ++ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); ++ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, ++ "too many codes"); ++ Tracev((stderr, "\nbl counts: ")); ++ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ ++ send_bits(s, dcodes-1, 5); ++ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ ++ for (rank = 0; rank < blcodes; rank++) { ++ Tracev((stderr, "\nbl code %2d ", bl_order[rank])); ++ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); ++ } ++ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); ++ ++ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ ++ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); ++ ++ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ ++ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); ++} ++ ++/* =========================================================================== ++ * Send a stored block ++ */ ++void _tr_stored_block(s, buf, stored_len, eof) ++ deflate_state *s; ++ charf *buf; /* input block */ ++ ulg stored_len; /* length of input block */ ++ int eof; /* true if this is the last block for a file */ ++{ ++ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ ++#ifdef DEBUG ++ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; ++ s->compressed_len += (stored_len + 4) << 3; ++#endif ++ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ ++} ++ ++/* =========================================================================== ++ * Send one empty static block to give enough lookahead for inflate. ++ * This takes 10 bits, of which 7 may remain in the bit buffer. ++ * The current inflate code requires 9 bits of lookahead. If the ++ * last two codes for the previous block (real code plus EOB) were coded ++ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode ++ * the last real code. In this case we send two empty static blocks instead ++ * of one. (There are no problems if the previous block is stored or fixed.) ++ * To simplify the code, we assume the worst case of last real code encoded ++ * on one bit only. ++ */ ++void _tr_align(s) ++ deflate_state *s; ++{ ++ send_bits(s, STATIC_TREES<<1, 3); ++ send_code(s, END_BLOCK, static_ltree); ++#ifdef DEBUG ++ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ ++#endif ++ bi_flush(s); ++ /* Of the 10 bits for the empty block, we have already sent ++ * (10 - bi_valid) bits. The lookahead for the last real code (before ++ * the EOB of the previous block) was thus at least one plus the length ++ * of the EOB plus what we have just sent of the empty static block. ++ */ ++ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { ++ send_bits(s, STATIC_TREES<<1, 3); ++ send_code(s, END_BLOCK, static_ltree); ++#ifdef DEBUG ++ s->compressed_len += 10L; ++#endif ++ bi_flush(s); ++ } ++ s->last_eob_len = 7; ++} ++ ++/* =========================================================================== ++ * Determine the best encoding for the current block: dynamic trees, static ++ * trees or store, and output the encoded block to the zip file. ++ */ ++void _tr_flush_block(s, buf, stored_len, eof) ++ deflate_state *s; ++ charf *buf; /* input block, or NULL if too old */ ++ ulg stored_len; /* length of input block */ ++ int eof; /* true if this is the last block for a file */ ++{ ++ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ ++ int max_blindex = 0; /* index of last bit length code of non zero freq */ ++ ++ /* Build the Huffman trees unless a stored block is forced */ ++ if (s->level > 0) { ++ ++ /* Check if the file is ascii or binary */ ++ if (s->data_type == Z_UNKNOWN) set_data_type(s); ++ ++ /* Construct the literal and distance trees */ ++ build_tree(s, (tree_desc *)(&(s->l_desc))); ++ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, ++ s->static_len)); ++ ++ build_tree(s, (tree_desc *)(&(s->d_desc))); ++ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, ++ s->static_len)); ++ /* At this point, opt_len and static_len are the total bit lengths of ++ * the compressed block data, excluding the tree representations. ++ */ ++ ++ /* Build the bit length tree for the above two trees, and get the index ++ * in bl_order of the last bit length code to send. ++ */ ++ max_blindex = build_bl_tree(s); ++ ++ /* Determine the best encoding. Compute first the block length in bytes*/ ++ opt_lenb = (s->opt_len+3+7)>>3; ++ static_lenb = (s->static_len+3+7)>>3; ++ ++ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", ++ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, ++ s->last_lit)); ++ ++ if (static_lenb <= opt_lenb) opt_lenb = static_lenb; ++ ++ } else { ++ Assert(buf != (char*)0, "lost buf"); ++ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ ++ } ++ ++#ifdef FORCE_STORED ++ if (buf != (char*)0) { /* force stored block */ ++#else ++ if (stored_len+4 <= opt_lenb && buf != (char*)0) { ++ /* 4: two words for the lengths */ ++#endif ++ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. ++ * Otherwise we can't have processed more than WSIZE input bytes since ++ * the last block flush, because compression would have been ++ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to ++ * transform a block into a stored block. ++ */ ++ _tr_stored_block(s, buf, stored_len, eof); ++ ++#ifdef FORCE_STATIC ++ } else if (static_lenb >= 0) { /* force static trees */ ++#else ++ } else if (static_lenb == opt_lenb) { ++#endif ++ send_bits(s, (STATIC_TREES<<1)+eof, 3); ++ compress_block(s, static_ltree, static_dtree); ++#ifdef DEBUG ++ s->compressed_len += 3 + s->static_len; ++#endif ++ } else { ++ send_bits(s, (DYN_TREES<<1)+eof, 3); ++ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, ++ max_blindex+1); ++ compress_block(s, s->dyn_ltree, s->dyn_dtree); ++#ifdef DEBUG ++ s->compressed_len += 3 + s->opt_len; ++#endif ++ } ++ Assert (s->compressed_len == s->bits_sent, "bad compressed size"); ++ /* The above check is made mod 2^32, for files larger than 512 MB ++ * and uLong implemented on 32 bits. ++ */ ++ init_block(s); ++ ++ if (eof) { ++ bi_windup(s); ++#ifdef DEBUG ++ s->compressed_len += 7; /* align on byte boundary */ ++#endif ++ } ++ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, ++ s->compressed_len-7*eof)); ++} ++ ++/* =========================================================================== ++ * Save the match info and tally the frequency counts. Return true if ++ * the current block must be flushed. ++ */ ++int _tr_tally (s, dist, lc) ++ deflate_state *s; ++ unsigned dist; /* distance of matched string */ ++ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ ++{ ++ s->d_buf[s->last_lit] = (ush)dist; ++ s->l_buf[s->last_lit++] = (uch)lc; ++ if (dist == 0) { ++ /* lc is the unmatched char */ ++ s->dyn_ltree[lc].Freq++; ++ } else { ++ s->matches++; ++ /* Here, lc is the match length - MIN_MATCH */ ++ dist--; /* dist = match distance - 1 */ ++ Assert((ush)dist < (ush)MAX_DIST(s) && ++ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && ++ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); ++ ++ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; ++ s->dyn_dtree[d_code(dist)].Freq++; ++ } ++ ++#ifdef TRUNCATE_BLOCK ++ /* Try to guess if it is profitable to stop the current block here */ ++ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { ++ /* Compute an upper bound for the compressed length */ ++ ulg out_length = (ulg)s->last_lit*8L; ++ ulg in_length = (ulg)((long)s->strstart - s->block_start); ++ int dcode; ++ for (dcode = 0; dcode < D_CODES; dcode++) { ++ out_length += (ulg)s->dyn_dtree[dcode].Freq * ++ (5L+extra_dbits[dcode]); ++ } ++ out_length >>= 3; ++ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", ++ s->last_lit, in_length, out_length, ++ 100L - out_length*100L/in_length)); ++ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; ++ } ++#endif ++ return (s->last_lit == s->lit_bufsize-1); ++ /* We avoid equality with lit_bufsize because of wraparound at 64K ++ * on 16 bit machines and because stored blocks are restricted to ++ * 64K-1 bytes. ++ */ ++} ++ ++/* =========================================================================== ++ * Send the block data compressed using the given Huffman trees ++ */ ++local void compress_block(s, ltree, dtree) ++ deflate_state *s; ++ const ct_data *ltree; /* literal tree */ ++ const ct_data *dtree; /* distance tree */ ++{ ++ unsigned dist; /* distance of matched string */ ++ int lc; /* match length or unmatched char (if dist == 0) */ ++ unsigned lx = 0; /* running index in l_buf */ ++ unsigned code; /* the code to send */ ++ int extra; /* number of extra bits to send */ ++ ++ if (s->last_lit != 0) do { ++ dist = s->d_buf[lx]; ++ lc = s->l_buf[lx++]; ++ if (dist == 0) { ++ send_code(s, lc, ltree); /* send a literal byte */ ++ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); ++ } else { ++ /* Here, lc is the match length - MIN_MATCH */ ++ code = _length_code[lc]; ++ send_code(s, code+LITERALS+1, ltree); /* send the length code */ ++ extra = extra_lbits[code]; ++ if (extra != 0) { ++ lc -= base_length[code]; ++ send_bits(s, lc, extra); /* send the extra length bits */ ++ } ++ dist--; /* dist is now the match distance - 1 */ ++ code = d_code(dist); ++ Assert (code < D_CODES, "bad d_code"); ++ ++ send_code(s, code, dtree); /* send the distance code */ ++ extra = extra_dbits[code]; ++ if (extra != 0) { ++ dist -= base_dist[code]; ++ send_bits(s, dist, extra); /* send the extra distance bits */ ++ } ++ } /* literal or match pair ? */ ++ ++ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ ++ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); ++ ++ } while (lx < s->last_lit); ++ ++ send_code(s, END_BLOCK, ltree); ++ s->last_eob_len = ltree[END_BLOCK].Len; ++} ++ ++/* =========================================================================== ++ * Set the data type to ASCII or BINARY, using a crude approximation: ++ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. ++ * IN assertion: the fields freq of dyn_ltree are set and the total of all ++ * frequencies does not exceed 64K (to fit in an int on 16 bit machines). ++ */ ++local void set_data_type(s) ++ deflate_state *s; ++{ ++ int n = 0; ++ unsigned ascii_freq = 0; ++ unsigned bin_freq = 0; ++ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; ++ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; ++ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; ++ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); ++} ++ ++/* =========================================================================== ++ * Reverse the first len bits of a code, using straightforward code (a faster ++ * method would use a table) ++ * IN assertion: 1 <= len <= 15 ++ */ ++local unsigned bi_reverse(code, len) ++ unsigned code; /* the value to invert */ ++ int len; /* its bit length */ ++{ ++ register unsigned res = 0; ++ do { ++ res |= code & 1; ++ code >>= 1, res <<= 1; ++ } while (--len > 0); ++ return res >> 1; ++} ++ ++/* =========================================================================== ++ * Flush the bit buffer, keeping at most 7 bits in it. ++ */ ++local void bi_flush(s) ++ deflate_state *s; ++{ ++ if (s->bi_valid == 16) { ++ put_short(s, s->bi_buf); ++ s->bi_buf = 0; ++ s->bi_valid = 0; ++ } else if (s->bi_valid >= 8) { ++ put_byte(s, (Byte)s->bi_buf); ++ s->bi_buf >>= 8; ++ s->bi_valid -= 8; ++ } ++} ++ ++/* =========================================================================== ++ * Flush the bit buffer and align the output on a byte boundary ++ */ ++local void bi_windup(s) ++ deflate_state *s; ++{ ++ if (s->bi_valid > 8) { ++ put_short(s, s->bi_buf); ++ } else if (s->bi_valid > 0) { ++ put_byte(s, (Byte)s->bi_buf); ++ } ++ s->bi_buf = 0; ++ s->bi_valid = 0; ++#ifdef DEBUG ++ s->bits_sent = (s->bits_sent+7) & ~7; ++#endif ++} ++ ++/* =========================================================================== ++ * Copy a stored block, storing first the length and its ++ * one's complement if requested. ++ */ ++local void copy_block(s, buf, len, header) ++ deflate_state *s; ++ charf *buf; /* the input data */ ++ unsigned len; /* its length */ ++ int header; /* true if block header must be written */ ++{ ++ bi_windup(s); /* align on byte boundary */ ++ s->last_eob_len = 8; /* enough lookahead for inflate */ ++ ++ if (header) { ++ put_short(s, (ush)len); ++ put_short(s, (ush)~len); ++#ifdef DEBUG ++ s->bits_sent += 2*16; ++#endif ++ } ++#ifdef DEBUG ++ s->bits_sent += (ulg)len<<3; ++#endif ++ while (len--) { ++ put_byte(s, *buf++); ++ } ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/trees.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,128 @@ ++/* header created automatically with -DGEN_TREES_H */ ++ ++local const ct_data static_ltree[L_CODES+2] = { ++{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, ++{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, ++{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, ++{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, ++{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, ++{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, ++{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, ++{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, ++{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, ++{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, ++{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, ++{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, ++{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, ++{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, ++{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, ++{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, ++{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, ++{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, ++{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, ++{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, ++{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, ++{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, ++{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, ++{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, ++{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, ++{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, ++{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, ++{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, ++{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, ++{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, ++{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, ++{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, ++{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, ++{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, ++{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, ++{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, ++{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, ++{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, ++{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, ++{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, ++{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, ++{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, ++{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, ++{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, ++{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, ++{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, ++{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, ++{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, ++{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, ++{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, ++{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, ++{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, ++{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, ++{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, ++{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, ++{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, ++{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, ++{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} ++}; ++ ++local const ct_data static_dtree[D_CODES] = { ++{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, ++{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, ++{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, ++{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, ++{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, ++{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} ++}; ++ ++const uch _dist_code[DIST_CODE_LEN] = { ++ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, ++ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, ++10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, ++11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, ++12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, ++13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, ++13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, ++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, ++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, ++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, ++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, ++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, ++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, ++18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, ++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, ++24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, ++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, ++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, ++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, ++28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, ++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, ++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, ++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 ++}; ++ ++const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, ++13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, ++17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, ++19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, ++21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, ++22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, ++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, ++24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, ++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, ++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, ++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, ++26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, ++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 ++}; ++ ++local const int base_length[LENGTH_CODES] = { ++0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, ++64, 80, 96, 112, 128, 160, 192, 224, 0 ++}; ++ ++local const int base_dist[D_CODES] = { ++ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, ++ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, ++ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 ++}; ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/zconf.h Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,309 @@ ++/* zconf.h -- configuration of the zlib compression library ++ * Copyright (C) 1995-2002 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* @(#) $Id: zconf.h,v 1.3 2002/04/24 07:36:45 mcr Exp $ */ ++ ++#ifndef _ZCONF_H ++#define _ZCONF_H ++ ++/* ++ * If you *really* need a unique prefix for all types and library functions, ++ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. ++ */ ++#ifdef IPCOMP_PREFIX ++# define deflateInit_ ipcomp_deflateInit_ ++# define deflate ipcomp_deflate ++# define deflateEnd ipcomp_deflateEnd ++# define inflateInit_ ipcomp_inflateInit_ ++# define inflate ipcomp_inflate ++# define inflateEnd ipcomp_inflateEnd ++# define deflateInit2_ ipcomp_deflateInit2_ ++# define deflateSetDictionary ipcomp_deflateSetDictionary ++# define deflateCopy ipcomp_deflateCopy ++# define deflateReset ipcomp_deflateReset ++# define deflateParams ipcomp_deflateParams ++# define inflateInit2_ ipcomp_inflateInit2_ ++# define inflateSetDictionary ipcomp_inflateSetDictionary ++# define inflateSync ipcomp_inflateSync ++# define inflateSyncPoint ipcomp_inflateSyncPoint ++# define inflateReset ipcomp_inflateReset ++# define compress ipcomp_compress ++# define compress2 ipcomp_compress2 ++# define uncompress ipcomp_uncompress ++# define adler32 ipcomp_adler32 ++# define crc32 ipcomp_crc32 ++# define get_crc_table ipcomp_get_crc_table ++/* SSS: these also need to be prefixed to avoid clash with ppp_deflate and ext2compression */ ++# define inflate_blocks ipcomp_deflate_blocks ++# define inflate_blocks_free ipcomp_deflate_blocks_free ++# define inflate_blocks_new ipcomp_inflate_blocks_new ++# define inflate_blocks_reset ipcomp_inflate_blocks_reset ++# define inflate_blocks_sync_point ipcomp_inflate_blocks_sync_point ++# define inflate_set_dictionary ipcomp_inflate_set_dictionary ++# define inflate_codes ipcomp_inflate_codes ++# define inflate_codes_free ipcomp_inflate_codes_free ++# define inflate_codes_new ipcomp_inflate_codes_new ++# define inflate_fast ipcomp_inflate_fast ++# define inflate_trees_bits ipcomp_inflate_trees_bits ++# define inflate_trees_dynamic ipcomp_inflate_trees_dynamic ++# define inflate_trees_fixed ipcomp_inflate_trees_fixed ++# define inflate_flush ipcomp_inflate_flush ++# define inflate_mask ipcomp_inflate_mask ++# define _dist_code _ipcomp_dist_code ++# define _length_code _ipcomp_length_code ++# define _tr_align _ipcomp_tr_align ++# define _tr_flush_block _ipcomp_tr_flush_block ++# define _tr_init _ipcomp_tr_init ++# define _tr_stored_block _ipcomp_tr_stored_block ++# define _tr_tally _ipcomp_tr_tally ++# define zError ipcomp_zError ++# define z_errmsg ipcomp_z_errmsg ++# define zlibVersion ipcomp_zlibVersion ++# define match_init ipcomp_match_init ++# define longest_match ipcomp_longest_match ++#endif ++ ++#ifdef Z_PREFIX ++# define Byte z_Byte ++# define uInt z_uInt ++# define uLong z_uLong ++# define Bytef z_Bytef ++# define charf z_charf ++# define intf z_intf ++# define uIntf z_uIntf ++# define uLongf z_uLongf ++# define voidpf z_voidpf ++# define voidp z_voidp ++#endif ++ ++#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) ++# define WIN32 ++#endif ++#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) ++# ifndef __32BIT__ ++# define __32BIT__ ++# endif ++#endif ++#if defined(__MSDOS__) && !defined(MSDOS) ++# define MSDOS ++#endif ++ ++/* ++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more ++ * than 64k bytes at a time (needed on systems with 16-bit int). ++ */ ++#if defined(MSDOS) && !defined(__32BIT__) ++# define MAXSEG_64K ++#endif ++#ifdef MSDOS ++# define UNALIGNED_OK ++#endif ++ ++#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) ++# define STDC ++#endif ++#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) ++# ifndef STDC ++# define STDC ++# endif ++#endif ++ ++#ifndef STDC ++# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ ++# define const ++# endif ++#endif ++ ++/* Some Mac compilers merge all .h files incorrectly: */ ++#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) ++# define NO_DUMMY_DECL ++#endif ++ ++/* Old Borland C incorrectly complains about missing returns: */ ++#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) ++# define NEED_DUMMY_RETURN ++#endif ++ ++ ++/* Maximum value for memLevel in deflateInit2 */ ++#ifndef MAX_MEM_LEVEL ++# ifdef MAXSEG_64K ++# define MAX_MEM_LEVEL 8 ++# else ++# define MAX_MEM_LEVEL 9 ++# endif ++#endif ++ ++/* Maximum value for windowBits in deflateInit2 and inflateInit2. ++ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files ++ * created by gzip. (Files created by minigzip can still be extracted by ++ * gzip.) ++ */ ++#ifndef MAX_WBITS ++# define MAX_WBITS 15 /* 32K LZ77 window */ ++#endif ++ ++/* The memory requirements for deflate are (in bytes): ++ (1 << (windowBits+2)) + (1 << (memLevel+9)) ++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) ++ plus a few kilobytes for small objects. For example, if you want to reduce ++ the default memory requirements from 256K to 128K, compile with ++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" ++ Of course this will generally degrade compression (there's no free lunch). ++ ++ The memory requirements for inflate are (in bytes) 1 << windowBits ++ that is, 32K for windowBits=15 (default value) plus a few kilobytes ++ for small objects. ++*/ ++ ++ /* Type declarations */ ++ ++#ifndef OF /* function prototypes */ ++# ifdef STDC ++# define OF(args) args ++# else ++# define OF(args) () ++# endif ++#endif ++ ++/* The following definitions for FAR are needed only for MSDOS mixed ++ * model programming (small or medium model with some far allocations). ++ * This was tested only with MSC; for other MSDOS compilers you may have ++ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, ++ * just define FAR to be empty. ++ */ ++#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) ++ /* MSC small or medium model */ ++# define SMALL_MEDIUM ++# ifdef _MSC_VER ++# define FAR _far ++# else ++# define FAR far ++# endif ++#endif ++#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) ++# ifndef __32BIT__ ++# define SMALL_MEDIUM ++# define FAR _far ++# endif ++#endif ++ ++/* Compile with -DZLIB_DLL for Windows DLL support */ ++#if defined(ZLIB_DLL) ++# if defined(_WINDOWS) || defined(WINDOWS) ++# ifdef FAR ++# undef FAR ++# endif ++# include ++# define ZEXPORT WINAPI ++# ifdef WIN32 ++# define ZEXPORTVA WINAPIV ++# else ++# define ZEXPORTVA FAR _cdecl _export ++# endif ++# endif ++# if defined (__BORLANDC__) ++# if (__BORLANDC__ >= 0x0500) && defined (WIN32) ++# include ++# define ZEXPORT __declspec(dllexport) WINAPI ++# define ZEXPORTRVA __declspec(dllexport) WINAPIV ++# else ++# if defined (_Windows) && defined (__DLL__) ++# define ZEXPORT _export ++# define ZEXPORTVA _export ++# endif ++# endif ++# endif ++#endif ++ ++#if defined (__BEOS__) ++# if defined (ZLIB_DLL) ++# define ZEXTERN extern __declspec(dllexport) ++# else ++# define ZEXTERN extern __declspec(dllimport) ++# endif ++#endif ++ ++#ifndef ZEXPORT ++# define ZEXPORT ++#endif ++#ifndef ZEXPORTVA ++# define ZEXPORTVA ++#endif ++#ifndef ZEXTERN ++# define ZEXTERN extern ++#endif ++ ++#ifndef FAR ++# define FAR ++#endif ++ ++#if !defined(MACOS) && !defined(TARGET_OS_MAC) ++typedef unsigned char Byte; /* 8 bits */ ++#endif ++typedef unsigned int uInt; /* 16 bits or more */ ++typedef unsigned long uLong; /* 32 bits or more */ ++ ++#ifdef SMALL_MEDIUM ++ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ ++# define Bytef Byte FAR ++#else ++ typedef Byte FAR Bytef; ++#endif ++typedef char FAR charf; ++typedef int FAR intf; ++typedef uInt FAR uIntf; ++typedef uLong FAR uLongf; ++ ++#ifdef STDC ++ typedef void FAR *voidpf; ++ typedef void *voidp; ++#else ++ typedef Byte FAR *voidpf; ++ typedef Byte *voidp; ++#endif ++ ++#ifdef HAVE_UNISTD_H ++# include /* for off_t */ ++# include /* for SEEK_* and off_t */ ++# define z_off_t off_t ++#endif ++#ifndef SEEK_SET ++# define SEEK_SET 0 /* Seek from beginning of file. */ ++# define SEEK_CUR 1 /* Seek from current position. */ ++# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ ++#endif ++#ifndef z_off_t ++# define z_off_t long ++#endif ++ ++/* MVS linker does not support external names larger than 8 bytes */ ++#if defined(__MVS__) ++# pragma map(deflateInit_,"DEIN") ++# pragma map(deflateInit2_,"DEIN2") ++# pragma map(deflateEnd,"DEEND") ++# pragma map(inflateInit_,"ININ") ++# pragma map(inflateInit2_,"ININ2") ++# pragma map(inflateEnd,"INEND") ++# pragma map(inflateSync,"INSY") ++# pragma map(inflateSetDictionary,"INSEDI") ++# pragma map(inflate_blocks,"INBL") ++# pragma map(inflate_blocks_new,"INBLNE") ++# pragma map(inflate_blocks_free,"INBLFR") ++# pragma map(inflate_blocks_reset,"INBLRE") ++# pragma map(inflate_codes_free,"INCOFR") ++# pragma map(inflate_codes,"INCO") ++# pragma map(inflate_fast,"INFA") ++# pragma map(inflate_flush,"INFLU") ++# pragma map(inflate_mask,"INMA") ++# pragma map(inflate_set_dictionary,"INSEDI2") ++# pragma map(ipcomp_inflate_copyright,"INCOPY") ++# pragma map(inflate_trees_bits,"INTRBI") ++# pragma map(inflate_trees_dynamic,"INTRDY") ++# pragma map(inflate_trees_fixed,"INTRFI") ++# pragma map(inflate_trees_free,"INTRFR") ++#endif ++ ++#endif /* _ZCONF_H */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/lib/zlib/zutil.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,227 @@ ++/* zutil.c -- target dependent utility functions for the compression library ++ * Copyright (C) 1995-2002 Jean-loup Gailly. ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++ ++/* @(#) $Id: zutil.c,v 1.4 2002/04/24 07:55:32 mcr Exp $ */ ++ ++#include ++ ++#define MY_ZCALLOC ++ ++struct internal_state {int dummy;}; /* for buggy compilers */ ++ ++#ifndef STDC ++extern void exit OF((int)); ++#endif ++ ++const char *z_errmsg[10] = { ++"need dictionary", /* Z_NEED_DICT 2 */ ++"stream end", /* Z_STREAM_END 1 */ ++"", /* Z_OK 0 */ ++"file error", /* Z_ERRNO (-1) */ ++"stream error", /* Z_STREAM_ERROR (-2) */ ++"data error", /* Z_DATA_ERROR (-3) */ ++"insufficient memory", /* Z_MEM_ERROR (-4) */ ++"buffer error", /* Z_BUF_ERROR (-5) */ ++"incompatible version",/* Z_VERSION_ERROR (-6) */ ++""}; ++ ++ ++const char * ZEXPORT zlibVersion() ++{ ++ return ZLIB_VERSION; ++} ++ ++#ifdef DEBUG ++ ++# ifndef verbose ++# define verbose 0 ++# endif ++int z_verbose = verbose; ++ ++void z_error (m) ++ char *m; ++{ ++ fprintf(stderr, "%s\n", m); ++ exit(1); ++} ++#endif ++ ++/* exported to allow conversion of error code to string for compress() and ++ * uncompress() ++ */ ++const char * ZEXPORT zError(err) ++ int err; ++{ ++ return ERR_MSG(err); ++} ++ ++ ++#ifndef HAVE_MEMCPY ++ ++void zmemcpy(dest, source, len) ++ Bytef* dest; ++ const Bytef* source; ++ uInt len; ++{ ++ if (len == 0) return; ++ do { ++ *dest++ = *source++; /* ??? to be unrolled */ ++ } while (--len != 0); ++} ++ ++int zmemcmp(s1, s2, len) ++ const Bytef* s1; ++ const Bytef* s2; ++ uInt len; ++{ ++ uInt j; ++ ++ for (j = 0; j < len; j++) { ++ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; ++ } ++ return 0; ++} ++ ++void zmemzero(dest, len) ++ Bytef* dest; ++ uInt len; ++{ ++ if (len == 0) return; ++ do { ++ *dest++ = 0; /* ??? to be unrolled */ ++ } while (--len != 0); ++} ++#endif ++ ++#ifdef __TURBOC__ ++#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) ++/* Small and medium model in Turbo C are for now limited to near allocation ++ * with reduced MAX_WBITS and MAX_MEM_LEVEL ++ */ ++# define MY_ZCALLOC ++ ++/* Turbo C malloc() does not allow dynamic allocation of 64K bytes ++ * and farmalloc(64K) returns a pointer with an offset of 8, so we ++ * must fix the pointer. Warning: the pointer must be put back to its ++ * original form in order to free it, use zcfree(). ++ */ ++ ++#define MAX_PTR 10 ++/* 10*64K = 640K */ ++ ++local int next_ptr = 0; ++ ++typedef struct ptr_table_s { ++ voidpf org_ptr; ++ voidpf new_ptr; ++} ptr_table; ++ ++local ptr_table table[MAX_PTR]; ++/* This table is used to remember the original form of pointers ++ * to large buffers (64K). Such pointers are normalized with a zero offset. ++ * Since MSDOS is not a preemptive multitasking OS, this table is not ++ * protected from concurrent access. This hack doesn't work anyway on ++ * a protected system like OS/2. Use Microsoft C instead. ++ */ ++ ++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) ++{ ++ voidpf buf = opaque; /* just to make some compilers happy */ ++ ulg bsize = (ulg)items*size; ++ ++ /* If we allocate less than 65520 bytes, we assume that farmalloc ++ * will return a usable pointer which doesn't have to be normalized. ++ */ ++ if (bsize < 65520L) { ++ buf = farmalloc(bsize); ++ if (*(ush*)&buf != 0) return buf; ++ } else { ++ buf = farmalloc(bsize + 16L); ++ } ++ if (buf == NULL || next_ptr >= MAX_PTR) return NULL; ++ table[next_ptr].org_ptr = buf; ++ ++ /* Normalize the pointer to seg:0 */ ++ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; ++ *(ush*)&buf = 0; ++ table[next_ptr++].new_ptr = buf; ++ return buf; ++} ++ ++void zcfree (voidpf opaque, voidpf ptr) ++{ ++ int n; ++ if (*(ush*)&ptr != 0) { /* object < 64K */ ++ farfree(ptr); ++ return; ++ } ++ /* Find the original pointer */ ++ for (n = 0; n < next_ptr; n++) { ++ if (ptr != table[n].new_ptr) continue; ++ ++ farfree(table[n].org_ptr); ++ while (++n < next_ptr) { ++ table[n-1] = table[n]; ++ } ++ next_ptr--; ++ return; ++ } ++ ptr = opaque; /* just to make some compilers happy */ ++ Assert(0, "zcfree: ptr not found"); ++} ++#endif ++#endif /* __TURBOC__ */ ++ ++ ++#if defined(M_I86) && !defined(__32BIT__) ++/* Microsoft C in 16-bit mode */ ++ ++# define MY_ZCALLOC ++ ++#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) ++# define _halloc halloc ++# define _hfree hfree ++#endif ++ ++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) ++{ ++ if (opaque) opaque = 0; /* to make compiler happy */ ++ return _halloc((long)items, size); ++} ++ ++void zcfree (voidpf opaque, voidpf ptr) ++{ ++ if (opaque) opaque = 0; /* to make compiler happy */ ++ _hfree(ptr); ++} ++ ++#endif /* MSC */ ++ ++ ++#ifndef MY_ZCALLOC /* Any system without a special alloc function */ ++ ++#ifndef STDC ++extern voidp calloc OF((uInt items, uInt size)); ++extern void free OF((voidpf ptr)); ++#endif ++ ++voidpf zcalloc (opaque, items, size) ++ voidpf opaque; ++ unsigned items; ++ unsigned size; ++{ ++ if (opaque) items += size - size; /* make compiler happy */ ++ return (voidpf)calloc(items, size); ++} ++ ++void zcfree (opaque, ptr) ++ voidpf opaque; ++ voidpf ptr; ++{ ++ free(ptr); ++ if (opaque) return; /* make compiler happy */ ++} ++ ++#endif /* MY_ZCALLOC */ +--- linux/net/Config.in.orig Fri Feb 9 14:34:13 2001 ++++ linux/net/Config.in Thu Feb 22 19:40:08 2001 +@@ -88,4 +88,9 @@ + #bool 'Network code profiler' CONFIG_NET_PROFILE + endmenu + ++tristate 'IP Security Protocol (FreeS/WAN IPSEC)' CONFIG_IPSEC ++if [ "$CONFIG_IPSEC" != "n" ]; then ++ source net/ipsec/Config.in ++fi ++ + endmenu +RCSID $Id: Makefile.fs2_4.patch,v 1.7 2002/07/28 23:12:39 mcr Exp $ +--- linux/net/Makefile.preipsec Mon Jun 11 22:15:27 2001 ++++ linux/net/Makefile Tue Nov 6 21:07:43 2001 +@@ -17,6 +17,7 @@ + subdir-$(CONFIG_NET) += 802 sched + subdir-$(CONFIG_INET) += ipv4 + subdir-$(CONFIG_NETFILTER) += ipv4/netfilter ++subdir-$(CONFIG_IPSEC) += ipsec + subdir-$(CONFIG_UNIX) += unix + subdir-$(CONFIG_IPV6) += ipv6 + +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/Config.in Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,76 @@ ++# ++# IPSEC configuration ++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs. ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. See . ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# for more details. ++# ++# RCSID $Id: Config.in,v 1.30 2004/06/23 09:49:37 ken Exp $ ++ ++comment 'IPSec options (Openswan)' ++ ++bool ' IPSEC: IP-in-IP encapsulation (tunnel mode)' CONFIG_IPSEC_IPIP ++ ++bool ' IPSEC: Authentication Header' CONFIG_IPSEC_AH ++if [ "$CONFIG_IPSEC_AH" = "y" -o "$CONFIG_IPSEC_ESP" = "y" ]; then ++ bool ' HMAC-MD5 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_MD5 ++ bool ' HMAC-SHA1 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_SHA1 ++fi ++ ++bool ' IPSEC: Encapsulating Security Payload' CONFIG_IPSEC_ESP ++if [ "$CONFIG_IPSEC_ESP" = "y" ]; then ++ bool ' 3DES encryption algorithm' CONFIG_IPSEC_ENC_3DES ++ bool ' AES encryption algorithm' CONFIG_IPSEC_ENC_AES ++fi ++ ++bool ' IPSEC Modular Extensions' CONFIG_IPSEC_ALG ++if [ "$CONFIG_IPSEC_ALG" != "n" ]; then ++ source net/ipsec/alg/Config.in ++fi ++ ++bool ' IPSEC: IP Compression' CONFIG_IPSEC_IPCOMP ++ ++bool ' IPSEC Debugging Option' CONFIG_IPSEC_DEBUG ++ ++# ++# ++# $Log: Config.in,v $ ++# Revision 1.30 2004/06/23 09:49:37 ken ++# Free -> Open ++# ++# Revision 1.29 2004/04/06 02:49:25 mcr ++# pullup of algo code from alg-branch. ++# ++# Revision 1.28 2004/02/03 03:12:26 mcr ++# remove NAT-traversal option from IPsec config, ++# as it should be in the kernel configuration if ++# the NAT-T patch is installed. ++# ++# Revision 1.27.2.2 2004/04/05 04:30:46 mcr ++# patches for alg-branch to compile/work with 2.x openswan ++# ++# Revision 1.27.2.1 2003/12/23 12:48:25 jjo ++# Added missing alg part to linux/net/ipsec/Config.in ++# ++# Revision 1.27 2003/12/10 01:14:27 mcr ++# NAT-traversal patches to KLIPS. ++# ++# Revision 1.26 2002/04/24 07:36:26 mcr ++# Moved from ./klips/net/ipsec/Config.in,v ++# ++# Revision 1.25 2002/02/21 19:55:12 mcr ++# removed all traces of IPSEC_CONFIG_REGRESS because it ++# screwed up 2.2's "make menuconfig" scripts. ++# ++# Revision 1.24 2002/01/28 20:24:31 mcr ++# commented out IPSEC_REGRESS option from user visible config. ++# ++# ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/Makefile Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,337 @@ ++# Makefile for KLIPS kernel code as a module ++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs. ++# Copyright (C) 2002 Michael Richardson ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. See . ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++# for more details. ++# ++# RCSID $Id: Makefile,v 1.72 2004/06/22 14:44:07 ken Exp $ ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++ ++ifeq ($(strip $(KLIPSMODULE)),) ++OPENSWANSRCDIR=. ++else ++OPENSWANSRCDIR=../../.. ++endif ++-include ${OPENSWANSRCDIR}/Makefile.ver ++ ++ifeq ($(strip $(KLIPS_TOP)),) ++KLIPS_TOP=../.. ++endif ++ ++ifneq ($(strip $(KLIPSMODULE)),) ++ ++ifndef TOPDIR ++TOPDIR:=/usr/src/linux ++endif ++export TOPDIR ++ ++endif ++ ++# ++# This magic from User-Mode-Linux list. It gets list of -I options, as ++# UML needs some extra, that varry by revision. ++# ++KERNEL_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(CFLAGS)' ) ++ ++MODULE_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(MODFLAGS)' ) ++ ++subdir- := ++subdir-n := ++subdir-y := ++subdir-m := ++ ++ ++MOD_DESTDIR:=net/ipsec ++ ++export TOPDIR ++ ++all: ipsec.o ++ ++foo: ++ echo KERNEL: ${KERNEL_CFLAGS} ++ echo MODULE: ${MODULE_CFLAGS} ++ ++ipsec.o: foo ++ ++O_TARGET := ipsec.o ++obj-y := ipsec_init.o ipsec_sa.o ipsec_radij.o radij.o ++obj-y += ipsec_life.o ipsec_proc.o ++obj-y += ipsec_tunnel.o ipsec_xmit.o ipsec_rcv.o ipsec_ipip.o ++obj-y += sysctl_net_ipsec.o ++obj-y += pfkey_v2.o pfkey_v2_parser.o pfkey_v2_ext_process.o ++obj-y += version.o ++obj-$(CONFIG_IPSEC_AH) += ipsec_ah.o ++obj-$(CONFIG_IPSEC_ESP) += ipsec_esp.o ++obj-$(CONFIG_IPSEC_IPCOMP)+= ipsec_ipcomp.o ++ ++CFLAGS_ipsec_alg.o += -DEXPORT_SYMTAB ++obj-$(CONFIG_IPSEC_ALG) += ipsec_alg.o ++obj-$(CONFIG_IPSEC_ENC_AES) += ipsec_alg_aes.o ++obj-$(CONFIG_IPSEC_ENC_CRYPTOAPI) += ipsec_alg_cryptoapi.o ++ ++export-objs += ipsec_alg.o ++ ++ ++LIBDESDIR=${KLIPS_TOP}/crypto/ciphers/des ++VPATH+= ${LIBDESDIR} ++ ++include ${LIBDESDIR}/Makefile.objs ++ ++LIBFREESWANDIR=${KLIPS_TOP}/lib/libfreeswan ++VPATH+=${LIBFREESWANDIR} ++ ++include ${LIBFREESWANDIR}/Makefile.objs ++ ++# IPcomp stuff ++obj-$(CONFIG_IPSEC_IPCOMP) += ipcomp.o ++ ++LIBZLIBSRCDIR=${KLIPS_TOP}/lib/zlib ++VPATH+=${LIBZLIBSRCDIR} ++ ++LIBAESDIR=$(KLIPS_TOP)/crypto/ciphers/aes ++VPATH+=${LIBAESDIR} ++include ${LIBAESDIR}/Makefile.objs ++ ++# CFLAGS='$(CFLAGS)' \ ++# MODULE_CFLAGS='$(MODULE_CFLAGS)' KERNEL_CFLAGS='$(KERNEL_CFLAGS)' \ ++# ++include ${LIBZLIBSRCDIR}/Makefile.objs ++ ++export-objs := radij.o ++ ++EXTRA_CFLAGS += $(ALGO_FLAGS) ++ ++ ++# include file with .h-style macros that would otherwise be created by ++# config. Must occur before other includes. ++ifneq ($(strip $(MODULE_DEF_INCLUDE)),) ++EXTRA_CFLAGS += -include ${MODULE_DEF_INCLUDE} ++endif ++ ++# 'override CFLAGS' should really be 'EXTRA_CFLAGS' ++#EXTRA_CFLAGS += -nostdinc ++EXTRA_CFLAGS += -I${KLIPS_TOP}/include ++ ++EXTRA_CFLAGS += -I${TOPDIR}/include ++EXTRA_CFLAGS += -I${LIBZLIBSRCDIR} ++ ++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.2-2) ++EXTRA_CFLAGS += -DREDHAT_BOGOSITY ++endif ++ ++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.3-12) ++EXTRA_CFLAGS += -DREDHAT_BOGOSITY ++endif ++ ++ ++#ifeq ($(CONFIG_IPSEC_DEBUG),y) ++#EXTRA_CFLAGS += -g ++#endif ++ ++#ifeq ($(CONFIG_IPSEC_ALG), y) ++EXTRA_CFLAGS += -DCONFIG_IPSEC_ALG ++#endif ++# MOST of these flags are in KERNEL_CFLAGS already! ++ ++EXTRA_CFLAGS += $(KLIPSCOMPILE) ++EXTRA_CFLAGS += -Wall ++#EXTRA_CFLAGS += -Werror ++#EXTRA_CFLAGS += -Wconversion ++#EXTRA_CFLAGS += -Wmissing-prototypes ++# cannot use both -Wpointer-arith and -Werror with CONFIG_HIGHMEM ++# include/linux/highmem.h has an inline function definition that uses void* arithmentic. ++ifeq ($(CONFIG_NOHIGHMEM),y) ++EXTRA_CFLAGS += -Wpointer-arith ++endif ++#EXTRA_CFLAGS += -Wcast-qual ++#EXTRA_CFLAGS += -Wmissing-declarations ++#EXTRA_CFLAGS += -Wstrict-prototypes ++#EXTRA_CFLAGS += -pedantic ++#EXTRA_CFLAGS += -O3 ++#EXTRA_CFLAGS += -W ++#EXTRA_CFLAGS += -Wwrite-strings ++#EXTRA_CFLAGS += -Wbad-function-cast ++ ++ifneq ($(strip $(KLIPSMODULE)),) ++# for when we aren't building in the kernel tree ++EXTRA_CFLAGS += -DARCH=${ARCH} ++EXTRA_CFLAGS += -DMODVERSIONS ++EXTRA_CFLAGS += -include ${TOPDIR}/include/linux/modversions.h ++EXTRA_CFLAGS += ${MODULE_CFLAGS} ++endif ++ ++EXTRA_CFLAGS += ${KERNEL_CFLAGS} ++ ++#EXTRA_CFLAGS += -DRJ_DEBUG -DRJ_DEBUG2 ++ ++ ++# GCC 3.2 (and we presume any other 3.x) wants -falign-functions ++# in place of the traditional -malign-functions. Getting this ++# wrong leads to a warning, which is fatal due to our use of -Werror. ++ifeq ($(patsubst 3.%,3,$(shell $(CC) -dumpversion)),3) ++override CFLAGS:=$(subst -malign-functions=,-falign-functions=,$(CFLAGS)) ++endif ++ ++ ++obj-$(CONFIG_IPSEC_AUTH_HMAC_MD5) += ipsec_md5c.o ++obj-$(CONFIG_IPSEC_AUTH_HMAC_SHA1) += ipsec_sha1.o ++ ++### ++### Pre Rules.make ++### ++# undo O_TARGET, obj-y if no static ++ifneq ($(CONFIG_IPSEC),y) ++O_TARGET := ++ipsec_obj-y := $(obj-y) ++obj-y := ++subdir-y := ++endif ++ ++# Define obj-m if modular ipsec ++ifeq ($(CONFIG_IPSEC),m) ++obj-m += ipsec.o ++endif ++ ++ ++# These rules translate from new to old makefile rules ++# Translate to Rules.make lists. ++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m)) ++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs)) ++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m)) ++O_OBJS := $(obj-y) ++M_OBJS := $(obj-m) ++MIX_OBJS := $(filter $(export-objs), $(active-objs)) ++OX_OBJS := $(export-objs) ++SUB_DIRS := $(subdir-y) ++ALL_SUB_DIRS := $(subdir-y) $(subdir-m) ++MOD_SUB_DIRS := $(subdir-m) ++ ++# dunno why, but some 2.2 setups may need explicit -DEXPORT_SYMTAB ++# uncomment next line if ipsec_alg.c compilation fails with ++# "parse error before `EXPORT_SYMTAB_not_defined'" --Juanjo ++ ++include $(TOPDIR)/Rules.make ++ ++### ++### Post Rules.make ++### ++# for modular ipsec, no O_TARGET defined => define ipsec.o creation rules ++ifeq ($(CONFIG_IPSEC),m) ++ipsec.o : $(ipsec_obj-y) ++ rm -f $@ ++ $(LD) $(LD_EXTRAFLAGS) -r $(ipsec_obj-y) -o $@ ++endif ++ ++$(ipsec_obj-y) $(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h ++ ++#$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h ++ ++USE_STANDARD_AS_RULE=true ++ ++clean: ++ $(MAKE) -C alg clean ++ -rm -f *.o ++ -rm -f .*.o.flags ++ -rm -f version.c ++ ++tags TAGS: *.c *.h libfreeswan/*.c libfreeswan/*.h ++ etags *.c ../../include/*.h ../../include/freeswan/*.h ++ ctags *.c ../../include/*.h ../../include/freeswan/*.h ++ ++tar: ++ tar -cvf /dev/f1 . ++ ++# ++# $Log: Makefile,v $ ++# Revision 1.72 2004/06/22 14:44:07 ken ++# Merge nice version of Nate's CryptoAPI patch ++# ++# Revision 1.71 2004/04/18 03:04:21 mcr ++# removed duplicate version.o reference. ++# ++# Revision 1.70 2004/04/14 05:09:39 ken ++# We need to link version.o ++# ++# Revision 1.69 2004/04/12 04:02:39 ken ++# version.o no longer exists ++# ++# Revision 1.68 2004/04/11 17:08:41 mcr ++# moved PASSTHROUGH definitions to openswan.h ++# requirement for internal.h removed. ++# version.c is now generated by patch at patch-time. ++# ++# Revision 1.67 2004/04/06 02:49:25 mcr ++# pullup of algo code from alg-branch. ++# ++# Revision 1.66 2004/04/03 19:44:41 ken ++# FREESWANSRCDIR -> OPENSWANSRCDIR (patch by folken) ++# ++# Revision 1.65 2004/02/09 16:22:07 paul ++# Added -f to rm version.c in clean target to prevent bogus error ++# ++# Revision 1.64 2003/12/22 19:40:57 mcr ++# NAT-T patches 0.6c. ++# ++# Revision 1.63 2003/12/13 19:10:21 mcr ++# refactored rcv and xmit code - same as FS 2.05. ++# ++# Revision 1.62.4.2 2004/04/05 04:30:46 mcr ++# patches for alg-branch to compile/work with 2.x openswan ++# ++# Revision 1.62.4.1 2003/12/22 15:25:52 jjo ++# Merged algo-0.8.1-rc11-test1 into alg-branch ++# ++# Revision 1.62 2003/10/31 02:27:55 mcr ++# pulled up port-selector patches and sa_id elimination. ++# ++# Revision 1.61.4.1 2003/10/29 01:30:41 mcr ++# elimited "struct sa_id". ++# ++# Revision 1.61 2003/06/22 21:07:46 mcr ++# adjusted TAGS target in makefile to be useful in 2.00 source layout. ++# ++# Revision 1.60 2003/05/03 23:45:23 mcr ++# rm .o.flags and generated version.c file. ++# ++# Revision 1.59 2003/02/12 19:32:47 rgb ++# Added ipsec_xmit to the list of object files. ++# ++# Revision 1.58 2003/01/03 00:36:44 rgb ++# ++# Added emacs compile-command. ++# ++# Revision 1.57 2002/11/08 23:49:53 mcr ++# use KERNEL_CFLAGS and MODULE_CFLAGS to get proper list ++# of include directories. ++# This also eliminates some of the guesswork in the kernel ++# configuration file. ++# ++# Revision 1.56 2002/11/08 23:23:18 mcr ++# attempt to guess kernel compilation flags (i.e. list of -I) ++# by using some magic targets in the kernel makefile. ++# ++# Revision 1.55 2002/11/08 10:13:33 mcr ++# added additional include directories for module builds for 2.4.19. ++# ++# Revision 1.54 2002/10/20 06:10:30 build ++# CONFIG_NOHIGHMEM for -Wpointer-arith RPM building issues. ++# ++# (elided rest of log) ++# ++# Local Variables: ++# compile-command: "(cd ../../.. && source umlsetup.sh && make -C ${POOLSPACE} module/ipsec.o)" ++# End Variables: ++# ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Config.alg_aes.in Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,3 @@ ++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then ++ tristate ' AES encryption algorithm' CONFIG_IPSEC_ALG_AES ++fi +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Config.alg_cryptoapi.in Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,6 @@ ++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then ++ dep_tristate ' CRYPTOAPI ciphers support (needs cryptoapi patch)' CONFIG_IPSEC_ALG_CRYPTOAPI $CONFIG_CRYPTO ++ if [ "$CONFIG_IPSEC_ALG_CRYPTOAPI" != "n" ]; then ++ bool ' CRYPTOAPI proprietary ciphers ' CONFIG_IPSEC_ALG_NON_LIBRE ++ fi ++fi +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Config.in Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,3 @@ ++#Placeholder ++source net/ipsec/alg/Config.alg_aes.in ++source net/ipsec/alg/Config.alg_cryptoapi.in +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Makefile Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,112 @@ ++# Makefile,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ifeq ($(strip $(KLIPSMODULE)),) ++FREESWANSRCDIR=. ++else ++FREESWANSRCDIR=../../../.. ++endif ++ifeq ($(strip $(KLIPS_TOP)),) ++KLIPS_TOP=../../.. ++override EXTRA_CFLAGS += -I$(KLIPS_TOP)/include ++endif ++ ++ifeq ($(CONFIG_IPSEC_DEBUG),y) ++override EXTRA_CFLAGS += -g ++endif ++ ++# LIBCRYPTO normally comes as an argument from "parent" Makefile ++# (this applies both to FS' "make module" and eg. Linux' "make modules" ++# But make dep doest follow same evaluations, so we need this default: ++LIBCRYPTO=$(TOPDIR)/lib/libcrypto ++ ++override EXTRA_CFLAGS += -I$(LIBCRYPTO)/include ++override EXTRA_CFLAGS += -Wall -Wpointer-arith -Wstrict-prototypes ++ ++MOD_LIST_NAME := NET_MISC_MODULES ++ ++#O_TARGET := static_init.o ++ ++subdir- := ++subdir-n := ++subdir-y := ++subdir-m := ++ ++obj-y := static_init.o ++ ++ARCH_ASM-y := ++ARCH_ASM-$(CONFIG_M586) := i586 ++ARCH_ASM-$(CONFIG_M586TSC) := i586 ++ARCH_ASM-$(CONFIG_M586MMX) := i586 ++ARCH_ASM-$(CONFIG_MK6) := i586 ++ARCH_ASM-$(CONFIG_M686) := i686 ++ARCH_ASM-$(CONFIG_MPENTIUMIII) := i686 ++ARCH_ASM-$(CONFIG_MPENTIUM4) := i686 ++ARCH_ASM-$(CONFIG_MK7) := i686 ++ARCH_ASM-$(CONFIG_MCRUSOE) := i586 ++ARCH_ASM-$(CONFIG_MWINCHIPC6) := i586 ++ARCH_ASM-$(CONFIG_MWINCHIP2) := i586 ++ARCH_ASM-$(CONFIG_MWINCHIP3D) := i586 ++ARCH_ASM-$(CONFIG_USERMODE) := i586 ++ ++ARCH_ASM :=$(ARCH_ASM-y) ++ifdef NO_ASM ++ARCH_ASM := ++endif ++ ++# The algorithm makefiles may put dependences, short-circuit them ++null: ++ ++makefiles=$(filter-out %.preipsec, $(wildcard Makefile.alg_*)) ++ifneq ($(makefiles),) ++#include Makefile.alg_aes ++#include Makefile.alg_aes-opt ++include $(makefiles) ++endif ++ ++# These rules translate from new to old makefile rules ++# Translate to Rules.make lists. ++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m)) ++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs)) ++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m)) ++O_OBJS := $(obj-y) ++M_OBJS := $(obj-m) ++MIX_OBJS := $(filter $(export-objs), $(active-objs)) ++#OX_OBJS := $(export-objs) ++SUB_DIRS := $(subdir-y) ++ALL_SUB_DIRS := $(subdir-y) $(subdir-m) ++MOD_SUB_DIRS := $(subdir-m) ++ ++ ++static_init_mod.o: $(obj-y) ++ rm -f $@ ++ $(LD) $(LD_EXTRAFLAGS) $(obj-y) -r -o $@ ++ ++perlasm: $(LIBCRYPTO)/perlasm ++ ln -sf $? $@ ++ ++$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h ++$(alg_obj-y) $(alg_obj-m): perlasm $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h ++ ++ ++all_alg_modules: perlasm $(ALG_MODULES) ++ @echo "ALG_MODULES=$(ALG_MODULES)" ++ ++ ++# ++# Construct alg. init. function: call ipsec_ALGO_init() for every static algo ++# Needed when there are static algos (with static or modular ipsec.o) ++# ++static_init.c: $(TOPDIR)/include/linux/autoconf.h Makefile $(makefiles) scripts/mk-static_init.c.sh ++ @echo "Re-creating $@" ++ $(SHELL) scripts/mk-static_init.c.sh $(static_init-func-y) > $@ ++ ++clean: ++ @for i in $(ALG_SUBDIRS);do test -d $$i && make -C $$i clean;done;exit 0 ++ @find . -type l -exec rm -f {} \; ++ -rm -f perlasm ++ -rm -rf $(ALG_SUBDIRS) ++ -rm -f *.o static_init.c ++ ++ifdef TOPDIR ++include $(TOPDIR)/Rules.make ++endif ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Makefile.alg_aes Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,23 @@ ++MOD_AES := ipsec_aes.o ++ ++ALG_MODULES += $(MOD_AES) ++ALG_SUBDIRS += libaes ++ ++obj-$(CONFIG_IPSEC_ALG_AES) += $(MOD_AES) ++static_init-func-$(CONFIG_IPSEC_ALG_AES)+= ipsec_aes_init ++alg_obj-$(CONFIG_IPSEC_ALG_AES) += ipsec_alg_aes.o ++ ++AES_OBJS := ipsec_alg_aes.o libaes/libaes.a ++ ++$(MOD_AES): libaes $(AES_OBJS) ++ $(LD) $(EXTRA_LDFLAGS) -r $(AES_OBJS) -o $@ ++ ++libaes: $(LIBCRYPTO)/libaes ++ test -d $@ || mkdir $@ ;exit 0 ++ test -d $@/asm || mkdir $@/asm;exit 0 ++ cd $@ && ln -sf $?/Makefile $?/*.[chS] . ++ cd $@/asm && ln -sf $?/asm/*.S . ++ ++libaes/libaes.a: libaes ++ ( cd libaes && \ ++ $(MAKE) CC='$(CC)' 'ARCH_ASM=$(ARCH_ASM)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' libaes.a ;) +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/Makefile.alg_cryptoapi Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,14 @@ ++MOD_CRYPTOAPI := ipsec_cryptoapi.o ++ ++ifneq ($(wildcard $(TOPDIR)/include/linux/crypto.h),) ++ALG_MODULES += $(MOD_CRYPTOAPI) ++obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += $(MOD_CRYPTOAPI) ++static_init-func-$(CONFIG_IPSEC_ALG_CRYPTOAPI)+= ipsec_cryptoapi_init ++alg_obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += ipsec_alg_cryptoapi.o ++else ++$(warning "Linux CryptoAPI (2.4.22+ or 2.6.x) not found, not building ipsec_cryptoapi.o") ++endif ++ ++CRYPTOAPI_OBJS := ipsec_alg_cryptoapi.o ++$(MOD_CRYPTOAPI): $(CRYPTOAPI_OBJS) ++ $(LD) -r $(CRYPTOAPI_OBJS) -o $@ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,253 @@ ++/* ++ * ipsec_alg AES cipher stubs ++ * ++ * Author: JuanJo Ciarlante ++ * ++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * Fixes by: ++ * PK: Pawel Krawczyk ++ * Fixes list: ++ * PK: make XCBC comply with latest draft (keylength) ++ * ++ */ ++#include ++#include ++ ++/* ++ * special case: ipsec core modular with this static algo inside: ++ * must avoid MODULE magic for this file ++ */ ++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES ++#undef MODULE ++#endif ++ ++#include ++#include ++ ++#include /* printk() */ ++#include /* error codes */ ++#include /* size_t */ ++#include ++ ++/* Check if __exit is defined, if not null it */ ++#ifndef __exit ++#define __exit ++#endif ++ ++/* Low freeswan header coupling */ ++#include "freeswan/ipsec_alg.h" ++#include "libaes/aes_cbc.h" ++ ++#define CONFIG_IPSEC_ALG_AES_MAC 1 ++ ++#define AES_CONTEXT_T aes_context ++MODULE_AUTHOR("JuanJo Ciarlante "); ++static int debug=0; ++MODULE_PARM(debug, "i"); ++static int test=0; ++MODULE_PARM(test, "i"); ++static int excl=0; ++MODULE_PARM(excl, "i"); ++static int keyminbits=0; ++MODULE_PARM(keyminbits, "i"); ++static int keymaxbits=0; ++MODULE_PARM(keymaxbits, "i"); ++ ++#if CONFIG_IPSEC_ALG_AES_MAC ++#include "libaes/aes_xcbc_mac.h" ++ ++/* ++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt). ++ * We use 9 for non-modular algorithm and none for modular, thus ++ * forcing user to specify one on module load. -kravietz ++ */ ++#ifdef MODULE ++static int auth_id=0; ++#else ++static int auth_id=9; ++#endif ++MODULE_PARM(auth_id, "i"); ++#endif ++ ++#define ESP_AES 12 /* truely _constant_ :) */ ++ ++/* 128, 192 or 256 */ ++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */ ++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */ ++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */ ++ ++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt ++ * -kravietz ++ */ ++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */ ++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */ ++ ++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { ++ int ret; ++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; ++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL; ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_aes_set_key:" ++ "ret=%d key_e=%p key=%p keysize=%d\n", ++ ret, key_e, key, keysize); ++ return ret; ++} ++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { ++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:" ++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", ++ key_e, in, ilen, iv, encrypt); ++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt); ++} ++#if CONFIG_IPSEC_ALG_AES_MAC ++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) { ++ aes_context_mac *ctxm=(aes_context_mac *)key_a; ++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL; ++} ++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) { ++ int ret; ++ char hash_buf[16]; ++ aes_context_mac *ctxm=(aes_context_mac *)key_a; ++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf); ++ memcpy(hash, hash_buf, hashlen); ++ return ret; ++} ++static struct ipsec_alg_auth ipsec_alg_AES_MAC = { ++ ixt_version: IPSEC_ALG_VERSION, ++ ixt_module: THIS_MODULE, ++ ixt_refcnt: ATOMIC_INIT(0), ++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH, ++ ixt_alg_id: 0, ++ ixt_name: "aes_mac", ++ ixt_blocksize: ESP_AES_MAC_BLK_LEN, ++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8, ++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8, ++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ, ++ ixt_a_ctx_size: sizeof(aes_context_mac), ++ ixt_a_hmac_set_key: _aes_mac_set_key, ++ ixt_a_hmac_hash:_aes_mac_hash, ++}; ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++static struct ipsec_alg_enc ipsec_alg_AES = { ++ ixt_version: IPSEC_ALG_VERSION, ++ ixt_module: THIS_MODULE, ++ ixt_refcnt: ATOMIC_INIT(0), ++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, ++ ixt_alg_id: ESP_AES, ++ ixt_name: "aes", ++ ixt_blocksize: ESP_AES_CBC_BLK_LEN, ++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8, ++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8, ++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX, ++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T), ++ ixt_e_set_key: _aes_set_key, ++ ixt_e_cbc_encrypt:_aes_cbc_encrypt, ++}; ++ ++IPSEC_ALG_MODULE_INIT( ipsec_aes_init ) ++{ ++ int ret, test_ret; ++ if (keyminbits) ++ ipsec_alg_AES.ixt_keyminbits=keyminbits; ++ if (keymaxbits) { ++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits; ++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits) ++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8; ++ } ++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL; ++ ret=register_ipsec_alg_enc(&ipsec_alg_AES); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ ipsec_alg_AES.ixt_name, ++ ret); ++ if (ret==0 && test) { ++ test_ret=ipsec_alg_test( ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ test); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ test_ret); ++ } ++#if CONFIG_IPSEC_ALG_AES_MAC ++ if (auth_id!=0){ ++ int ret; ++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id; ++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ ipsec_alg_AES_MAC.ixt_name, ++ ret); ++ if (ret==0 && test) { ++ test_ret=ipsec_alg_test( ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ test); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ test_ret); ++ } ++ } else { ++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id); ++ } ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++ return ret; ++} ++IPSEC_ALG_MODULE_EXIT( ipsec_aes_fini ) ++{ ++#if CONFIG_IPSEC_ALG_AES_MAC ++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC); ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++ unregister_ipsec_alg_enc(&ipsec_alg_AES); ++ return; ++} ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++#if 0+NOT_YET ++#ifndef MODULE ++/* ++ * This is intended for static module setups, currently ++ * doesn't work for modular ipsec.o with static algos inside ++ */ ++static int setup_keybits(const char *str) ++{ ++ unsigned aux; ++ char *end; ++ ++ aux = simple_strtoul(str,&end,0); ++ if (aux != 128 && aux != 192 && aux != 256) ++ return 0; ++ keyminbits = aux; ++ ++ if (*end == 0 || *end != ',') ++ return 1; ++ str=end+1; ++ aux = simple_strtoul(str, NULL, 0); ++ if (aux != 128 && aux != 192 && aux != 256) ++ return 0; ++ if (aux >= keyminbits) ++ keymaxbits = aux; ++ return 1; ++} ++__setup("ipsec_aes_keybits=", setup_keybits); ++#endif ++#endif ++EXPORT_NO_SYMBOLS; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,421 @@ ++/* ++ * ipsec_alg to linux cryptoapi GLUE ++ * ++ * Authors: CODE.ar TEAM ++ * Harpo MAxx ++ * JuanJo Ciarlante ++ * Luciano Ruete ++ * ++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * Example usage: ++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos) ++ * modprobe ipsec_cryptoapi ++ * modprobe ipsec_cryptoapi test=1 ++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo) ++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers) ++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens) ++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES) ++ */ ++#include ++#include ++ ++/* ++ * special case: ipsec core modular with this static algo inside: ++ * must avoid MODULE magic for this file ++ */ ++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI ++#undef MODULE ++#endif ++ ++#include ++#include ++ ++#include /* printk() */ ++#include /* error codes */ ++#include /* size_t */ ++#include ++ ++/* Check if __exit is defined, if not null it */ ++#ifndef __exit ++#define __exit ++#endif ++ ++/* warn the innocent */ ++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE) ++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x" ++#define NO_CRYPTOAPI_SUPPORT ++#endif ++/* Low freeswan header coupling */ ++#include "freeswan/ipsec_alg.h" ++ ++#include ++#ifdef CRYPTO_API_VERSION_CODE ++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported" ++#define NO_CRYPTOAPI_SUPPORT ++#endif ++ ++#ifdef NO_CRYPTOAPI_SUPPORT ++#warning "Building an unusable module :P" ++/* Catch old CryptoAPI by not allowing module to load */ ++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) ++{ ++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n"); ++ return -EINVAL; ++} ++#else ++#include ++#include ++#include ++ ++#define CIPHERNAME_AES "aes" ++#define CIPHERNAME_3DES "des3_ede" ++#define CIPHERNAME_BLOWFISH "blowfish" ++#define CIPHERNAME_CAST "cast5" ++#define CIPHERNAME_SERPENT "serpent" ++#define CIPHERNAME_TWOFISH "twofish" ++ ++#define ESP_3DES 3 ++#define ESP_AES 12 ++#define ESP_BLOWFISH 7 /* truely _constant_ :) */ ++#define ESP_CAST 6 /* quite constant :) */ ++#define ESP_SERPENT 252 /* from ipsec drafts */ ++#define ESP_TWOFISH 253 /* from ipsec drafts */ ++ ++#define AH_MD5 2 ++#define AH_SHA 3 ++#define DIGESTNAME_MD5 "md5" ++#define DIGESTNAME_SHA1 "sha1" ++ ++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete"); ++static int debug=0; ++MODULE_PARM(debug, "i"); ++static int test=0; ++MODULE_PARM(test, "i"); ++static int excl=0; ++MODULE_PARM(excl, "i"); ++ ++static int noauto = 0; ++MODULE_PARM(noauto,"i"); ++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones"); ++ ++static int des_ede3[] = {-1, -1}; ++static int aes[] = {-1, -1}; ++static int blowfish[] = {-1, -1}; ++static int cast[] = {-1, -1}; ++static int serpent[] = {-1, -1}; ++static int twofish[] = {-1, -1}; ++ ++MODULE_PARM(des_ede3,"1-2i"); ++MODULE_PARM(aes,"1-2i"); ++MODULE_PARM(blowfish,"1-2i"); ++MODULE_PARM(cast,"1-2i"); ++MODULE_PARM(serpent,"1-2i"); ++MODULE_PARM(twofish,"1-2i"); ++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse"); ++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens"); ++ ++struct ipsec_alg_capi_cipher { ++ const char *ciphername; /* cryptoapi's ciphername */ ++ unsigned blocksize; ++ unsigned short minbits; ++ unsigned short maxbits; ++ int *parm; /* lkm param for this cipher */ ++ struct ipsec_alg_enc alg; /* note it's not a pointer */ ++}; ++static struct ipsec_alg_capi_cipher alg_capi_carray[] = { ++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }}, ++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }}, ++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }}, ++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }}, ++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }}, ++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }}, ++ { NULL, 0, 0, 0, NULL, {} } ++}; ++#ifdef NOT_YET ++struct ipsec_alg_capi_digest { ++ const char *digestname; /* cryptoapi's digestname */ ++ struct digest_implementation *di; ++ struct ipsec_alg_auth alg; /* note it's not a pointer */ ++}; ++static struct ipsec_alg_capi_cipher alg_capi_darray[] = { ++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }}, ++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }}, ++ { NULL, NULL, {} } ++}; ++#endif ++/* ++ * "generic" linux cryptoapi setup_cipher() function ++ */ ++int setup_cipher(const char *ciphername) ++{ ++ return crypto_alg_available(ciphername, 0); ++} ++ ++/* ++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling ++ * register_ipsec_alg for cointaned ipsec_alg object ++ */ ++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e); ++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen); ++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt); ++ ++static int ++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr) ++{ ++ int ret; ++ cptr->alg.ixt_version = IPSEC_ALG_VERSION; ++ cptr->alg.ixt_module = THIS_MODULE; ++ atomic_set (& cptr->alg.ixt_refcnt, 0); ++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name)); ++ ++ cptr->alg.ixt_blocksize=cptr->blocksize; ++ cptr->alg.ixt_keyminbits=cptr->minbits; ++ cptr->alg.ixt_keymaxbits=cptr->maxbits; ++ cptr->alg.ixt_state = 0; ++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL; ++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8; ++ cptr->alg.ixt_e_ctx_size = 0; ++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT; ++ cptr->alg.ixt_e_new_key = _capi_new_key; ++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key; ++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt; ++ cptr->alg.ixt_data = cptr; ++ ++ ret=register_ipsec_alg_enc(&cptr->alg); ++ printk("setup_ipsec_alg_capi_cipher(): " ++ "alg_type=%d alg_id=%d name=%s " ++ "keyminbits=%d keymaxbits=%d, ret=%d\n", ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ cptr->alg.ixt_name, ++ cptr->alg.ixt_keyminbits, ++ cptr->alg.ixt_keymaxbits, ++ ret); ++ return ret; ++} ++/* ++ * called in ipsec_sa_wipe() time, will destroy key contexts ++ * and do 1 unbind() ++ */ ++static void ++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e) ++{ ++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e; ++ ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:" ++ "name=%s key_e=%p \n", ++ alg->ixt_name, key_e); ++ if (!key_e) { ++ printk(KERN_ERR "klips_debug: _capi_destroy_key:" ++ "name=%s NULL key_e!\n", ++ alg->ixt_name); ++ return; ++ } ++ crypto_free_tfm(tfm); ++} ++ ++/* ++ * create new key context, need alg->ixt_data to know which ++ * (of many) cipher inside this module is the target ++ */ ++static __u8 * ++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ struct crypto_tfm *tfm=NULL; ++ ++ cptr = alg->ixt_data; ++ if (!cptr) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "NULL ixt_data (?!) for \"%s\" algo\n" ++ , alg->ixt_name); ++ goto err; ++ } ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_capi_new_key:" ++ "name=%s cptr=%p key=%p keysize=%d\n", ++ alg->ixt_name, cptr, key, keylen); ++ ++ /* ++ * alloc tfm ++ */ ++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC); ++ if (!tfm) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n" ++ , alg->ixt_name, cptr->ciphername); ++ goto err; ++ } ++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n" ++ , alg->ixt_name, keylen); ++ crypto_free_tfm(tfm); ++ tfm=NULL; ++ } ++err: ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_capi_new_key:" ++ "name=%s key=%p keylen=%d tfm=%p\n", ++ alg->ixt_name, key, keylen, tfm); ++ return (__u8 *) tfm; ++} ++/* ++ * core encryption function: will use cx->ci to call actual cipher's ++ * cbc function ++ */ ++static int ++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { ++ int error =0; ++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e; ++ struct scatterlist sg = { ++ .page = virt_to_page(in), ++ .offset = (unsigned long)(in) % PAGE_SIZE, ++ .length=ilen, ++ }; ++ if (debug > 1) ++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" ++ "key_e=%p " ++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n" ++ , key_e ++ , in, in, ilen, iv, encrypt); ++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm)); ++ if (encrypt) ++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen); ++ else ++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen); ++ if (debug > 1) ++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" ++ "error=%d\n" ++ , error); ++ return (error<0)? error : ilen; ++} ++/* ++ * main initialization loop: for each cipher in list, do ++ * 1) setup cryptoapi cipher else continue ++ * 2) register ipsec_alg object ++ */ ++static int ++setup_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ /* ++ * see if cipher has been disabled (0) or ++ * if noauto set and not enabled (1) ++ */ ++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) { ++ if (debug>0) ++ printk(KERN_INFO "setup_cipher_list(): " ++ "ciphername=%s skipped at user request: " ++ "noauto=%d parm[0]=%d parm[1]=%d\n" ++ , cptr->ciphername ++ , noauto ++ , cptr->parm[0] ++ , cptr->parm[1]); ++ continue; ++ } ++ /* ++ * use a local ci to avoid touching cptr->ci, ++ * if register ipsec_alg success then bind cipher ++ */ ++ if( setup_cipher(cptr->ciphername) ) { ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:" ++ "setup_cipher_list():" ++ "ciphername=%s found\n" ++ , cptr->ciphername); ++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) { ++ ++ ++ } else { ++ printk(KERN_ERR "klips_debug:" ++ "setup_cipher_list():" ++ "ciphername=%s failed ipsec_alg_register\n" ++ , cptr->ciphername); ++ } ++ } else { ++ if (debug>0) ++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n", ++ cptr->ciphername); ++ } ++ } ++ return 0; ++} ++/* ++ * deregister ipsec_alg objects and unbind ciphers ++ */ ++static int ++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { ++ unregister_ipsec_alg_enc(&cptr->alg); ++ } ++ } ++ return 0; ++} ++/* ++ * test loop for registered algos ++ */ ++static int ++test_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ int test_ret; ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { ++ test_ret=ipsec_alg_test( ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ test); ++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n", ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ test_ret); ++ } ++ } ++ return 0; ++} ++ ++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init ) ++{ ++ int ret, test_ret; ++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0) ++ return -EPROTONOSUPPORT; ++ if (ret==0 && test) { ++ test_ret=test_cipher_list(alg_capi_carray); ++ } ++ return ret; ++} ++IPSEC_ALG_MODULE_EXIT( ipsec_cryptoapi_fini ) ++{ ++ unsetup_cipher_list(alg_capi_carray); ++ return; ++} ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++EXPORT_NO_SYMBOLS; ++#endif /* NO_CRYPTOAPI_SUPPORT */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/alg/scripts/mk-static_init.c.sh Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,18 @@ ++#!/bin/sh ++cat << EOF ++#include ++#include ++#include "freeswan/ipsec_alg.h" ++$(for i in $*; do ++ test -z "$i" && continue ++ echo "extern int $i(void);" ++done) ++void ipsec_alg_static_init(void){ ++ int __attribute__ ((unused)) err=0; ++$(for i in $*; do ++ test -z "$i" && continue ++ echo " if ((err=$i()) < 0)" ++ echo " printk(KERN_WARNING \"$i() returned %d\", err);" ++done) ++} ++EOF +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/defconfig Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,146 @@ ++ ++# ++# RCSID $Id: defconfig,v 1.25 2004/07/05 01:03:53 mcr Exp $ ++# ++ ++# ++# FreeS/WAN IPSec implementation, KLIPS kernel config defaults ++# ++ ++# ++# First, lets override stuff already set or not in the kernel config. ++# ++# We can't even think about leaving this off... ++CONFIG_INET=y ++ ++# ++# This must be on for subnet protection. ++CONFIG_IP_FORWARD=y ++ ++# Shut off IPSEC masquerading if it has been enabled, since it will ++# break the compile. IPPROTO_ESP and IPPROTO_AH were included in ++# net/ipv4/ip_masq.c when they should have gone into include/linux/in.h. ++CONFIG_IP_MASQUERADE_IPSEC=n ++ ++# ++# Next, lets set the recommended FreeS/WAN configuration. ++# ++ ++# To config as static (preferred), 'y'. To config as module, 'm'. ++CONFIG_IPSEC=y ++ ++# To do tunnel mode IPSec, this must be enabled. ++CONFIG_IPSEC_IPIP=y ++ ++# To enable authentication, say 'y'. (Highly recommended) ++CONFIG_IPSEC_AH=y ++ ++# Authentication algorithm(s): ++CONFIG_IPSEC_AUTH_HMAC_MD5=y ++CONFIG_IPSEC_AUTH_HMAC_SHA1=y ++ ++# To enable encryption, say 'y'. (Highly recommended) ++CONFIG_IPSEC_ESP=y ++ ++# Encryption algorithm(s): ++CONFIG_IPSEC_ENC_3DES=y ++CONFIG_IPSEC_ENC_AES=y ++ ++# modular algo extensions (and new ALGOs) ++CONFIG_IPSEC_ALG=y ++CONFIG_IPSEC_ENC_3DES=y ++CONFIG_IPSEC_ENC_AES=y ++ ++CONFIG_IPSEC_ALG_TWOFISH=m ++CONFIG_IPSEC_ALG_BLOWFISH=m ++CONFIG_IPSEC_ALG_SERPENT=m ++CONFIG_IPSEC_ALG_MD5=m ++CONFIG_IPSEC_ALG_SHA1=m ++CONFIG_IPSEC_ALG_SHA2=m ++#CONFIG_IPSEC_ALG_CAST=n ++#CONFIG_IPSEC_ALG_NULL=n ++ ++# Use CryptoAPI for ALG? - by default, no. ++CONFIG_IPSEC_ENC_CRYPTOAPI=n ++ ++ ++# IP Compression: new, probably still has minor bugs. ++CONFIG_IPSEC_IPCOMP=y ++ ++# To enable userspace-switchable KLIPS debugging, say 'y'. ++CONFIG_IPSEC_DEBUG=y ++ ++# NAT Traversal ++CONFIG_IPSEC_NAT_TRAVERSAL=y ++ ++# ++# ++# $Log: defconfig,v $ ++# Revision 1.25 2004/07/05 01:03:53 mcr ++# fix for adding cryptoapi code. ++# keep it off for now, since UMLs do not have it yet. ++# ++# Revision 1.24 2004/04/06 02:49:25 mcr ++# pullup of algo code from alg-branch. ++# ++# Revision 1.23.2.2 2004/04/05 04:30:46 mcr ++# patches for alg-branch to compile/work with 2.x openswan ++# ++# Revision 1.23.2.1 2003/12/22 15:25:52 jjo ++# . Merged algo-0.8.1-rc11-test1 into alg-branch ++# ++# Revision 1.23 2003/12/10 01:14:27 mcr ++# NAT-traversal patches to KLIPS. ++# ++# Revision 1.22 2003/02/24 19:37:27 mcr ++# changed default compilation mode to static. ++# ++# Revision 1.21 2002/04/24 07:36:27 mcr ++# Moved from ./klips/net/ipsec/defconfig,v ++# ++# Revision 1.20 2002/04/02 04:07:40 mcr ++# default build is now 'm'odule for KLIPS ++# ++# Revision 1.19 2002/03/08 18:57:17 rgb ++# Added a blank line at the beginning of the file to make it easier for ++# other projects to patch ./arch/i386/defconfig, for example ++# LIDS+grSecurity requested by Jason Pattie. ++# ++# Revision 1.18 2000/11/30 17:26:56 rgb ++# Cleaned out unused options and enabled ipcomp by default. ++# ++# Revision 1.17 2000/09/15 11:37:01 rgb ++# Merge in heavily modified Svenning Soerensen's ++# IPCOMP zlib deflate code. ++# ++# Revision 1.16 2000/09/08 19:12:55 rgb ++# Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++# ++# Revision 1.15 2000/05/24 19:37:13 rgb ++# *** empty log message *** ++# ++# Revision 1.14 2000/05/11 21:14:57 henry ++# just commenting the FOOBAR=y lines out is not enough ++# ++# Revision 1.13 2000/05/10 20:17:58 rgb ++# Comment out netlink defaults, which are no longer needed. ++# ++# Revision 1.12 2000/05/10 19:13:38 rgb ++# Added configure option to shut off no eroute passthrough. ++# ++# Revision 1.11 2000/03/16 07:09:46 rgb ++# Hardcode PF_KEYv2 support. ++# Disable IPSEC_ICMP by default. ++# Remove DES config option from defaults file. ++# ++# Revision 1.10 2000/01/11 03:09:42 rgb ++# Added a default of 'y' to PF_KEYv2 keying I/F. ++# ++# Revision 1.9 1999/05/08 21:23:12 rgb ++# Added support for 2.2.x kernels. ++# ++# Revision 1.8 1999/04/06 04:54:25 rgb ++# Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++# patch shell fixes. ++# ++# +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipcomp.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,725 @@ ++/* ++ * IPCOMP zlib interface code. ++ * Copyright (C) 2000 Svenning Soerensen ++ * Copyright (C) 2000, 2001 Richard Guy Briggs ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipcomp_c_version[] = "RCSID $Id: ipcomp.c,v 1.36 2004/04/06 02:49:25 mcr Exp $"; ++ ++/* SSS */ ++ ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include ++#include ++#include ++#include ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++ ++#include ++ ++#ifdef NET_21 ++# include ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" /* sysctl_ipsec_inbound_policy_check */ ++#include "openswan/ipcomp.h" ++#include "zlib/zlib.h" ++#include "zlib/zutil.h" ++ ++#include /* SADB_X_CALG_DEFLATE */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int sysctl_ipsec_debug_ipcomp = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++static ++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask); ++ ++static ++voidpf my_zcalloc(voidpf opaque, uInt items, uInt size) ++{ ++ return (voidpf) kmalloc(items*size, GFP_ATOMIC); ++} ++ ++static ++void my_zfree(voidpf opaque, voidpf address) ++{ ++ kfree(address); ++} ++ ++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags) ++{ ++ struct iphdr *iph; ++ unsigned int iphlen, pyldsz, cpyldsz; ++ unsigned char *buffer; ++ z_stream zs; ++ int zresult; ++ ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: .\n"); ++ ++ if(skb == NULL) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "passed in NULL skb, returning ERROR.\n"); ++ if(flags != NULL) { ++ *flags |= IPCOMP_PARMERROR; ++ } ++ return skb; ++ } ++ ++ if(ips == NULL) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "passed in NULL ipsec_sa needed for cpi, returning ERROR.\n"); ++ if(flags) { ++ *flags |= IPCOMP_PARMERROR; ++ } ++ return skb; ++ } ++ ++ if (flags == NULL) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "passed in NULL flags, returning ERROR.\n"); ++ ipsec_kfree_skb(skb); ++ return NULL; ++ } ++ ++#ifdef NET_21 ++ iph = skb->nh.iph; ++#else /* NET_21 */ ++ iph = skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ switch (iph->protocol) { ++ case IPPROTO_COMP: ++ case IPPROTO_AH: ++ case IPPROTO_ESP: ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "skipping compression of packet with ip protocol %d.\n", ++ iph->protocol); ++ *flags |= IPCOMP_UNCOMPRESSABLE; ++ return skb; ++ } ++ ++ /* Don't compress packets already fragmented */ ++ if (iph->frag_off & __constant_htons(IP_MF | IP_OFFSET)) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "skipping compression of fragmented packet.\n"); ++ *flags |= IPCOMP_UNCOMPRESSABLE; ++ return skb; ++ } ++ ++ iphlen = iph->ihl << 2; ++ pyldsz = ntohs(iph->tot_len) - iphlen; ++ ++ /* Don't compress less than 90 bytes (rfc 2394) */ ++ if (pyldsz < 90) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "skipping compression of tiny packet, len=%d.\n", ++ pyldsz); ++ *flags |= IPCOMP_UNCOMPRESSABLE; ++ return skb; ++ } ++ ++ /* Adaptive decision */ ++ if (ips->ips_comp_adapt_skip) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "skipping compression: ips_comp_adapt_skip=%d.\n", ++ ips->ips_comp_adapt_skip); ++ ips->ips_comp_adapt_skip--; ++ *flags |= IPCOMP_UNCOMPRESSABLE; ++ return skb; ++ } ++ ++ zs.zalloc = my_zcalloc; ++ zs.zfree = my_zfree; ++ zs.opaque = 0; ++ ++ /* We want to use deflateInit2 because we don't want the adler ++ header. */ ++ zresult = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -11, ++ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); ++ if (zresult != Z_OK) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_compress: " ++ "deflateInit2() returned error %d (%s), " ++ "skipping compression.\n", ++ zresult, ++ zs.msg ? zs.msg : zError(zresult)); ++ *flags |= IPCOMP_COMPRESSIONERROR; ++ return skb; ++ } ++ ++ ++ /* Max output size. Result should be max this size. ++ * Implementation specific tweak: ++ * If it's not at least 32 bytes and 6.25% smaller than ++ * the original packet, it's probably not worth wasting ++ * the receiver's CPU cycles decompressing it. ++ * Your mileage may vary. ++ */ ++ cpyldsz = pyldsz - sizeof(struct ipcomphdr) - (pyldsz <= 512 ? 32 : pyldsz >> 4); ++ ++ buffer = kmalloc(cpyldsz, GFP_ATOMIC); ++ if (!buffer) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_compress: " ++ "unable to kmalloc(%d, GFP_ATOMIC), " ++ "skipping compression.\n", ++ cpyldsz); ++ *flags |= IPCOMP_COMPRESSIONERROR; ++ deflateEnd(&zs); ++ return skb; ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { ++ __u8 *c; ++ int i; ++ ++ c = (__u8*)iph + iphlen; ++ for(i = 0; i < pyldsz; i++, c++) { ++ if(!(i % 16)) { ++ printk(KERN_INFO "skb_compress: before:"); ++ } ++ printk("%02x ", *c); ++ if(!((i + 1) % 16)) { ++ printk("\n"); ++ } ++ } ++ if(i % 16) { ++ printk("\n"); ++ } ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ zs.next_in = (char *) iph + iphlen; /* start of payload */ ++ zs.avail_in = pyldsz; ++ zs.next_out = buffer; /* start of compressed payload */ ++ zs.avail_out = cpyldsz; ++ ++ /* Finish compression in one step */ ++ zresult = deflate(&zs, Z_FINISH); ++ ++ /* Free all dynamically allocated buffers */ ++ deflateEnd(&zs); ++ if (zresult != Z_STREAM_END) { ++ *flags |= IPCOMP_UNCOMPRESSABLE; ++ kfree(buffer); ++ ++ /* Adjust adaptive counters */ ++ if (++(ips->ips_comp_adapt_tries) == IPCOMP_ADAPT_INITIAL_TRIES) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "first %d packets didn't compress, " ++ "skipping next %d\n", ++ IPCOMP_ADAPT_INITIAL_TRIES, ++ IPCOMP_ADAPT_INITIAL_SKIP); ++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_INITIAL_SKIP; ++ } ++ else if (ips->ips_comp_adapt_tries == IPCOMP_ADAPT_INITIAL_TRIES + IPCOMP_ADAPT_SUBSEQ_TRIES) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "next %d packets didn't compress, " ++ "skipping next %d\n", ++ IPCOMP_ADAPT_SUBSEQ_TRIES, ++ IPCOMP_ADAPT_SUBSEQ_SKIP); ++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_SUBSEQ_SKIP; ++ ips->ips_comp_adapt_tries = IPCOMP_ADAPT_INITIAL_TRIES; ++ } ++ ++ return skb; ++ } ++ ++ /* resulting compressed size */ ++ cpyldsz -= zs.avail_out; ++ ++ /* Insert IPCOMP header */ ++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_nh = iph->protocol; ++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_flags = 0; ++ /* use the bottom 16 bits of the spi for the cpi. The top 16 bits are ++ for internal reference only. */ ++ ((struct ipcomphdr*) (((char*)iph) + iphlen))->ipcomp_cpi = htons((__u16)(ntohl(ips->ips_said.spi) & 0x0000ffff)); ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_compress: " ++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: raw=%d, comp=%d.\n", ++ ntohl(ips->ips_said.spi), ++ ntohl(ips->ips_said.spi) & 0x0000ffff, ++ ntohs(((struct ipcomphdr*)(((char*)iph)+iphlen))->ipcomp_cpi), ++ pyldsz, ++ cpyldsz); ++ ++ /* Update IP header */ ++ iph->protocol = IPPROTO_COMP; ++ iph->tot_len = htons(iphlen + sizeof(struct ipcomphdr) + cpyldsz); ++#if 1 /* XXX checksum is done by ipsec_tunnel ? */ ++ iph->check = 0; ++ iph->check = ip_fast_csum((char *) iph, iph->ihl); ++#endif ++ ++ /* Copy compressed payload */ ++ memcpy((char *) iph + iphlen + sizeof(struct ipcomphdr), ++ buffer, ++ cpyldsz); ++ kfree(buffer); ++ ++ /* Update skb length/tail by "unputting" the shrinkage */ ++ skb_put(skb, ++ cpyldsz + sizeof(struct ipcomphdr) - pyldsz); ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { ++ __u8 *c; ++ int i; ++ ++ c = (__u8*)iph + iphlen + sizeof(struct ipcomphdr); ++ for(i = 0; i < cpyldsz; i++, c++) { ++ if(!(i % 16)) { ++ printk(KERN_INFO "skb_compress: result:"); ++ } ++ printk("%02x ", *c); ++ if(!((i + 1) % 16)) { ++ printk("\n"); ++ } ++ } ++ if(i % 16) { ++ printk("\n"); ++ } ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ips->ips_comp_adapt_skip = 0; ++ ips->ips_comp_adapt_tries = 0; ++ ++ return skb; ++} ++ ++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags) ++{ ++ struct sk_buff *nskb = NULL; ++ ++ /* original ip header */ ++ struct iphdr *oiph, *iph; ++ unsigned int iphlen, pyldsz, cpyldsz; ++ z_stream zs; ++ int zresult; ++ ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_decompress: .\n"); ++ ++ if(!skb) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "passed in NULL skb, returning ERROR.\n"); ++ if (flags) *flags |= IPCOMP_PARMERROR; ++ return skb; ++ } ++ ++ if(!ips && sysctl_ipsec_inbound_policy_check) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "passed in NULL ipsec_sa needed for comp alg, returning ERROR.\n"); ++ if (flags) *flags |= IPCOMP_PARMERROR; ++ return skb; ++ } ++ ++ if (!flags) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "passed in NULL flags, returning ERROR.\n"); ++ ipsec_kfree_skb(skb); ++ return NULL; ++ } ++ ++#ifdef NET_21 ++ oiph = skb->nh.iph; ++#else /* NET_21 */ ++ oiph = skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ iphlen = oiph->ihl << 2; ++ ++ if (oiph->protocol != IPPROTO_COMP) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "called with non-IPCOMP packet (protocol=%d)," ++ "skipping decompression.\n", ++ oiph->protocol); ++ *flags |= IPCOMP_PARMERROR; ++ return skb; ++ } ++ ++ if ( (((struct ipcomphdr*)((char*) oiph + iphlen))->ipcomp_flags != 0) ++ || ((((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi ++ != htons(SADB_X_CALG_DEFLATE)) ++ && sysctl_ipsec_inbound_policy_check ++ && (!ips || (ips && (ips->ips_encalg != SADB_X_CALG_DEFLATE)))) ) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "called with incompatible IPCOMP packet (flags=%d, " ++ "cpi=%d), ips-compalg=%d, skipping decompression.\n", ++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_flags), ++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi), ++ ips ? ips->ips_encalg : 0); ++ *flags |= IPCOMP_PARMERROR; ++ ++ return skb; ++ } ++ ++ if (ntohs(oiph->frag_off) & ~0x4000) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "called with fragmented IPCOMP packet, " ++ "skipping decompression.\n"); ++ *flags |= IPCOMP_PARMERROR; ++ return skb; ++ } ++ ++ /* original compressed payload size */ ++ cpyldsz = ntohs(oiph->tot_len) - iphlen - sizeof(struct ipcomphdr); ++ ++ zs.zalloc = my_zcalloc; ++ zs.zfree = my_zfree; ++ zs.opaque = 0; ++ ++ zs.next_in = (char *) oiph + iphlen + sizeof(struct ipcomphdr); ++ zs.avail_in = cpyldsz; ++ ++ /* Maybe we should be a bit conservative about memory ++ requirements and use inflateInit2 */ ++ /* Beware, that this might make us unable to decompress packets ++ from other implementations - HINT: check PGPnet source code */ ++ /* We want to use inflateInit2 because we don't want the adler ++ header. */ ++ zresult = inflateInit2(&zs, -15); ++ if (zresult != Z_OK) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "inflateInit2() returned error %d (%s), " ++ "skipping decompression.\n", ++ zresult, ++ zs.msg ? zs.msg : zError(zresult)); ++ *flags |= IPCOMP_DECOMPRESSIONERROR; ++ ++ return skb; ++ } ++ ++ /* We have no way of knowing the exact length of the resulting ++ decompressed output before we have actually done the decompression. ++ For now, we guess that the packet will not be bigger than the ++ attached ipsec device's mtu or 16260, whichever is biggest. ++ This may be wrong, since the sender's mtu may be bigger yet. ++ XXX This must be dealt with later XXX ++ */ ++ ++ /* max payload size */ ++ pyldsz = skb->dev ? (skb->dev->mtu < 16260 ? 16260 : skb->dev->mtu) ++ : (65520 - iphlen); ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_decompress: " ++ "max payload size: %d\n", pyldsz); ++ ++ while (pyldsz > (cpyldsz + sizeof(struct ipcomphdr)) && ++ (nskb = skb_copy_ipcomp(skb, ++ pyldsz - cpyldsz - sizeof(struct ipcomphdr), ++ GFP_ATOMIC)) == NULL) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "unable to skb_copy_ipcomp(skb, %d, GFP_ATOMIC), " ++ "trying with less payload size.\n", ++ (int)(pyldsz - cpyldsz - sizeof(struct ipcomphdr))); ++ pyldsz >>=1; ++ } ++ ++ if (!nskb) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "unable to allocate memory, dropping packet.\n"); ++ *flags |= IPCOMP_DECOMPRESSIONERROR; ++ inflateEnd(&zs); ++ ++ return skb; ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { ++ __u8 *c; ++ int i; ++ ++ c = (__u8*)oiph + iphlen + sizeof(struct ipcomphdr); ++ for(i = 0; i < cpyldsz; i++, c++) { ++ if(!(i % 16)) { ++ printk(KERN_INFO "skb_decompress: before:"); ++ } ++ printk("%02x ", *c); ++ if(!((i + 1) % 16)) { ++ printk("\n"); ++ } ++ } ++ if(i % 16) { ++ printk("\n"); ++ } ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#ifdef NET_21 ++ iph = nskb->nh.iph; ++#else /* NET_21 */ ++ iph = nskb->ip_hdr; ++#endif /* NET_21 */ ++ zs.next_out = (char *)iph + iphlen; ++ zs.avail_out = pyldsz; ++ ++ zresult = inflate(&zs, Z_SYNC_FLUSH); ++ ++ /* work around a bug in zlib, which sometimes wants to taste an extra ++ * byte when being used in the (undocumented) raw deflate mode. ++ */ ++ if (zresult == Z_OK && !zs.avail_in && zs.avail_out) { ++ __u8 zerostuff = 0; ++ ++ zs.next_in = &zerostuff; ++ zs.avail_in = 1; ++ zresult = inflate(&zs, Z_FINISH); ++ } ++ ++ inflateEnd(&zs); ++ if (zresult != Z_STREAM_END) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_error:skb_decompress: " ++ "inflate() returned error %d (%s), " ++ "skipping decompression.\n", ++ zresult, ++ zs.msg ? zs.msg : zError(zresult)); ++ *flags |= IPCOMP_DECOMPRESSIONERROR; ++ ipsec_kfree_skb(nskb); ++ ++ return skb; ++ } ++ ++ /* Update IP header */ ++ /* resulting decompressed size */ ++ pyldsz -= zs.avail_out; ++ iph->tot_len = htons(iphlen + pyldsz); ++ iph->protocol = ((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_nh; ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_decompress: " ++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: comp=%d, raw=%d, nh=%d.\n", ++ ips ? ntohl(ips->ips_said.spi) : 0, ++ ips ? ntohl(ips->ips_said.spi) & 0x0000ffff : 0, ++ ntohs(((struct ipcomphdr*)(((char*)oiph)+iphlen))->ipcomp_cpi), ++ cpyldsz, ++ pyldsz, ++ iph->protocol); ++ ++#if 1 /* XXX checksum is done by ipsec_rcv ? */ ++ iph->check = 0; ++ iph->check = ip_fast_csum((char*) iph, iph->ihl); ++#endif ++ ++ /* Update skb length/tail by "unputting" the unused data area */ ++ skb_put(nskb, -zs.avail_out); ++ ++ ipsec_kfree_skb(skb); ++ ++ if (iph->protocol == IPPROTO_COMP) ++ { ++#ifdef CONFIG_IPSEC_DEBUG ++ if(sysctl_ipsec_debug_ipcomp) ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_decompress: " ++ "Eh? inner packet is also compressed, dropping.\n"); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ipsec_kfree_skb(nskb); ++ return NULL; ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { ++ __u8 *c; ++ int i; ++ ++ c = (__u8*)iph + iphlen; ++ for(i = 0; i < pyldsz; i++, c++) { ++ if(!(i % 16)) { ++ printk(KERN_INFO "skb_decompress: result:"); ++ } ++ printk("%02x ", *c); ++ if(!((i + 1) % 16)) { ++ printk("\n"); ++ } ++ } ++ if(i % 16) { ++ printk("\n"); ++ } ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ return nskb; ++} ++ ++ ++/* this is derived from skb_copy() in linux 2.2.14 */ ++/* May be incompatible with other kernel versions!! */ ++static ++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask) ++{ ++ struct sk_buff *n; ++ struct iphdr *iph; ++ unsigned long offset; ++ unsigned int iphlen; ++ ++ if(!skb) { ++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, ++ "klips_debug:skb_copy_ipcomp: " ++ "passed in NULL skb, returning NULL.\n"); ++ return NULL; ++ } ++ ++ /* ++ * Allocate the copy buffer ++ */ ++ ++#ifdef NET_21 ++ iph = skb->nh.iph; ++#else /* NET_21 */ ++ iph = skb->ip_hdr; ++#endif /* NET_21 */ ++ if (!iph) return NULL; ++ iphlen = iph->ihl << 2; ++ ++ n=alloc_skb(skb->end - skb->head + data_growth, gfp_mask); ++ if(n==NULL) ++ return NULL; ++ ++ /* ++ * Shift between the two data areas in bytes ++ */ ++ ++ offset=n->head-skb->head; ++ ++ /* Set the data pointer */ ++ skb_reserve(n,skb->data-skb->head); ++ /* Set the tail pointer and length */ ++ skb_put(n,skb->len+data_growth); ++ /* Copy the bytes up to and including the ip header */ ++ memcpy(n->head, ++ skb->head, ++ ((char *)iph - (char *)skb->head) + iphlen); ++ n->list=NULL; ++ n->next=NULL; ++ n->prev=NULL; ++ n->sk=NULL; ++ n->dev=skb->dev; ++ if (skb->h.raw) ++ n->h.raw=skb->h.raw+offset; ++ else ++ n->h.raw=NULL; ++ n->protocol=skb->protocol; ++#ifdef NET_21 ++ n->csum = 0; ++ n->priority=skb->priority; ++ n->dst=dst_clone(skb->dst); ++ n->nh.raw=skb->nh.raw+offset; ++#ifndef NETDEV_23 ++ n->is_clone=0; ++#endif /* NETDEV_23 */ ++ atomic_set(&n->users, 1); ++ n->destructor = NULL; ++ n->security=skb->security; ++ memcpy(n->cb, skb->cb, sizeof(skb->cb)); ++#ifdef CONFIG_IP_FIREWALL ++ n->fwmark = skb->fwmark; ++#endif ++#else /* NET_21 */ ++ n->link3=NULL; ++ n->when=skb->when; ++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); ++ n->saddr=skb->saddr; ++ n->daddr=skb->daddr; ++ n->raddr=skb->raddr; ++ n->seq=skb->seq; ++ n->end_seq=skb->end_seq; ++ n->ack_seq=skb->ack_seq; ++ n->acked=skb->acked; ++ n->free=1; ++ n->arp=skb->arp; ++ n->tries=0; ++ n->lock=0; ++ n->users=0; ++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); ++#endif /* NET_21 */ ++ if (skb->mac.raw) ++ n->mac.raw=skb->mac.raw+offset; ++ else ++ n->mac.raw=NULL; ++#ifndef NETDEV_23 ++ n->used=skb->used; ++#endif /* !NETDEV_23 */ ++ n->pkt_type=skb->pkt_type; ++#ifndef NETDEV_23 ++ n->pkt_bridged=skb->pkt_bridged; ++#endif /* NETDEV_23 */ ++ n->ip_summed=0; ++ n->stamp=skb->stamp; ++#ifndef NETDEV_23 /* this seems to have been removed in 2.4 */ ++#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) ++ n->shapelatency=skb->shapelatency; /* Latency on frame */ ++ n->shapeclock=skb->shapeclock; /* Time it should go out */ ++ n->shapelen=skb->shapelen; /* Frame length in clocks */ ++ n->shapestamp=skb->shapestamp; /* Stamp for shaper */ ++ n->shapepend=skb->shapepend; /* Pending */ ++#endif /* defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) */ ++#endif /* NETDEV_23 */ ++#ifdef CONFIG_HIPPI ++ n->private.ifield=skb->private.ifield; ++#endif /* CONFIG_HIPPI */ ++ ++ return n; ++} +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_ah.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,363 @@ ++/* ++ * processing code for AH ++ * Copyright (C) 2003-2004 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_ah_c_version[] = "RCSID $Id: ipsec_ah.c,v 1.2 2004/04/06 02:49:25 mcr Exp $"; ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_xmit.h" ++ ++#include "openswan/ipsec_auth.h" ++ ++#ifdef CONFIG_IPSEC_AH ++#include "openswan/ipsec_ah.h" ++#endif /* CONFIG_IPSEC_AH */ ++ ++#include "openswan/ipsec_proto.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_ah = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++__u32 zeroes[AH_AMAX]; ++ ++#ifdef CONFIG_IPSEC_AH ++enum ipsec_rcv_value ++ipsec_rcv_ah_checks(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb) ++{ ++ int ahminlen; ++ ++ ahminlen = irs->hard_header_len + sizeof(struct iphdr); ++ ++ /* take care not to deref this pointer until we check the minlen though */ ++ irs->protostuff.ahstuff.ahp = (struct ahhdr *) (skb->data + irs->iphlen); ++ ++ if((skb->len < ahminlen+sizeof(struct ahhdr)) || ++ (skb->len < ahminlen+(irs->protostuff.ahstuff.ahp->ah_hl << 2))) { ++ KLIPS_PRINT(debug_rcv & DB_RX_INAU, ++ "klips_debug:ipsec_rcv: " ++ "runt ah packet of skb->len=%d received from %s, dropped.\n", ++ skb->len, ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++ irs->said.spi = irs->protostuff.ahstuff.ahp->ah_spi; ++ ++ /* XXX we only support the one 12-byte authenticator for now */ ++ if(irs->protostuff.ahstuff.ahp->ah_hl != ((AHHMAC_HASHLEN+AHHMAC_RPLLEN) >> 2)) { ++ KLIPS_PRINT(debug_rcv & DB_RX_INAU, ++ "klips_debug:ipsec_rcv: " ++ "bad authenticator length %ld, expected %lu from %s.\n", ++ (long)(irs->protostuff.ahstuff.ahp->ah_hl << 2), ++ (unsigned long) sizeof(struct ahhdr), ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++ return IPSEC_RCV_OK; ++} ++ ++ ++enum ipsec_rcv_value ++ipsec_rcv_ah_setup_auth(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb, ++ __u32 *replay, ++ unsigned char **authenticator) ++{ ++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; ++ ++ *replay = ntohl(ahp->ah_rpl); ++ *authenticator = ahp->ah_data; ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_rcv_value ++ipsec_rcv_ah_authcalc(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb) ++{ ++ struct auth_alg *aa; ++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; ++ union { ++ MD5_CTX md5; ++ SHA1_CTX sha1; ++ } tctx; ++ struct iphdr ipo; ++ int ahhlen; ++ ++ aa = irs->authfuncs; ++ ++ /* copy the initialized keying material */ ++ memcpy(&tctx, irs->ictx, irs->ictx_len); ++ ++ ipo = *irs->ipp; ++ ipo.tos = 0; /* mutable RFC 2402 3.3.3.1.1.1 */ ++ ipo.frag_off = 0; ++ ipo.ttl = 0; ++ ipo.check = 0; ++ ++ ++ /* do the sanitized header */ ++ (*aa->update)((void*)&tctx, (caddr_t)&ipo, sizeof(struct iphdr)); ++ ++ /* XXX we didn't do the options here! */ ++ ++ /* now do the AH header itself */ ++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2); ++ (*aa->update)((void*)&tctx, (caddr_t)ahp, ahhlen - AHHMAC_HASHLEN); ++ ++ /* now, do some zeroes */ ++ (*aa->update)((void*)&tctx, (caddr_t)zeroes, AHHMAC_HASHLEN); ++ ++ /* finally, do the packet contents themselves */ ++ (*aa->update)((void*)&tctx, ++ (caddr_t)skb->data + irs->iphlen + ahhlen, ++ skb->len - irs->iphlen - ahhlen); ++ ++ (*aa->final)(irs->hash, (void *)&tctx); ++ ++ memcpy(&tctx, irs->octx, irs->octx_len); ++ ++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen); ++ (*aa->final)(irs->hash, (void *)&tctx); ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_rcv_value ++ipsec_rcv_ah_decap(struct ipsec_rcv_state *irs) ++{ ++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp; ++ struct sk_buff *skb; ++ int ahhlen; ++ ++ skb=irs->skb; ++ ++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2); ++ ++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - ahhlen); ++ irs->next_header = ahp->ah_nh; ++ ++ /* ++ * move the IP header forward by the size of the AH header, which ++ * will remove the the AH header from the packet. ++ */ ++ memmove((void *)(skb->data + ahhlen), ++ (void *)(skb->data), irs->iphlen); ++ ++ ipsec_rcv_dmp("ah postmove", skb->data, skb->len); ++ ++ /* skb_pull below, will move up by ahhlen */ ++ ++ /* XXX not clear how this can happen, as the message indicates */ ++ if(skb->len < ahhlen) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_rcv: " ++ "tried to skb_pull ahhlen=%d, %d available. This should never happen, please report.\n", ++ ahhlen, ++ (int)(skb->len)); ++ return IPSEC_RCV_DECAPFAIL; ++ } ++ skb_pull(skb, ahhlen); ++ ++ irs->ipp = (struct iphdr *)skb->data; ++ ++ ipsec_rcv_dmp("ah postpull", skb->data, skb->len); ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_ah_setup(struct ipsec_xmit_state *ixs) ++{ ++ struct iphdr ipo; ++ struct ahhdr *ahp; ++ __u8 hash[AH_AMAX]; ++ union { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ MD5_CTX md5; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ SHA1_CTX sha1; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ } tctx; ++ unsigned char *dat = (unsigned char *)ixs->iph; ++ ++ ahp = (struct ahhdr *)(dat + ixs->iphlen); ++ ahp->ah_spi = ixs->ipsp->ips_said.spi; ++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); ++ ahp->ah_rv = 0; ++ ahp->ah_nh = ixs->iph->protocol; ++ ahp->ah_hl = (sizeof(struct ahhdr) >> 2) - sizeof(__u64)/sizeof(__u32); ++ ixs->iph->protocol = IPPROTO_AH; ++ ipsec_xmit_dmp("ahp", (char*)ahp, sizeof(*ahp)); ++ ++ ipo = *ixs->iph; ++ ipo.tos = 0; ++ ipo.frag_off = 0; ++ ipo.ttl = 0; ++ ipo.check = 0; ++ ipsec_xmit_dmp("ipo", (char*)&ipo, sizeof(ipo)); ++ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr)); ++ ipsec_xmit_dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)ahp, ++ sizeof(struct ahhdr) - sizeof(ahp->ah_data)); ++ ipsec_xmit_dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN); ++ ipsec_xmit_dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, dat + ixs->iphlen + sizeof(struct ahhdr), ++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr)); ++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash)); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, hash, AHMD596_ALEN); ++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash)); ++ ++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr)); ++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, sizeof(struct ahhdr) - sizeof(ahp->ah_data)); ++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN); ++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + sizeof(struct ahhdr), ++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr)); ++ SHA1Final(hash, &tctx.sha1); ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); ++ SHA1Final(hash, &tctx.sha1); ++ ++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_AH_BADALG; ++ } ++#ifdef NET_21 ++ ixs->skb->h.raw = (unsigned char*)ahp; ++#endif /* NET_21 */ ++ ++ return IPSEC_XMIT_OK; ++} ++ ++struct xform_functions ah_xform_funcs[]={ ++ { rcv_checks: ipsec_rcv_ah_checks, ++ rcv_setup_auth: ipsec_rcv_ah_setup_auth, ++ rcv_calc_auth: ipsec_rcv_ah_authcalc, ++ rcv_decrypt: ipsec_rcv_ah_decap, ++ ++ xmit_setup: ipsec_xmit_ah_setup, ++ xmit_headroom: sizeof(struct ahhdr), ++ xmit_needtailroom: 0, ++ }, ++}; ++ ++struct inet_protocol ah_protocol = ++{ ++ ipsec_rcv, /* AH handler */ ++ NULL, /* TUNNEL error control */ ++#ifdef NETDEV_25 ++ 1, /* no policy */ ++#else ++ 0, /* next */ ++ IPPROTO_AH, /* protocol ID */ ++ 0, /* copy */ ++ NULL, /* data */ ++ "AH" /* name */ ++#endif ++}; ++ ++#endif /* CONFIG_IPSEC_AH */ ++ ++/* ++ * $Log: ipsec_ah.c,v $ ++ * Revision 1.2 2004/04/06 02:49:25 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_alg.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,953 @@ ++/* ++ * Modular extensions service and registration functions ++ * ++ * Author: JuanJo Ciarlante ++ * ++ * Version: 0.8.1 ++ * ++ * ipsec_alg.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ */ ++#ifdef CONFIG_IPSEC_ALG ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#include ++#include ++#include /* memcmp() */ ++#include /* get_random_bytes() */ ++#include /* error codes */ ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include "openswan/ipsec_param.h" ++#include ++#include "openswan/ipsec_sa.h" ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) ++# include "openswan/ipsec_ah.h" ++#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */ ++#ifdef CONFIG_IPSEC_ESP ++# include "openswan/ipsec_esp.h" ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_IPCOMP ++# include "openswan/ipcomp.h" ++#endif /* CONFIG_IPSEC_COMP */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_alg.h" ++ ++#ifndef CONFIG_IPSEC_ALG ++#error This file _MUST_ be compiled with CONFIG_IPSEC_ALG enabled ! ++#endif ++#if SADB_EALG_MAX < 255 ++#warning Compiling with limited ESP support ( SADB_EALG_MAX < 256 ) ++#endif ++ ++static rwlock_t ipsec_alg_lock = RW_LOCK_UNLOCKED; ++#define IPSEC_ALG_HASHSZ 16 /* must be power of 2, even 2^0=1 */ ++static struct list_head ipsec_alg_hash_table[IPSEC_ALG_HASHSZ]; ++ ++/* Old gcc's will fail here */ ++#define barf_out(fmt, args...) do { printk(KERN_ERR "%s: (%s) " fmt, __FUNCTION__, ixt->ixt_name , ## args)\ ++ ; goto out; } while(0) ++ ++/* ++ * Must be already protected by lock ++ */ ++static void __ipsec_alg_usage_inc(struct ipsec_alg *ixt) { ++ if (ixt->ixt_module) ++ __MOD_INC_USE_COUNT(ixt->ixt_module); ++ atomic_inc(&ixt->ixt_refcnt); ++} ++static void __ipsec_alg_usage_dec(struct ipsec_alg *ixt) { ++ atomic_dec(&ixt->ixt_refcnt); ++ if (ixt->ixt_module) ++ __MOD_DEC_USE_COUNT(ixt->ixt_module); ++} ++/* ++ * simple hash function, optimized for 0-hash (1 list) special ++ * case ++ */ ++#if IPSEC_ALG_HASHSZ > 1 ++static inline unsigned ipsec_alg_hashfn(int alg_type, int alg_id) { ++ return ((alg_type^alg_id)&(IPSEC_ALG_HASHSZ-1)); ++} ++#else ++#define ipsec_alg_hashfn(x,y) (0) ++#endif ++ ++/***************************************************************** ++ * ++ * INTERNAL table handling: insert, delete, find ++ * ++ *****************************************************************/ ++ ++/* ++ * hash table initialization, called from ipsec_alg_init() ++ */ ++static void ipsec_alg_hash_init(void) { ++ struct list_head *head = ipsec_alg_hash_table; ++ int i = IPSEC_ALG_HASHSZ; ++ do { ++ INIT_LIST_HEAD(head); ++ head++; ++ i--; ++ } while (i); ++} ++/* ++ * hash list lookup by {alg_type, alg_id} and table head, ++ * must be already protected by lock ++ */ ++static struct ipsec_alg *__ipsec_alg_find(unsigned alg_type, unsigned alg_id, struct list_head * head) { ++ struct list_head *p; ++ struct ipsec_alg *ixt=NULL; ++ for (p=head->next; p!=head; p=p->next) { ++ ixt = list_entry(p, struct ipsec_alg, ixt_list); ++ if (ixt->ixt_alg_type == alg_type && ixt->ixt_alg_id==alg_id) { ++ goto out; ++ } ++ } ++ ixt=NULL; ++out: ++ return ixt; ++} ++/* ++ * inserts (in front) a new entry in hash table, ++ * called from ipsec_alg_register() when new algorithm is registered. ++ */ ++static int ipsec_alg_insert(struct ipsec_alg *ixt) { ++ int ret=-EINVAL; ++ unsigned hashval=ipsec_alg_hashfn(ixt->ixt_alg_type, ixt->ixt_alg_id); ++ struct list_head *head= ipsec_alg_hash_table + hashval; ++ struct ipsec_alg *ixt_cur; ++ /* new element must be virgin ... */ ++ if (ixt->ixt_list.next != &ixt->ixt_list || ++ ixt->ixt_list.prev != &ixt->ixt_list) { ++ printk(KERN_ERR "ipsec_alg_insert: ixt object \"%s\" " ++ "list head not initialized\n", ++ ixt->ixt_name); ++ return ret; ++ } ++ write_lock_bh(&ipsec_alg_lock); ++ ixt_cur = __ipsec_alg_find(ixt->ixt_alg_type, ixt->ixt_alg_id, head); ++ /* if previous (current) ipsec_alg found check excl flag of _anyone_ */ ++ if (ixt_cur && ((ixt->ixt_state|ixt_cur->ixt_state) & IPSEC_ALG_ST_EXCL)) ++ barf_out("ipsec_alg for alg_type=%d, alg_id=%d already exist. " ++ "Not loaded (ret=%d).\n", ++ ixt->ixt_alg_type, ++ ixt->ixt_alg_id, ret=-EEXIST); ++ list_add(&ixt->ixt_list, head); ++ ixt->ixt_state |= IPSEC_ALG_ST_REGISTERED; ++ ret=0; ++out: ++ write_unlock_bh(&ipsec_alg_lock); ++ return ret; ++} ++/* ++ * deletes an existing entry in hash table, ++ * called from ipsec_alg_unregister() when algorithm is unregistered. ++ */ ++static int ipsec_alg_delete(struct ipsec_alg *ixt) { ++ write_lock_bh(&ipsec_alg_lock); ++ list_del(&ixt->ixt_list); ++ write_unlock_bh(&ipsec_alg_lock); ++ return 0; ++} ++/* ++ * here @user context (read-only when @kernel bh context) ++ * -> no bh disabling ++ * ++ * called from ipsec_sa_init() -> ipsec_alg_sa_init() ++ */ ++static struct ipsec_alg *ipsec_alg_get(int alg_type, int alg_id) { ++ unsigned hashval=ipsec_alg_hashfn(alg_type, alg_id); ++ struct list_head *head= ipsec_alg_hash_table + hashval; ++ struct ipsec_alg *ixt; ++ read_lock(&ipsec_alg_lock); ++ ixt=__ipsec_alg_find(alg_type, alg_id, head); ++ if (ixt) __ipsec_alg_usage_inc(ixt); ++ read_unlock(&ipsec_alg_lock); ++ return ixt; ++} ++ ++static void ipsec_alg_put(struct ipsec_alg *ixt) { ++ __ipsec_alg_usage_dec((struct ipsec_alg *)ixt); ++} ++ ++/***************************************************************** ++ * ++ * INTERFACE for ENC services: key creation, encrypt function ++ * ++ *****************************************************************/ ++ ++/* ++ * main encrypt service entry point ++ * called from ipsec_rcv() with encrypt=IPSEC_ALG_DECRYPT and ++ * ipsec_tunnel_start_xmit with encrypt=IPSEC_ALG_ENCRYPT ++ */ ++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 * idat, int ilen, const __u8 * iv, int encrypt) { ++ int ret; ++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; ++ KLIPS_PRINT(debug_rcv||debug_tunnel, ++ "klips_debug:ipsec_alg_esp_encrypt: " ++ "entering with encalg=%d, ixt_e=%p\n", ++ sa_p->ips_encalg, ixt_e); ++ if (!ixt_e) { ++ KLIPS_PRINT(debug_rcv||debug_tunnel, ++ "klips_debug:ipsec_alg_esp_encrypt: " ++ "NULL ipsec_alg_enc object\n"); ++ return -1; ++ } ++ KLIPS_PRINT(debug_rcv||debug_tunnel, ++ "klips_debug:ipsec_alg_esp_encrypt: " ++ "calling cbc_encrypt encalg=%d " ++ "ips_key_e=%p idat=%p ilen=%d iv=%p, encrypt=%d\n", ++ sa_p->ips_encalg, ++ sa_p->ips_key_e, idat, ilen, iv, encrypt); ++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, sa_p->ips_key_e, idat, ilen, iv, encrypt); ++ KLIPS_PRINT(debug_rcv||debug_tunnel, ++ "klips_debug:ipsec_alg_esp_encrypt: " ++ "returned ret=%d\n", ++ ret); ++ return ret; ++} ++/* ++ * encryption key context creation function ++ * called from pfkey_v2_parser.c:pfkey_ips_init() ++ */ ++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p) { ++ int ret=-EINVAL; ++ int keyminbits, keymaxbits; ++ caddr_t ekp; ++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_enc_key_create: " ++ "entering with encalg=%d ixt_e=%p\n", ++ sa_p->ips_encalg, ixt_e); ++ if (!ixt_e) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_enc_key_create: " ++ "NULL ipsec_alg_enc object\n"); ++ return -EPROTO; ++ } ++ ++ /* ++ * grRRR... DES 7bits jurassic stuff ... f*ckk --jjo ++ */ ++ switch(ixt_e->ixt_alg_id) { ++ case ESP_3DES: ++ keyminbits=keymaxbits=192;break; ++ case ESP_DES: ++ keyminbits=keymaxbits=64;break; ++ default: ++ keyminbits=ixt_e->ixt_keyminbits; ++ keymaxbits=ixt_e->ixt_keymaxbits; ++ } ++ if(sa_p->ips_key_bits_eips_key_bits_e>keymaxbits) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_enc_key_create: " ++ "incorrect encryption key size for id=%d: %d bits -- " ++ "must be between %d,%d bits\n" /*octets (bytes)\n"*/, ++ ixt_e->ixt_alg_id, ++ sa_p->ips_key_bits_e, keyminbits, keymaxbits); ++ ret=-EINVAL; ++ goto ixt_out; ++ } ++ /* save encryption key pointer */ ++ ekp = sa_p->ips_key_e; ++ ++ ++ if (ixt_e->ixt_e_new_key) { ++ sa_p->ips_key_e = ixt_e->ixt_e_new_key(ixt_e, ++ ekp, sa_p->ips_key_bits_e/8); ++ ret = (sa_p->ips_key_e)? 0 : -EINVAL; ++ } else { ++ if((sa_p->ips_key_e = (caddr_t) ++ kmalloc((sa_p->ips_key_e_size = ixt_e->ixt_e_ctx_size), ++ GFP_ATOMIC)) == NULL) { ++ ret=-ENOMEM; ++ goto ixt_out; ++ } ++ /* zero-out key_e */ ++ memset(sa_p->ips_key_e, 0, sa_p->ips_key_e_size); ++ ++ /* I cast here to allow more decoupling in alg module */ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_enc_key_create: about to call:" ++ "set_key(key_e=%p, ekp=%p, key_size=%d)\n", ++ (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); ++ ret = ixt_e->ixt_e_set_key(ixt_e, (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8); ++ } ++ /* paranoid */ ++ memset(ekp, 0, sa_p->ips_key_bits_e/8); ++ kfree(ekp); ++ixt_out: ++ return ret; ++} ++ ++/*************************************************************** ++ * ++ * INTERFACE for AUTH services: key creation, hash functions ++ * ++ ***************************************************************/ ++ ++/* ++ * auth key context creation function ++ * called from pfkey_v2_parser.c:pfkey_ips_init() ++ */ ++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p) { ++ int ret=-EINVAL; ++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; ++ int keyminbits, keymaxbits; ++ unsigned char *akp; ++ unsigned int aks; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_auth_key_create: " ++ "entering with authalg=%d ixt_a=%p\n", ++ sa_p->ips_authalg, ixt_a); ++ if (!ixt_a) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_auth_key_create: " ++ "NULL ipsec_alg_auth object\n"); ++ return -EPROTO; ++ } ++ keyminbits=ixt_a->ixt_keyminbits; ++ keymaxbits=ixt_a->ixt_keymaxbits; ++ if(sa_p->ips_key_bits_aips_key_bits_a>keymaxbits) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_alg_auth_key_create: incorrect auth" ++ "key size: %d bits -- must be between %d,%d bits\n"/*octets (bytes)\n"*/, ++ sa_p->ips_key_bits_a, keyminbits, keymaxbits); ++ ret=-EINVAL; ++ goto ixt_out; ++ } ++ /* save auth key pointer */ ++ sa_p->ips_auth_bits = ixt_a->ixt_a_keylen * 8; /* XXX XXX */ ++ akp = sa_p->ips_key_a; ++ aks = sa_p->ips_key_a_size; ++ ++ /* will hold: 2 ctx and a blocksize buffer: kb */ ++ sa_p->ips_key_a_size = ixt_a->ixt_a_ctx_size; ++ if((sa_p->ips_key_a = ++ (caddr_t) kmalloc(sa_p->ips_key_a_size, GFP_ATOMIC)) == NULL) { ++ ret=-ENOMEM; ++ goto ixt_out; ++ } ++ ixt_a->ixt_a_hmac_set_key(ixt_a, sa_p->ips_key_a, akp, sa_p->ips_key_bits_a/8); /* XXX XXX */ ++ ret=0; ++ memset(akp, 0, aks); ++ kfree(akp); ++ ++ixt_out: ++ return ret; ++} ++ ++ ++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) { ++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth; ++ if (!ixt_a) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_sa_esp_hash: " ++ "NULL ipsec_alg_auth object\n"); ++ return -EPROTO; ++ } ++ KLIPS_PRINT(debug_tunnel|debug_rcv, ++ "klips_debug:ipsec_sa_esp_hash: " ++ "hashing %p (%d bytes) to %p (%d bytes)\n", ++ espp, len, ++ hash, hashlen); ++ ixt_a->ixt_a_hmac_hash(ixt_a, ++ sa_p->ips_key_a, ++ espp, len, ++ hash, hashlen); ++ return 0; ++} ++ ++/*************************************************************** ++ * ++ * INTERFACE for module loading,testing, and unloading ++ * ++ ***************************************************************/ ++ ++/* validation for registering (enc) module */ ++static int check_enc(struct ipsec_alg_enc *ixt) { ++ int ret=-EINVAL; ++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_EALG_MAX) ++ barf_out("invalid alg_id=%d >= %d\n", ixt->ixt_alg_id, SADB_EALG_MAX); ++ if (ixt->ixt_blocksize==0) /* || ixt->ixt_blocksize%2) need for ESP_NULL */ ++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); ++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_e_keylen==0) ++ goto zero_key_ok; ++ if (ixt->ixt_keyminbits==0) ++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); ++ if (ixt->ixt_keymaxbits==0) ++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); ++ if (ixt->ixt_e_keylen==0) ++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_e_keylen); ++zero_key_ok: ++ if (ixt->ixt_e_ctx_size==0 && ixt->ixt_e_new_key == NULL) ++ barf_out(KERN_ERR "invalid key_e_size=%d and ixt_e_new_key=NULL\n", ixt->ixt_e_ctx_size); ++ if (ixt->ixt_e_cbc_encrypt==NULL) ++ barf_out(KERN_ERR "e_cbc_encrypt() must be not NULL\n"); ++ ret=0; ++out: ++ return ret; ++} ++ ++/* validation for registering (auth) module */ ++static int check_auth(struct ipsec_alg_auth *ixt) ++{ ++ int ret=-EINVAL; ++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_AALG_MAX) ++ barf_out("invalid alg_id=%d > %d (SADB_AALG_MAX)\n", ixt->ixt_alg_id, SADB_AALG_MAX); ++ if (ixt->ixt_blocksize==0 || ixt->ixt_blocksize%2) ++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize); ++ if (ixt->ixt_blocksize>AH_BLKLEN_MAX) ++ barf_out(KERN_ERR "sorry blocksize=%d > %d. " ++ "Please increase AH_BLKLEN_MAX and recompile\n", ++ ixt->ixt_blocksize, ++ AH_BLKLEN_MAX); ++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_a_keylen==0) ++ goto zero_key_ok; ++ if (ixt->ixt_keyminbits==0) ++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits); ++ if (ixt->ixt_keymaxbits==0) ++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits); ++ if (ixt->ixt_keymaxbits!=ixt->ixt_keyminbits) ++ barf_out(KERN_ERR "keymaxbits must equal keyminbits (not sure).\n"); ++ if (ixt->ixt_a_keylen==0) ++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_a_keylen); ++zero_key_ok: ++ if (ixt->ixt_a_ctx_size==0) ++ barf_out(KERN_ERR "invalid a_ctx_size=%d\n", ixt->ixt_a_ctx_size); ++ if (ixt->ixt_a_hmac_set_key==NULL) ++ barf_out(KERN_ERR "a_hmac_set_key() must be not NULL\n"); ++ if (ixt->ixt_a_hmac_hash==NULL) ++ barf_out(KERN_ERR "a_hmac_hash() must be not NULL\n"); ++ ret=0; ++out: ++ return ret; ++} ++ ++/* ++ * Generic (enc, auth) registration entry point ++ */ ++int register_ipsec_alg(struct ipsec_alg *ixt) { ++ int ret=-EINVAL; ++ /* Validation */ ++ if (ixt==NULL) ++ barf_out("NULL ipsec_alg object passed\n"); ++ if ((ixt->ixt_version&0xffffff00) != (IPSEC_ALG_VERSION&0xffffff00)) ++ barf_out("incorrect version: %d.%d.%d-%d, " ++ "must be %d.%d.%d[-%d]\n", ++ IPSEC_ALG_VERSION_QUAD(ixt->ixt_version), ++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION)); ++ switch(ixt->ixt_alg_type) { ++ case IPSEC_ALG_TYPE_AUTH: ++ if ((ret=check_auth((struct ipsec_alg_auth *)ixt)<0)) ++ goto out; ++ break; ++ case IPSEC_ALG_TYPE_ENCRYPT: ++ if ((ret=check_enc((struct ipsec_alg_enc *)ixt)<0)) ++ goto out; ++ /* ++ * Adapted two lines below: ++ * ivlen == 0 is possible (NULL enc has blocksize==1) ++ * ++ * fixed NULL support by David De Reu ++ */ ++ if (ixt->ixt_ivlen == 0 && ixt->ixt_blocksize > 1) ++ ixt->ixt_ivlen = ixt->ixt_blocksize*8; ++ break; ++ default: ++ barf_out("alg_type=%d not supported\n", ixt->ixt_alg_type); ++ } ++ INIT_LIST_HEAD(&ixt->ixt_list); ++ ret = ipsec_alg_insert(ixt); ++ if (ret<0) ++ barf_out(KERN_WARNING "ipsec_alg for alg_id=%d failed." ++ "Not loaded (ret=%d).\n", ++ ixt->ixt_alg_id, ret); ++ ++ ret = pfkey_list_insert_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); ++ if (ret==0) { ++ ixt->ixt_state |= IPSEC_ALG_ST_SUPP; ++ /* send register event to userspace */ ++ pfkey_register_reply(SADB_SATYPE_ESP, NULL); ++ } else ++ printk(KERN_ERR "pfkey_list_insert_supported returned %d. " ++ "Loading anyway.\n", ret); ++ ret=0; ++out: ++ return ret; ++} ++ ++/* ++ * unregister ipsec_alg object from own tables, if ++ * success => calls pfkey_list_remove_supported() ++ */ ++int unregister_ipsec_alg(struct ipsec_alg *ixt) { ++ int ret= -EINVAL; ++ switch(ixt->ixt_alg_type) { ++ case IPSEC_ALG_TYPE_AUTH: ++ case IPSEC_ALG_TYPE_ENCRYPT: ++ break; ++ default: ++ /* this is not a typo :) */ ++ barf_out("frog found in list (\"%s\"): ixt_p=NULL\n", ++ ixt->ixt_name); ++ } ++ ++ ret=ipsec_alg_delete(ixt); ++ if (ixt->ixt_state&IPSEC_ALG_ST_SUPP) { ++ ixt->ixt_state &= ~IPSEC_ALG_ST_SUPP; ++ pfkey_list_remove_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP])); ++ /* send register event to userspace */ ++ pfkey_register_reply(SADB_SATYPE_ESP, NULL); ++ } ++ ++out: ++ return ret; ++} ++ ++/* ++ * Must be called from user context ++ * used at module load type for testing algo implementation ++ */ ++static int ipsec_alg_test_encrypt(int enc_alg, int test) { ++ int ret; ++ caddr_t buf = NULL; ++ int iv_size, keysize, key_e_size; ++ struct ipsec_alg_enc *ixt_e; ++ void *tmp_key_e = NULL; ++ #define BUFSZ 1024 ++ #define MARGIN 0 ++ #define test_enc (buf+MARGIN) ++ #define test_dec (test_enc+BUFSZ+MARGIN) ++ #define test_tmp (test_dec+BUFSZ+MARGIN) ++ #define test_key_e (test_tmp+BUFSZ+MARGIN) ++ #define test_iv (test_key_e+key_e_size+MARGIN) ++ #define test_key (test_iv+iv_size+MARGIN) ++ #define test_size (BUFSZ*3+key_e_size+iv_size+keysize+MARGIN*7) ++ ixt_e=(struct ipsec_alg_enc *)ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, enc_alg); ++ if (ixt_e==NULL) { ++ KLIPS_PRINT(1, ++ "klips_debug: ipsec_alg_test_encrypt: " ++ "encalg=%d object not found\n", ++ enc_alg); ++ ret=-EINVAL; ++ goto out; ++ } ++ iv_size=ixt_e->ixt_ivlen / 8; ++ key_e_size=ixt_e->ixt_e_ctx_size; ++ keysize=ixt_e->ixt_e_keylen; ++ KLIPS_PRINT(1, ++ "klips_debug: ipsec_alg_test_encrypt: " ++ "enc_alg=%d blocksize=%d key_e_size=%d keysize=%d\n", ++ enc_alg, iv_size, key_e_size, keysize); ++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { ++ ret= -ENOMEM; ++ goto out; ++ } ++ get_random_bytes(test_key, keysize); ++ get_random_bytes(test_iv, iv_size); ++ if (ixt_e->ixt_e_new_key) { ++ tmp_key_e = ixt_e->ixt_e_new_key(ixt_e, test_key, keysize); ++ ret = tmp_key_e ? 0 : -EINVAL; ++ } else { ++ tmp_key_e = test_key_e; ++ ret = ixt_e->ixt_e_set_key(ixt_e, test_key_e, test_key, keysize); ++ } ++ if (ret < 0) ++ goto out; ++ get_random_bytes(test_enc, BUFSZ); ++ memcpy(test_tmp, test_enc, BUFSZ); ++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_enc, BUFSZ, test_iv, 1); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_encrypt: " ++ "cbc_encrypt=1 ret=%d\n", ++ ret); ++ ret=memcmp(test_enc, test_tmp, BUFSZ); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_encrypt: " ++ "memcmp(enc, tmp) ret=%d: %s\n", ret, ++ ret!=0? "OK. (encr->DIFFers)" : "FAIL! (encr->SAME)" ); ++ memcpy(test_dec, test_enc, BUFSZ); ++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_dec, BUFSZ, test_iv, 0); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_encrypt: " ++ "cbc_encrypt=0 ret=%d\n", ret); ++ ret=memcmp(test_dec, test_tmp, BUFSZ); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_encrypt: " ++ "memcmp(dec,tmp) ret=%d: %s\n", ret, ++ ret==0? "OK. (encr->decr->SAME)" : "FAIL! (encr->decr->DIFFers)" ); ++ { ++ /* Shamelessly taken from drivers/md sources O:) */ ++ unsigned long now; ++ int i, count, max=0; ++ int encrypt, speed; ++ for (encrypt=0; encrypt <2;encrypt ++) { ++ for (i = 0; i < 5; i++) { ++ now = jiffies; ++ count = 0; ++ while (jiffies == now) { ++ mb(); ++ ixt_e->ixt_e_cbc_encrypt(ixt_e, ++ tmp_key_e, test_tmp, ++ BUFSZ, test_iv, encrypt); ++ mb(); ++ count++; ++ mb(); ++ } ++ if (count > max) ++ max = count; ++ } ++ speed = max * (HZ * BUFSZ / 1024); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_encrypt: " ++ "%s %s speed=%d KB/s\n", ++ ixt_e->ixt_name, ++ encrypt? "encrypt": "decrypt", speed); ++ } ++ } ++out: ++ if (tmp_key_e && ixt_e->ixt_e_destroy_key) ixt_e->ixt_e_destroy_key(ixt_e, tmp_key_e); ++ if (buf) kfree(buf); ++ if (ixt_e) ipsec_alg_put((struct ipsec_alg *)ixt_e); ++ return ret; ++ #undef test_enc ++ #undef test_dec ++ #undef test_tmp ++ #undef test_key_e ++ #undef test_iv ++ #undef test_key ++ #undef test_size ++} ++ ++/* ++ * Must be called from user context ++ * used at module load type for testing algo implementation ++ */ ++static int ipsec_alg_test_auth(int auth_alg, int test) { ++ int ret; ++ caddr_t buf = NULL; ++ int blocksize, keysize, key_a_size; ++ struct ipsec_alg_auth *ixt_a; ++ #define BUFSZ 1024 ++ #define MARGIN 0 ++ #define test_auth (buf+MARGIN) ++ #define test_key_a (test_auth+BUFSZ+MARGIN) ++ #define test_key (test_key_a+key_a_size+MARGIN) ++ #define test_hash (test_key+keysize+MARGIN) ++ #define test_size (BUFSZ+key_a_size+keysize+AHHMAC_HASHLEN+MARGIN*4) ++ ixt_a=(struct ipsec_alg_auth *)ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, auth_alg); ++ if (ixt_a==NULL) { ++ KLIPS_PRINT(1, ++ "klips_debug: ipsec_alg_test_auth: " ++ "encalg=%d object not found\n", ++ auth_alg); ++ ret=-EINVAL; ++ goto out; ++ } ++ blocksize=ixt_a->ixt_blocksize; ++ key_a_size=ixt_a->ixt_a_ctx_size; ++ keysize=ixt_a->ixt_a_keylen; ++ KLIPS_PRINT(1, ++ "klips_debug: ipsec_alg_test_auth: " ++ "auth_alg=%d blocksize=%d key_a_size=%d keysize=%d\n", ++ auth_alg, blocksize, key_a_size, keysize); ++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) { ++ ret= -ENOMEM; ++ goto out; ++ } ++ get_random_bytes(test_key, keysize); ++ ret = ixt_a->ixt_a_hmac_set_key(ixt_a, test_key_a, test_key, keysize); ++ if (ret < 0 ) ++ goto out; ++ get_random_bytes(test_auth, BUFSZ); ++ ret=ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_auth: " ++ "ret=%d\n", ret); ++ { ++ /* Shamelessly taken from drivers/md sources O:) */ ++ unsigned long now; ++ int i, count, max=0; ++ int speed; ++ for (i = 0; i < 5; i++) { ++ now = jiffies; ++ count = 0; ++ while (jiffies == now) { ++ mb(); ++ ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN); ++ mb(); ++ count++; ++ mb(); ++ } ++ if (count > max) ++ max = count; ++ } ++ speed = max * (HZ * BUFSZ / 1024); ++ printk(KERN_INFO ++ "klips_info: ipsec_alg_test_auth: " ++ "%s hash speed=%d KB/s\n", ++ ixt_a->ixt_name, ++ speed); ++ } ++out: ++ if (buf) kfree(buf); ++ if (ixt_a) ipsec_alg_put((struct ipsec_alg *)ixt_a); ++ return ret; ++ #undef test_auth ++ #undef test_key_a ++ #undef test_key ++ #undef test_hash ++ #undef test_size ++} ++ ++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int test) { ++ switch(alg_type) { ++ case IPSEC_ALG_TYPE_ENCRYPT: ++ return ipsec_alg_test_encrypt(alg_id, test); ++ break; ++ case IPSEC_ALG_TYPE_AUTH: ++ return ipsec_alg_test_auth(alg_id, test); ++ break; ++ } ++ printk(KERN_ERR "klips_info: ipsec_alg_test() called incorrectly: " ++ "alg_type=%d alg_id=%d\n", ++ alg_type, alg_id); ++ return -EINVAL; ++} ++ ++int ipsec_alg_init(void) { ++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " ++ "KLIPS alg v=%d.%d.%d-%d (EALG_MAX=%d, AALG_MAX=%d)\n", ++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION), ++ SADB_EALG_MAX, SADB_AALG_MAX); ++ /* Initialize tables */ ++ write_lock_bh(&ipsec_alg_lock); ++ ipsec_alg_hash_init(); ++ write_unlock_bh(&ipsec_alg_lock); ++ ++ /* Initialize static algos */ ++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: " ++ "calling ipsec_alg_static_init()\n"); ++ ++ /* If we are suppose to use our AES, and don't have CryptoAPI enabled... */ ++#if defined(CONFIG_IPSEC_ENC_AES) && CONFIG_IPSEC_ENC_AES && !defined(CONFIG_IPSEC_ENC_AES_MODULE) && !CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE) ++ { ++ extern int ipsec_aes_init(void); ++ ipsec_aes_init(); ++ } ++#endif ++ ++ /* If we are doing CryptoAPI, then init */ ++#if defined(CONFIG_IPSEC_ENC_CRYPTOAPI) && CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE) ++ { ++ extern int ipsec_cryptoapi_init(void); ++ ipsec_cryptoapi_init(); ++ } ++#endif ++ ++ ++ return 0; ++} ++ ++/********************************************** ++ * ++ * INTERFACE for ipsec_sa init and wipe ++ * ++ **********************************************/ ++ ++/* ++ * Called from pluto -> pfkey_v2_parser.c:pfkey_ipsec_sa_init() ++ */ ++int ipsec_alg_sa_init(struct ipsec_sa *sa_p) { ++ struct ipsec_alg_enc *ixt_e; ++ struct ipsec_alg_auth *ixt_a; ++ ++ /* Only ESP for now ... */ ++ if (sa_p->ips_said.proto != IPPROTO_ESP) ++ return -EPROTONOSUPPORT; ++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_init() :" ++ "entering for encalg=%d, authalg=%d\n", ++ sa_p->ips_encalg, sa_p->ips_authalg); ++ if ((ixt_e=(struct ipsec_alg_enc *) ++ ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, sa_p->ips_encalg))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug: ipsec_alg_sa_init() :" ++ "found ipsec_alg (ixt_e=%p) for encalg=%d\n", ++ ixt_e, sa_p->ips_encalg); ++ sa_p->ips_alg_enc=ixt_e; ++ } ++ if ((ixt_a=(struct ipsec_alg_auth *) ++ ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, sa_p->ips_authalg))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug: ipsec_alg_sa_init() :" ++ "found ipsec_alg (ixt_a=%p) for auth=%d\n", ++ ixt_a, sa_p->ips_authalg); ++ sa_p->ips_alg_auth=ixt_a; ++ } ++ return 0; ++} ++ ++/* ++ * Called from pluto -> ipsec_sa.c:ipsec_sa_delchain() ++ */ ++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p) { ++ struct ipsec_alg *ixt; ++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_enc)) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" ++ "unlinking for encalg=%d\n", ++ ixt->ixt_alg_id); ++ ipsec_alg_put(ixt); ++ } ++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_auth)) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :" ++ "unlinking for authalg=%d\n", ++ ixt->ixt_alg_id); ++ ipsec_alg_put(ixt); ++ } ++ return 0; ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_xform_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ int len = 0; ++ off_t begin = 0; ++ int i; ++ struct list_head *head; ++ struct ipsec_alg *ixt; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_tncfg_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ for(i = 0, head = ipsec_alg_hash_table; i< IPSEC_ALG_HASHSZ; i++, head++) ++ { ++ struct list_head *p; ++ for (p=head->next; p!=head; p=p->next) ++ { ++ ixt = list_entry(p, struct ipsec_alg, ixt_list); ++ len += ipsec_snprintf(buffer+len, length-len, ++ "VERSION=%d TYPE=%d ID=%d NAME=%s REFCNT=%d ", ++ ixt->ixt_version, ixt->ixt_alg_type, ixt->ixt_alg_id, ++ ixt->ixt_name, ixt->ixt_refcnt); ++ ++ len += ipsec_snprintf(buffer+len, length-len, ++ "STATE=%08x BLOCKSIZE=%d IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", ++ ixt->ixt_state, ixt->ixt_blocksize, ++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); ++ ++ len += ipsec_snprintf(buffer+len, length-len, ++ "IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ", ++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits); ++ ++ switch(ixt->ixt_alg_type) ++ { ++ case IPSEC_ALG_TYPE_AUTH: ++ { ++ struct ipsec_alg_auth *auth = (struct ipsec_alg_auth *)ixt; ++ ++ len += ipsec_snprintf(buffer+len, length-len, ++ "KEYLEN=%d CTXSIZE=%d AUTHLEN=%d ", ++ auth->ixt_a_keylen, auth->ixt_a_ctx_size, ++ auth->ixt_a_authlen); ++ break; ++ } ++ case IPSEC_ALG_TYPE_ENCRYPT: ++ { ++ struct ipsec_alg_enc *enc = (struct ipsec_alg_enc *)ixt; ++ len += ipsec_snprintf(buffer+len, length-len, ++ "KEYLEN=%d CTXSIZE=%d ", ++ enc->ixt_e_keylen, enc->ixt_e_ctx_size); ++ ++ break; ++ } ++ } ++ ++ len += ipsec_snprintf(buffer+len, length-len, "\n"); ++ } ++ } ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ len -= (offset - begin); /* Start slop */ ++ if (len > length) ++ len = length; ++ return len; ++} ++ ++ ++/* ++ * As the author of this module, I ONLY ALLOW using it from ++ * GPL (or same LICENSE TERMS as kernel source) modules. ++ * ++ * In respect to hardware crypto engines this means: ++ * * Closed-source device drivers ARE NOT ALLOWED to use ++ * this interface. ++ * * Closed-source VHDL/Verilog firmware running on ++ * the crypto hardware device IS ALLOWED to use this interface ++ * via a GPL (or same LICENSE TERMS as kernel source) device driver. ++ * --Juan Jose Ciarlante 20/03/2002 (thanks RGB for the correct wording) ++ */ ++ ++/* ++ * These symbols can only be used from GPL modules ++ * for now, I'm disabling this because it creates false ++ * symbol problems for old modutils. ++ */ ++ ++/* #ifndef EXPORT_SYMBOL_GPL */ ++#undef EXPORT_SYMBOL_GPL ++#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL ++/* #endif */ ++EXPORT_SYMBOL_GPL(register_ipsec_alg); ++EXPORT_SYMBOL_GPL(unregister_ipsec_alg); ++EXPORT_SYMBOL_GPL(ipsec_alg_test); ++#endif /* CONFIG_IPSEC_ALG */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,263 @@ ++/* ++ * ipsec_alg AES cipher stubs ++ * ++ * Author: JuanJo Ciarlante ++ * ++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * Fixes by: ++ * PK: Pawel Krawczyk ++ * Fixes list: ++ * PK: make XCBC comply with latest draft (keylength) ++ * ++ */ ++#include ++#include ++ ++/* ++ * special case: ipsec core modular with this static algo inside: ++ * must avoid MODULE magic for this file ++ */ ++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES ++#undef MODULE ++#endif ++ ++#include ++#include ++ ++#include /* printk() */ ++#include /* error codes */ ++#include /* size_t */ ++#include ++ ++/* Check if __exit is defined, if not null it */ ++#ifndef __exit ++#define __exit ++#endif ++ ++/* Low freeswan header coupling */ ++#include "openswan/ipsec_alg.h" ++#include "crypto/aes_cbc.h" ++ ++#define CONFIG_IPSEC_ALG_AES_MAC 1 ++ ++#define AES_CONTEXT_T aes_context ++MODULE_AUTHOR("JuanJo Ciarlante "); ++static int debug=0; ++MODULE_PARM(debug, "i"); ++static int test=0; ++MODULE_PARM(test, "i"); ++static int excl=0; ++MODULE_PARM(excl, "i"); ++static int keyminbits=0; ++MODULE_PARM(keyminbits, "i"); ++static int keymaxbits=0; ++MODULE_PARM(keymaxbits, "i"); ++ ++#if CONFIG_IPSEC_ALG_AES_MAC ++#include "crypto/aes_xcbc_mac.h" ++ ++/* ++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt). ++ * We use 9 for non-modular algorithm and none for modular, thus ++ * forcing user to specify one on module load. -kravietz ++ */ ++#ifdef MODULE ++static int auth_id=0; ++#else ++static int auth_id=9; ++#endif ++MODULE_PARM(auth_id, "i"); ++#endif ++ ++#define ESP_AES 12 /* truely _constant_ :) */ ++ ++/* 128, 192 or 256 */ ++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */ ++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */ ++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */ ++ ++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt ++ * -kravietz ++ */ ++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */ ++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */ ++ ++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) { ++ int ret; ++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; ++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL; ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_aes_set_key:" ++ "ret=%d key_e=%p key=%p keysize=%d\n", ++ ret, key_e, key, keysize); ++ return ret; ++} ++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { ++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e; ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:" ++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n", ++ key_e, in, ilen, iv, encrypt); ++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt); ++} ++#if CONFIG_IPSEC_ALG_AES_MAC ++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) { ++ aes_context_mac *ctxm=(aes_context_mac *)key_a; ++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL; ++} ++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) { ++ int ret; ++ char hash_buf[16]; ++ aes_context_mac *ctxm=(aes_context_mac *)key_a; ++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf); ++ memcpy(hash, hash_buf, hashlen); ++ return ret; ++} ++static struct ipsec_alg_auth ipsec_alg_AES_MAC = { ++ ixt_version: IPSEC_ALG_VERSION, ++ ixt_module: THIS_MODULE, ++ ixt_refcnt: ATOMIC_INIT(0), ++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH, ++ ixt_alg_id: 0, ++ ixt_name: "aes_mac", ++ ixt_blocksize: ESP_AES_MAC_BLK_LEN, ++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8, ++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8, ++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ, ++ ixt_a_ctx_size: sizeof(aes_context_mac), ++ ixt_a_hmac_set_key: _aes_mac_set_key, ++ ixt_a_hmac_hash:_aes_mac_hash, ++}; ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++static struct ipsec_alg_enc ipsec_alg_AES = { ++ ixt_version: IPSEC_ALG_VERSION, ++ ixt_module: THIS_MODULE, ++ ixt_refcnt: ATOMIC_INIT(0), ++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT, ++ ixt_alg_id: ESP_AES, ++ ixt_name: "aes", ++ ixt_blocksize: ESP_AES_CBC_BLK_LEN, ++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8, ++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8, ++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX, ++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T), ++ ixt_e_set_key: _aes_set_key, ++ ixt_e_cbc_encrypt:_aes_cbc_encrypt, ++}; ++ ++#if defined(CONFIG_IPSEC_ENC_AES_MODULE) ++IPSEC_ALG_MODULE_INIT_MOD( ipsec_aes_init ) ++#else ++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_aes_init ) ++#endif ++{ ++ int ret, test_ret; ++ ++ if (keyminbits) ++ ipsec_alg_AES.ixt_keyminbits=keyminbits; ++ if (keymaxbits) { ++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits; ++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits) ++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8; ++ } ++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL; ++ ret=register_ipsec_alg_enc(&ipsec_alg_AES); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ ipsec_alg_AES.ixt_name, ++ ret); ++ if (ret==0 && test) { ++ test_ret=ipsec_alg_test( ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ test); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", ++ ipsec_alg_AES.ixt_alg_type, ++ ipsec_alg_AES.ixt_alg_id, ++ test_ret); ++ } ++#if CONFIG_IPSEC_ALG_AES_MAC ++ if (auth_id!=0){ ++ int ret; ++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id; ++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n", ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ ipsec_alg_AES_MAC.ixt_name, ++ ret); ++ if (ret==0 && test) { ++ test_ret=ipsec_alg_test( ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ test); ++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n", ++ ipsec_alg_AES_MAC.ixt_alg_type, ++ ipsec_alg_AES_MAC.ixt_alg_id, ++ test_ret); ++ } ++ } else { ++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id); ++ } ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++ return ret; ++} ++ ++#if defined(CONFIG_IPSEC_ENC_AES_MODULE) ++IPSEC_ALG_MODULE_EXIT_MOD( ipsec_aes_fini ) ++#else ++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_aes_fini ) ++#endif ++{ ++#if CONFIG_IPSEC_ALG_AES_MAC ++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC); ++#endif /* CONFIG_IPSEC_ALG_AES_MAC */ ++ unregister_ipsec_alg_enc(&ipsec_alg_AES); ++ return; ++} ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++#if 0+NOT_YET ++#ifndef MODULE ++/* ++ * This is intended for static module setups, currently ++ * doesn't work for modular ipsec.o with static algos inside ++ */ ++static int setup_keybits(const char *str) ++{ ++ unsigned aux; ++ char *end; ++ ++ aux = simple_strtoul(str,&end,0); ++ if (aux != 128 && aux != 192 && aux != 256) ++ return 0; ++ keyminbits = aux; ++ ++ if (*end == 0 || *end != ',') ++ return 1; ++ str=end+1; ++ aux = simple_strtoul(str, NULL, 0); ++ if (aux != 128 && aux != 192 && aux != 256) ++ return 0; ++ if (aux >= keyminbits) ++ keymaxbits = aux; ++ return 1; ++} ++__setup("ipsec_aes_keybits=", setup_keybits); ++#endif ++#endif ++EXPORT_NO_SYMBOLS; +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,421 @@ ++/* ++ * ipsec_alg to linux cryptoapi GLUE ++ * ++ * Authors: CODE.ar TEAM ++ * Harpo MAxx ++ * JuanJo Ciarlante ++ * Luciano Ruete ++ * ++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * Example usage: ++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos) ++ * modprobe ipsec_cryptoapi ++ * modprobe ipsec_cryptoapi test=1 ++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo) ++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers) ++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens) ++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES) ++ */ ++#include ++#include ++ ++/* ++ * special case: ipsec core modular with this static algo inside: ++ * must avoid MODULE magic for this file ++ */ ++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI ++#undef MODULE ++#endif ++ ++#include ++#include ++ ++#include /* printk() */ ++#include /* error codes */ ++#include /* size_t */ ++#include ++ ++/* Check if __exit is defined, if not null it */ ++#ifndef __exit ++#define __exit ++#endif ++ ++/* warn the innocent */ ++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE) ++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x" ++#define NO_CRYPTOAPI_SUPPORT ++#endif ++/* Low Openswan header coupling */ ++#include "openswan/ipsec_alg.h" ++ ++#include ++#ifdef CRYPTO_API_VERSION_CODE ++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported" ++#define NO_CRYPTOAPI_SUPPORT ++#endif ++ ++#ifdef NO_CRYPTOAPI_SUPPORT ++#warning "Building an unusable module :P" ++/* Catch old CryptoAPI by not allowing module to load */ ++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init ) ++{ ++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n"); ++ return -EINVAL; ++} ++#else ++#include ++#include ++#include ++ ++#define CIPHERNAME_AES "aes" ++#define CIPHERNAME_3DES "des3_ede" ++#define CIPHERNAME_BLOWFISH "blowfish" ++#define CIPHERNAME_CAST "cast5" ++#define CIPHERNAME_SERPENT "serpent" ++#define CIPHERNAME_TWOFISH "twofish" ++ ++#define ESP_3DES 3 ++#define ESP_AES 12 ++#define ESP_BLOWFISH 7 /* truely _constant_ :) */ ++#define ESP_CAST 6 /* quite constant :) */ ++#define ESP_SERPENT 252 /* from ipsec drafts */ ++#define ESP_TWOFISH 253 /* from ipsec drafts */ ++ ++#define AH_MD5 2 ++#define AH_SHA 3 ++#define DIGESTNAME_MD5 "md5" ++#define DIGESTNAME_SHA1 "sha1" ++ ++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete"); ++static int debug=0; ++MODULE_PARM(debug, "i"); ++static int test=0; ++MODULE_PARM(test, "i"); ++static int excl=0; ++MODULE_PARM(excl, "i"); ++ ++static int noauto = 0; ++MODULE_PARM(noauto,"i"); ++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones"); ++ ++static int des_ede3[] = {-1, -1}; ++static int aes[] = {-1, -1}; ++static int blowfish[] = {-1, -1}; ++static int cast[] = {-1, -1}; ++static int serpent[] = {-1, -1}; ++static int twofish[] = {-1, -1}; ++ ++MODULE_PARM(des_ede3,"1-2i"); ++MODULE_PARM(aes,"1-2i"); ++MODULE_PARM(blowfish,"1-2i"); ++MODULE_PARM(cast,"1-2i"); ++MODULE_PARM(serpent,"1-2i"); ++MODULE_PARM(twofish,"1-2i"); ++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse"); ++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens"); ++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens"); ++ ++struct ipsec_alg_capi_cipher { ++ const char *ciphername; /* cryptoapi's ciphername */ ++ unsigned blocksize; ++ unsigned short minbits; ++ unsigned short maxbits; ++ int *parm; /* lkm param for this cipher */ ++ struct ipsec_alg_enc alg; /* note it's not a pointer */ ++}; ++static struct ipsec_alg_capi_cipher alg_capi_carray[] = { ++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }}, ++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }}, ++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }}, ++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }}, ++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }}, ++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }}, ++ { NULL, 0, 0, 0, NULL, {} } ++}; ++#ifdef NOT_YET ++struct ipsec_alg_capi_digest { ++ const char *digestname; /* cryptoapi's digestname */ ++ struct digest_implementation *di; ++ struct ipsec_alg_auth alg; /* note it's not a pointer */ ++}; ++static struct ipsec_alg_capi_cipher alg_capi_darray[] = { ++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }}, ++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }}, ++ { NULL, NULL, {} } ++}; ++#endif ++/* ++ * "generic" linux cryptoapi setup_cipher() function ++ */ ++int setup_cipher(const char *ciphername) ++{ ++ return crypto_alg_available(ciphername, 0); ++} ++ ++/* ++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling ++ * register_ipsec_alg for cointaned ipsec_alg object ++ */ ++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e); ++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen); ++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt); ++ ++static int ++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr) ++{ ++ int ret; ++ cptr->alg.ixt_version = IPSEC_ALG_VERSION; ++ cptr->alg.ixt_module = THIS_MODULE; ++ atomic_set (& cptr->alg.ixt_refcnt, 0); ++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name)); ++ ++ cptr->alg.ixt_blocksize=cptr->blocksize; ++ cptr->alg.ixt_keyminbits=cptr->minbits; ++ cptr->alg.ixt_keymaxbits=cptr->maxbits; ++ cptr->alg.ixt_state = 0; ++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL; ++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8; ++ cptr->alg.ixt_e_ctx_size = 0; ++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT; ++ cptr->alg.ixt_e_new_key = _capi_new_key; ++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key; ++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt; ++ cptr->alg.ixt_data = cptr; ++ ++ ret=register_ipsec_alg_enc(&cptr->alg); ++ printk("setup_ipsec_alg_capi_cipher(): " ++ "alg_type=%d alg_id=%d name=%s " ++ "keyminbits=%d keymaxbits=%d, ret=%d\n", ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ cptr->alg.ixt_name, ++ cptr->alg.ixt_keyminbits, ++ cptr->alg.ixt_keymaxbits, ++ ret); ++ return ret; ++} ++/* ++ * called in ipsec_sa_wipe() time, will destroy key contexts ++ * and do 1 unbind() ++ */ ++static void ++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e) ++{ ++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e; ++ ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:" ++ "name=%s key_e=%p \n", ++ alg->ixt_name, key_e); ++ if (!key_e) { ++ printk(KERN_ERR "klips_debug: _capi_destroy_key:" ++ "name=%s NULL key_e!\n", ++ alg->ixt_name); ++ return; ++ } ++ crypto_free_tfm(tfm); ++} ++ ++/* ++ * create new key context, need alg->ixt_data to know which ++ * (of many) cipher inside this module is the target ++ */ ++static __u8 * ++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ struct crypto_tfm *tfm=NULL; ++ ++ cptr = alg->ixt_data; ++ if (!cptr) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "NULL ixt_data (?!) for \"%s\" algo\n" ++ , alg->ixt_name); ++ goto err; ++ } ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_capi_new_key:" ++ "name=%s cptr=%p key=%p keysize=%d\n", ++ alg->ixt_name, cptr, key, keylen); ++ ++ /* ++ * alloc tfm ++ */ ++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC); ++ if (!tfm) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n" ++ , alg->ixt_name, cptr->ciphername); ++ goto err; ++ } ++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) { ++ printk(KERN_ERR "_capi_new_key(): " ++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n" ++ , alg->ixt_name, keylen); ++ crypto_free_tfm(tfm); ++ tfm=NULL; ++ } ++err: ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:_capi_new_key:" ++ "name=%s key=%p keylen=%d tfm=%p\n", ++ alg->ixt_name, key, keylen, tfm); ++ return (__u8 *) tfm; ++} ++/* ++ * core encryption function: will use cx->ci to call actual cipher's ++ * cbc function ++ */ ++static int ++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) { ++ int error =0; ++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e; ++ struct scatterlist sg = { ++ .page = virt_to_page(in), ++ .offset = (unsigned long)(in) % PAGE_SIZE, ++ .length=ilen, ++ }; ++ if (debug > 1) ++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" ++ "key_e=%p " ++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n" ++ , key_e ++ , in, in, ilen, iv, encrypt); ++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm)); ++ if (encrypt) ++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen); ++ else ++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen); ++ if (debug > 1) ++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:" ++ "error=%d\n" ++ , error); ++ return (error<0)? error : ilen; ++} ++/* ++ * main initialization loop: for each cipher in list, do ++ * 1) setup cryptoapi cipher else continue ++ * 2) register ipsec_alg object ++ */ ++static int ++setup_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ /* ++ * see if cipher has been disabled (0) or ++ * if noauto set and not enabled (1) ++ */ ++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) { ++ if (debug>0) ++ printk(KERN_INFO "setup_cipher_list(): " ++ "ciphername=%s skipped at user request: " ++ "noauto=%d parm[0]=%d parm[1]=%d\n" ++ , cptr->ciphername ++ , noauto ++ , cptr->parm[0] ++ , cptr->parm[1]); ++ continue; ++ } ++ /* ++ * use a local ci to avoid touching cptr->ci, ++ * if register ipsec_alg success then bind cipher ++ */ ++ if( setup_cipher(cptr->ciphername) ) { ++ if (debug > 0) ++ printk(KERN_DEBUG "klips_debug:" ++ "setup_cipher_list():" ++ "ciphername=%s found\n" ++ , cptr->ciphername); ++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) { ++ ++ ++ } else { ++ printk(KERN_ERR "klips_debug:" ++ "setup_cipher_list():" ++ "ciphername=%s failed ipsec_alg_register\n" ++ , cptr->ciphername); ++ } ++ } else { ++ if (debug>0) ++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n", ++ cptr->ciphername); ++ } ++ } ++ return 0; ++} ++/* ++ * deregister ipsec_alg objects and unbind ciphers ++ */ ++static int ++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { ++ unregister_ipsec_alg_enc(&cptr->alg); ++ } ++ } ++ return 0; ++} ++/* ++ * test loop for registered algos ++ */ ++static int ++test_cipher_list (struct ipsec_alg_capi_cipher* clist) ++{ ++ int test_ret; ++ struct ipsec_alg_capi_cipher *cptr; ++ /* foreach cipher in list ... */ ++ for (cptr=clist;cptr->ciphername;cptr++) { ++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) { ++ test_ret=ipsec_alg_test( ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ test); ++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n", ++ cptr->alg.ixt_alg_type, ++ cptr->alg.ixt_alg_id, ++ test_ret); ++ } ++ } ++ return 0; ++} ++ ++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init ) ++{ ++ int ret, test_ret; ++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0) ++ return -EPROTONOSUPPORT; ++ if (ret==0 && test) { ++ test_ret=test_cipher_list(alg_capi_carray); ++ } ++ return ret; ++} ++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_cryptoapi_fini ) ++{ ++ unsetup_cipher_list(alg_capi_carray); ++ return; ++} ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++EXPORT_NO_SYMBOLS; ++#endif /* NO_CRYPTOAPI_SUPPORT */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_esp.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,558 @@ ++/* ++ * processing code for ESP ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_esp_c_version[] = "RCSID $Id: ipsec_esp.c,v 1.2 2004/04/06 02:49:25 mcr Exp $"; ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_xmit.h" ++ ++#include "openswan/ipsec_auth.h" ++ ++#ifdef CONFIG_IPSEC_ESP ++#include "openswan/ipsec_esp.h" ++#endif /* CONFIG_IPSEC_ESP */ ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_esp = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ++#ifdef CONFIG_IPSEC_ESP ++enum ipsec_rcv_value ++ipsec_rcv_esp_checks(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb) ++{ ++ __u8 proto; ++ int len; /* packet length */ ++ ++ len = skb->len; ++ proto = irs->ipp->protocol; ++ ++ /* XXX this will need to be 8 for IPv6 */ ++ if ((proto == IPPROTO_ESP) && ((len - irs->iphlen) % 4)) { ++ printk("klips_error:ipsec_rcv: " ++ "got packet with content length = %d from %s -- should be on 4 octet boundary, packet dropped\n", ++ len - irs->iphlen, ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++ if(skb->len < (irs->hard_header_len + sizeof(struct iphdr) + sizeof(struct esphdr))) { ++ KLIPS_PRINT(debug_rcv & DB_RX_INAU, ++ "klips_debug:ipsec_rcv: " ++ "runt esp packet of skb->len=%d received from %s, dropped.\n", ++ skb->len, ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++ irs->protostuff.espstuff.espp = (struct esphdr *)(skb->data + irs->iphlen); ++ irs->said.spi = irs->protostuff.espstuff.espp->esp_spi; ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_rcv_value ++ipsec_rcv_esp_decrypt_setup(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb, ++ __u32 *replay, ++ unsigned char **authenticator) ++{ ++ struct esphdr *espp = irs->protostuff.espstuff.espp; ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "packet from %s received with seq=%d (iv)=0x%08x%08x iplen=%d esplen=%d sa=%s\n", ++ irs->ipsaddr_txt, ++ (__u32)ntohl(espp->esp_rpl), ++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) )), ++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) + 1)), ++ irs->len, ++ irs->ilen, ++ irs->sa_len ? irs->sa : " (error)"); ++ ++ *replay = ntohl(espp->esp_rpl); ++ *authenticator = &(skb->data[irs->len - irs->authlen]); ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_rcv_value ++ipsec_rcv_esp_authcalc(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb) ++{ ++ struct auth_alg *aa; ++ struct esphdr *espp = irs->protostuff.espstuff.espp; ++ union { ++ MD5_CTX md5; ++ SHA1_CTX sha1; ++ } tctx; ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (irs->ipsp->ips_alg_auth) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "ipsec_alg hashing proto=%d... ", ++ irs->said.proto); ++ if(irs->said.proto == IPPROTO_ESP) { ++ ipsec_alg_sa_esp_hash(irs->ipsp, ++ (caddr_t)espp, irs->ilen, ++ irs->hash, AHHMAC_HASHLEN); ++ return IPSEC_RCV_OK; ++ } ++ return IPSEC_RCV_BADPROTO; ++ } ++#endif ++ aa = irs->authfuncs; ++ ++ /* copy the initialized keying material */ ++ memcpy(&tctx, irs->ictx, irs->ictx_len); ++ ++ (*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen); ++ ++ (*aa->final)(irs->hash, (void *)&tctx); ++ ++ memcpy(&tctx, irs->octx, irs->octx_len); ++ ++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen); ++ (*aa->final)(irs->hash, (void *)&tctx); ++ ++ return IPSEC_RCV_OK; ++} ++ ++ ++enum ipsec_rcv_value ++ipsec_rcv_esp_decrypt(struct ipsec_rcv_state *irs) ++{ ++ struct ipsec_sa *ipsp = irs->ipsp; ++ struct esphdr *espp = irs->protostuff.espstuff.espp; ++ int esphlen = 0; ++ __u8 *idat; /* pointer to content to be decrypted/authenticated */ ++ __u32 iv[2]; ++ int pad = 0, padlen; ++ int badpad = 0; ++ int i; ++ struct sk_buff *skb; ++#ifdef CONFIG_IPSEC_ALG ++ struct ipsec_alg_enc *ixt_e=NULL; ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ skb=irs->skb; ++ ++ idat = skb->data + irs->iphlen; ++ ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_e=ipsp->ips_alg_enc)) { ++ esphlen = ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "encalg=%d esphlen=%d\n", ++ ipsp->ips_encalg, esphlen); ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ipsp->ips_encalg) { ++ case ESP_3DES: ++ iv[0] = *((__u32 *)(espp->esp_iv) ); ++ iv[1] = *((__u32 *)(espp->esp_iv) + 1); ++ esphlen = sizeof(struct esphdr); ++ break; ++ default: ++ ipsp->ips_errs.ips_alg_errs += 1; ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_ESP_BADALG; ++ } ++ ++ idat += esphlen; ++ irs->ilen -= esphlen; ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (ixt_e) ++ { ++ if (ipsec_alg_esp_encrypt(ipsp, ++ idat, irs->ilen, espp->esp_iv, ++ IPSEC_ALG_DECRYPT) <= 0) ++ { ++ printk("klips_error:ipsec_rcv: " ++ "got packet with esplen = %d " ++ "from %s -- should be on " ++ "ENC(%d) octet boundary, " ++ "packet dropped\n", ++ irs->ilen, ++ irs->ipsaddr_txt, ++ ipsp->ips_encalg); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BAD_DECRYPT; ++ } ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ipsp->ips_encalg) { ++ case ESP_3DES: ++ if ((irs->ilen) % 8) { ++ ipsp->ips_errs.ips_encsize_errs += 1; ++ printk("klips_error:ipsec_rcv: " ++ "got packet with esplen = %d from %s -- should be on 8 octet boundary, packet dropped\n", ++ irs->ilen, ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_3DES_BADBLOCKING; ++ } ++ des_ede3_cbc_encrypt((des_cblock *)idat, ++ (des_cblock *)idat, ++ irs->ilen, ++ ((struct des_eks *)(ipsp->ips_key_e))[0].ks, ++ ((struct des_eks *)(ipsp->ips_key_e))[1].ks, ++ ((struct des_eks *)(ipsp->ips_key_e))[2].ks, ++ (des_cblock *)iv, 0); ++ break; ++ } ++ ++ ipsec_rcv_dmp("postdecrypt", skb->data, skb->len); ++ ++ irs->next_header = idat[irs->ilen - 1]; ++ padlen = idat[irs->ilen - 2]; ++ pad = padlen + 2 + irs->authlen; ++ ++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD, ++ "klips_debug:ipsec_rcv: " ++ "padlen=%d, contents: 0x: 0x 0x ...\n", ++ padlen); ++ ++ for (i = 1; i <= padlen; i++) { ++ if((i % 16) == 1) { ++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD, ++ "klips_debug: %02x:", ++ i - 1); ++ } ++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, ++ " %02x", ++ idat[irs->ilen - 2 - padlen + i - 1]); ++ if(i != idat[irs->ilen - 2 - padlen + i - 1]) { ++ badpad = 1; ++ } ++ if((i % 16) == 0) { ++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, ++ "\n"); ++ } ++ } ++ if((i % 16) != 1) { ++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD, ++ "\n"); ++ } ++ if(badpad) { ++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD, ++ "klips_debug:ipsec_rcv: " ++ "warning, decrypted packet from %s has bad padding\n", ++ irs->ipsaddr_txt); ++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD, ++ "klips_debug:ipsec_rcv: " ++ "...may be bad decryption -- not dropped\n"); ++ ipsp->ips_errs.ips_encpad_errs += 1; ++ } ++ ++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD, ++ "klips_debug:ipsec_rcv: " ++ "packet decrypted from %s: next_header = %d, padding = %d\n", ++ irs->ipsaddr_txt, ++ irs->next_header, ++ pad - 2 - irs->authlen); ++ ++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - (esphlen + pad)); ++ ++ /* ++ * move the IP header forward by the size of the ESP header, which ++ * will remove the the ESP header from the packet. ++ */ ++ memmove((void *)(skb->data + esphlen), ++ (void *)(skb->data), irs->iphlen); ++ ++ ipsec_rcv_dmp("esp postmove", skb->data, skb->len); ++ ++ /* skb_pull below, will move up by esphlen */ ++ ++ /* XXX not clear how this can happen, as the message indicates */ ++ if(skb->len < esphlen) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_rcv: " ++ "tried to skb_pull esphlen=%d, %d available. This should never happen, please report.\n", ++ esphlen, (int)(skb->len)); ++ return IPSEC_RCV_ESP_DECAPFAIL; ++ } ++ skb_pull(skb, esphlen); ++ ++ irs->ipp = (struct iphdr *)skb->data; ++ ++ ipsec_rcv_dmp("esp postpull", skb->data, skb->len); ++ ++ /* now, trip off the padding from the end */ ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "trimming to %d.\n", ++ irs->len - esphlen - pad); ++ if(pad + esphlen <= irs->len) { ++ skb_trim(skb, irs->len - esphlen - pad); ++ } else { ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "bogus packet, size is zero or negative, dropping.\n"); ++ return IPSEC_RCV_DECAPFAIL; ++ } ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_esp_setup(struct ipsec_xmit_state *ixs) ++{ ++ __u32 iv[2]; ++ struct esphdr *espp; ++ int ilen = 0; ++ int padlen = 0, i; ++ unsigned char *dat; ++ unsigned char *idat, *pad; ++ __u8 hash[AH_AMAX]; ++ union { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ MD5_CTX md5; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ SHA1_CTX sha1; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ } tctx; ++ ++ dat = (unsigned char *)ixs->iph; ++ ++ espp = (struct esphdr *)(dat + ixs->iphlen); ++ espp->esp_spi = ixs->ipsp->ips_said.spi; ++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); ++ ++ switch(ixs->ipsp->ips_encalg) { ++#if defined(CONFIG_IPSEC_ENC_3DES) ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ iv[0] = *((__u32*)&(espp->esp_iv) ) = ++ ((__u32*)(ixs->ipsp->ips_iv))[0]; ++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) = ++ ((__u32*)(ixs->ipsp->ips_iv))[1]; ++ break; ++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++ idat = dat + ixs->iphlen + sizeof(struct esphdr); ++ ilen = ixs->skb->len - (ixs->iphlen + sizeof(struct esphdr) + ixs->authlen); ++ ++ /* Self-describing padding */ ++ pad = &dat[ixs->skb->len - ixs->tailroom]; ++ padlen = ixs->tailroom - 2 - ixs->authlen; ++ for (i = 0; i < padlen; i++) { ++ pad[i] = i + 1; ++ } ++ dat[ixs->skb->len - ixs->authlen - 2] = padlen; ++ ++ dat[ixs->skb->len - ixs->authlen - 1] = ixs->iph->protocol; ++ ixs->iph->protocol = IPPROTO_ESP; ++ ++ switch(ixs->ipsp->ips_encalg) { ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++ des_ede3_cbc_encrypt((des_cblock *)idat, ++ (des_cblock *)idat, ++ ilen, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks, ++ (des_cblock *)iv, 1); ++ break; ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++ switch(ixs->ipsp->ips_encalg) { ++#if defined(CONFIG_IPSEC_ENC_3DES) ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ /* XXX update IV with the last 8 octets of the encryption */ ++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK ++ ((__u32*)(ixs->ipsp->ips_iv))[0] = ++ ((__u32 *)(idat))[(ilen >> 2) - 2]; ++ ((__u32*)(ixs->ipsp->ips_iv))[1] = ++ ((__u32 *)(idat))[(ilen >> 2) - 1]; ++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ ++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ); ++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ ++ break; ++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ ipsec_xmit_dmp("espp", (char*)espp, ixs->skb->len - ixs->iphlen - ixs->authlen); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen); ++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash)); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, hash, AHMD596_ALEN); ++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash)); ++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ SHA1Update(&tctx.sha1, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen); ++ SHA1Final(hash, &tctx.sha1); ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); ++ SHA1Final(hash, &tctx.sha1); ++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ break; ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_AH_BADALG; ++ } ++#ifdef NET_21 ++ ixs->skb->h.raw = (unsigned char*)espp; ++#endif /* NET_21 */ ++ ++ return IPSEC_XMIT_OK; ++} ++ ++ ++struct xform_functions esp_xform_funcs[]={ ++ { rcv_checks: ipsec_rcv_esp_checks, ++ rcv_setup_auth: ipsec_rcv_esp_decrypt_setup, ++ rcv_calc_auth: ipsec_rcv_esp_authcalc, ++ rcv_decrypt: ipsec_rcv_esp_decrypt, ++ ++ xmit_setup: ipsec_xmit_esp_setup, ++ xmit_headroom: sizeof(struct esphdr), ++ xmit_needtailroom: 1, ++ }, ++}; ++ ++struct inet_protocol esp_protocol = ++{ ++ ipsec_rcv, /* ESP handler */ ++ NULL, /* TUNNEL error control */ ++#ifdef NETDEV_25 ++ 1, /* no policy */ ++#else ++ 0, /* next */ ++ IPPROTO_ESP, /* protocol ID */ ++ 0, /* copy */ ++ NULL, /* data */ ++ "ESP" /* name */ ++#endif ++}; ++ ++ ++ ++#endif /* !CONFIG_IPSEC_ESP */ ++ ++ ++/* ++ * $Log: ipsec_esp.c,v $ ++ * Revision 1.2 2004/04/06 02:49:25 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_init.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,531 @@ ++/* ++ * @(#) Initialization code. ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998 - 2002 Richard Guy Briggs ++ * 2001 - 2004 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * /proc system code was split out into ipsec_proc.c after rev. 1.70. ++ * ++ */ ++ ++char ipsec_init_c_version[] = "RCSID $Id: ipsec_init.c,v 1.93 2004/04/06 02:49:26 mcr Exp $"; ++ ++#include ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include /* struct sockaddr_in */ ++#include ++#include /* get_random_bytes() */ ++#include ++ ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* 23_SPINLOCK */ ++# include /* *lock* */ ++# endif /* 23_SPINLOCK */ ++#endif /* SPINLOCK */ ++ ++#ifdef NET_21 ++# include ++# include ++#endif /* NET_21 */ ++ ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++# include ++#endif /* CONFIG_PROC_FS */ ++ ++#ifdef NETLINK_SOCK ++# include ++#else ++# include ++#endif ++ ++#include "openswan/radij.h" ++ ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_stats.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++ ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++# include "openswan/ipcomp.h" ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++#include ++#include ++ ++#if !defined(CONFIG_IPSEC_ESP) && !defined(CONFIG_IPSEC_AH) ++#error "kernel configuration must include ESP or AH" ++#endif ++ ++/* ++ * seems to be present in 2.4.10 (Linus), but also in some RH and other ++ * distro kernels of a lower number. ++ */ ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_eroute = 0; ++int debug_spi = 0; ++int debug_netlink = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++struct prng ipsec_prng; ++ ++extern int ipsec_device_event(struct notifier_block *dnot, unsigned long event, void *ptr); ++/* ++ * the following structure is required so that we receive ++ * event notifications when network devices are enabled and ++ * disabled (ifconfig up and down). ++ */ ++static struct notifier_block ipsec_dev_notifier={ ++ ipsec_device_event, ++ NULL, ++ 0 ++}; ++ ++#ifdef CONFIG_SYSCTL ++extern int ipsec_sysctl_register(void); ++extern void ipsec_sysctl_unregister(void); ++#endif ++ ++static inline int ++openswan_inet_add_protocol(struct inet_protocol *prot, unsigned protocol) ++{ ++#ifdef NETDEV_25 ++ return inet_add_protocol(prot, protocol); ++#else ++ inet_add_protocol(prot); ++ return 0; ++#endif ++} ++ ++static inline int ++openswan_inet_del_protocol(struct inet_protocol *prot, unsigned protocol) ++{ ++#ifdef NETDEV_25 ++ return inet_del_protocol(prot, protocol); ++#else ++ inet_del_protocol(prot); ++ return 0; ++#endif ++} ++ ++/* void */ ++int ++ipsec_init(void) ++{ ++ int error = 0; ++ unsigned char seed[256]; ++#ifdef CONFIG_IPSEC_ENC_3DES ++ extern int des_check_key; ++ ++ /* turn off checking of keys */ ++ des_check_key=0; ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ ++ KLIPS_PRINT(1, "klips_info:ipsec_init: " ++ "KLIPS startup, Openswan KLIPS IPsec stack version: %s\n", ++ ipsec_version_code()); ++ ++ error |= ipsec_proc_init(); ++ ++#ifdef SPINLOCK ++ ipsec_sadb.sadb_lock = SPIN_LOCK_UNLOCKED; ++#else /* SPINLOCK */ ++ ipsec_sadb.sadb_lock = 0; ++#endif /* SPINLOCK */ ++ ++#ifndef SPINLOCK ++ tdb_lock.lock = 0; ++ eroute_lock.lock = 0; ++#endif /* !SPINLOCK */ ++ ++ error |= ipsec_sadb_init(); ++ error |= ipsec_radijinit(); ++ ++ error |= pfkey_init(); ++ ++ error |= register_netdevice_notifier(&ipsec_dev_notifier); ++ ++#ifdef CONFIG_IPSEC_ESP ++ openswan_inet_add_protocol(&esp_protocol, IPPROTO_ESP); ++#endif /* CONFIG_IPSEC_ESP */ ++ ++#ifdef CONFIG_IPSEC_AH ++ openswan_inet_add_protocol(&ah_protocol, IPPROTO_AH); ++#endif /* CONFIG_IPSEC_AH */ ++ ++/* we never actually link IPCOMP to the stack */ ++#ifdef IPCOMP_USED_ALONE ++#ifdef CONFIG_IPSEC_IPCOMP ++ openswan_inet_add_protocol(&comp_protocol, IPPROTO_COMP); ++#endif /* CONFIG_IPSEC_IPCOMP */ ++#endif ++ ++ error |= ipsec_tunnel_init_devices(); ++ ++ ++#ifdef CONFIG_SYSCTL ++ error |= ipsec_sysctl_register(); ++#endif ++ ++#ifdef CONFIG_IPSEC_ALG ++ ipsec_alg_init(); ++#endif ++ ++ get_random_bytes((void *)seed, sizeof(seed)); ++ prng_init(&ipsec_prng, seed, sizeof(seed)); ++ ++ return error; ++} ++ ++ ++/* void */ ++int ++ipsec_cleanup(void) ++{ ++ int error = 0; ++ ++#ifdef CONFIG_SYSCTL ++ ipsec_sysctl_unregister(); ++#endif ++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ ++ "klips_debug:ipsec_cleanup: " ++ "calling ipsec_tunnel_cleanup_devices.\n"); ++ error |= ipsec_tunnel_cleanup_devices(); ++ ++ KLIPS_PRINT(debug_netlink, "called ipsec_tunnel_cleanup_devices"); ++ ++/* we never actually link IPCOMP to the stack */ ++#ifdef IPCOMP_USED_ALONE ++#ifdef CONFIG_IPSEC_IPCOMP ++ if (openswan_inet_del_protocol(&comp_protocol, IPPROTO_COMP) < 0) ++ printk(KERN_INFO "klips_debug:ipsec_cleanup: " ++ "comp close: can't remove protocol\n"); ++#endif /* CONFIG_IPSEC_IPCOMP */ ++#endif /* IPCOMP_USED_ALONE */ ++ ++#ifdef CONFIG_IPSEC_AH ++ if (openswan_inet_del_protocol(&ah_protocol, IPPROTO_AH) < 0) ++ printk(KERN_INFO "klips_debug:ipsec_cleanup: " ++ "ah close: can't remove protocol\n"); ++#endif /* CONFIG_IPSEC_AH */ ++ ++#ifdef CONFIG_IPSEC_ESP ++ if (openswan_inet_del_protocol(&esp_protocol, IPPROTO_ESP) < 0) ++ printk(KERN_INFO "klips_debug:ipsec_cleanup: " ++ "esp close: can't remove protocol\n"); ++#endif /* CONFIG_IPSEC_ESP */ ++ ++ error |= unregister_netdevice_notifier(&ipsec_dev_notifier); ++ ++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ ++ "klips_debug:ipsec_cleanup: " ++ "calling ipsec_sadb_cleanup.\n"); ++ error |= ipsec_sadb_cleanup(0); ++ error |= ipsec_sadb_free(); ++ ++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ ++ "klips_debug:ipsec_cleanup: " ++ "calling ipsec_radijcleanup.\n"); ++ error |= ipsec_radijcleanup(); ++ ++ KLIPS_PRINT(debug_pfkey, /* debug_tunnel & DB_TN_INIT, */ ++ "klips_debug:ipsec_cleanup: " ++ "calling pfkey_cleanup.\n"); ++ error |= pfkey_cleanup(); ++ ++ ipsec_proc_cleanup(); ++ ++ prng_final(&ipsec_prng); ++ ++ return error; ++} ++ ++#ifdef MODULE ++int ++init_module(void) ++{ ++ int error = 0; ++ ++ error |= ipsec_init(); ++ ++ return error; ++} ++ ++int ++cleanup_module(void) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */ ++ "klips_debug:cleanup_module: " ++ "calling ipsec_cleanup.\n"); ++ ++ error |= ipsec_cleanup(); ++ ++ KLIPS_PRINT(1, "klips_info:cleanup_module: " ++ "ipsec module unloaded.\n"); ++ ++ return error; ++} ++#endif /* MODULE */ ++ ++/* ++ * $Log: ipsec_init.c,v $ ++ * Revision 1.93 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.92 2004/03/30 15:30:39 ken ++ * Proper Capitalization ++ * ++ * Revision 1.91 2004/03/22 01:51:51 ken ++ * We are open ++ * ++ * Revision 1.90.4.2 2004/04/05 04:30:46 mcr ++ * patches for alg-branch to compile/work with 2.x openswan ++ * ++ * Revision 1.90.4.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.90 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.89.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.89 2003/07/31 22:47:16 mcr ++ * preliminary (untested by FS-team) 2.5 patches. ++ * ++ * Revision 1.88 2003/06/22 20:05:36 mcr ++ * clarified why IPCOMP was not being registered, and put a new ++ * #ifdef in rather than #if 0. ++ * ++ * Revision 1.87 2002/09/20 15:40:51 rgb ++ * Added a lock to the global ipsec_sadb struct for future use. ++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem ++ * of freeing newly created structures when clearing the reftable upon startup ++ * to start from a known state. ++ * ++ * Revision 1.86 2002/08/15 18:39:15 rgb ++ * Move ipsec_prng outside debug code. ++ * ++ * Revision 1.85 2002/05/14 02:35:29 rgb ++ * Change reference to tdb to ipsa. ++ * ++ * Revision 1.84 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.83 2002/04/24 07:36:28 mcr ++ * Moved from ./klips/net/ipsec/ipsec_init.c,v ++ * ++ * Revision 1.82 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.81 2002/04/09 16:13:32 mcr ++ * switch license to straight GPL. ++ * ++ * Revision 1.80 2002/03/24 07:34:08 rgb ++ * Sanity check for at least one of AH or ESP configured. ++ * ++ * Revision 1.79 2002/02/05 22:55:15 mcr ++ * added MODULE_LICENSE declaration. ++ * This macro does not appear in all kernel versions (see comment). ++ * ++ * Revision 1.78 2002/01/29 17:17:55 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.77 2002/01/29 04:00:51 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.76 2002/01/29 02:13:17 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.75 2001/11/26 09:23:48 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.74 2001/11/22 05:44:11 henry ++ * new version stuff ++ * ++ * Revision 1.71.2.2 2001/10/22 20:51:00 mcr ++ * explicitely set des_check_key. ++ * ++ * Revision 1.71.2.1 2001/09/25 02:19:39 mcr ++ * /proc manipulation code moved to new ipsec_proc.c ++ * ++ * Revision 1.73 2001/11/06 19:47:17 rgb ++ * Changed lifetime_packets to uint32 from uint64. ++ * ++ * Revision 1.72 2001/10/18 04:45:19 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.71 2001/09/20 15:32:45 rgb ++ * Minor pfkey lifetime fixes. ++ * ++ * Revision 1.70 2001/07/06 19:51:21 rgb ++ * Added inbound policy checking code for IPIP SAs. ++ * ++ * Revision 1.69 2001/06/14 19:33:26 rgb ++ * Silence startup message for console, but allow it to be logged. ++ * Update copyright date. ++ * ++ * Revision 1.68 2001/05/29 05:14:36 rgb ++ * Added PMTU to /proc/net/ipsec_tncfg output. See 'man 5 ipsec_tncfg'. ++ * ++ * Revision 1.67 2001/05/04 16:34:52 rgb ++ * Rremove erroneous checking of return codes for proc_net_* in 2.4. ++ * ++ * Revision 1.66 2001/05/03 19:40:34 rgb ++ * Check error return codes in startup and shutdown. ++ * ++ * Revision 1.65 2001/02/28 05:03:27 rgb ++ * Clean up and rationalise startup messages. ++ * ++ * Revision 1.64 2001/02/27 22:24:53 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.63 2000/11/29 20:14:06 rgb ++ * Add src= to the output of /proc/net/ipsec_spi and delete dst from IPIP. ++ * ++ * Revision 1.62 2000/11/06 04:31:24 rgb ++ * Ditched spin_lock_irqsave in favour of spin_lock_bh. ++ * Fixed longlong for pre-2.4 kernels (Svenning). ++ * Add Svenning's adaptive content compression. ++ * Disabled registration of ipcomp handler. ++ * ++ * Revision 1.61 2000/10/11 13:37:54 rgb ++ * #ifdef out debug print that causes proc/net/ipsec_version to oops. ++ * ++ * Revision 1.60 2000/09/20 03:59:01 rgb ++ * Change static info functions to DEBUG_NO_STATIC to reveal function names ++ * in oopsen. ++ * ++ * Revision 1.59 2000/09/16 01:06:26 rgb ++ * Added cast of var to silence compiler warning about long fed to int ++ * format. ++ * ++ * Revision 1.58 2000/09/15 11:37:01 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.57 2000/09/12 03:21:50 rgb ++ * Moved radij_c_version printing to ipsec_version_get_info(). ++ * Reformatted ipsec_version_get_info(). ++ * Added sysctl_{,un}register() calls. ++ * ++ * Revision 1.56 2000/09/08 19:16:50 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Removed all references to CONFIG_IPSEC_PFKEYv2. ++ * ++ * Revision 1.55 2000/08/30 05:19:03 rgb ++ * Cleaned up no longer used spi_next, netlink register/unregister, other ++ * minor cleanup. ++ * Removed cruft replaced by TDB_XFORM_NAME. ++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst. ++ * Moved debug version strings to printk when /proc/net/ipsec_version is ++ * called. ++ * ++ * Revision 1.54 2000/08/20 18:31:05 rgb ++ * Changed cosmetic alignment in spi_info. ++ * Changed addtime and usetime to use actual value which is relative ++ * anyways, as intended. (Momchil) ++ * ++ * Revision 1.53 2000/08/18 17:37:03 rgb ++ * Added an (int) cast to shut up the compiler... ++ * ++ * Revision 1.52 2000/08/01 14:51:50 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.51 2000/07/25 20:41:22 rgb ++ * Removed duplicate parameter in spi_getinfo. ++ * ++ * Revision 1.50 2000/07/17 03:21:45 rgb ++ * Removed /proc/net/ipsec_spinew. ++ * ++ * Revision 1.49 2000/06/28 05:46:51 rgb ++ * Renamed ivlen to iv_bits for consistency. ++ * Changed output of add and use times to be relative to now. ++ * ++ * Revision 1.48 2000/05/11 18:26:10 rgb ++ * Commented out calls to netlink_attach/detach to avoid activating netlink ++ * in the kenrel config. ++ * ++ * Revision 1.47 2000/05/10 22:35:26 rgb ++ * Comment out most of the startup version information. ++ * ++ * Revision 1.46 2000/03/22 16:15:36 rgb ++ * Fixed renaming of dev_get (MB). ++ * ++ * Revision 1.45 2000/03/16 06:40:48 rgb ++ * Hardcode PF_KEYv2 support. ++ * ++ * Revision 1.44 2000/01/22 23:19:20 rgb ++ * Simplified code to use existing macro TDB_XFORM_NAME(). ++ * ++ * Revision 1.43 2000/01/21 06:14:04 rgb ++ * Print individual stats only if non-zero. ++ * Removed 'bits' from each keylength for brevity. ++ * Shortened lifetimes legend for brevity. ++ * Changed wording from 'last_used' to the clearer 'idle'. ++ * ++ * Revision 1.42 1999/12/31 14:57:19 rgb ++ * MB fix for new dummy-less proc_get_info in 2.3.35. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_ipcomp.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,276 @@ ++/* ++ * processing code for IPCOMP ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_ipcomp_c_version[] = "RCSID $Id: ipsec_ipcomp.c,v 1.2 2004/04/06 02:49:26 mcr Exp $"; ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_xmit.h" ++ ++#include "openswan/ipsec_auth.h" ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++#include "openswan/ipsec_ipcomp.h" ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#include "openswan/ipsec_proto.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_ipcomp = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++enum ipsec_rcv_value ++ipsec_rcv_ipcomp_checks(struct ipsec_rcv_state *irs, ++ struct sk_buff *skb) ++{ ++ int ipcompminlen; ++ ++ ipcompminlen = irs->hard_header_len + sizeof(struct iphdr); ++ ++ if(skb->len < (ipcompminlen + sizeof(struct ipcomphdr))) { ++ KLIPS_PRINT(debug_rcv & DB_RX_INAU, ++ "klips_debug:ipsec_rcv: " ++ "runt comp packet of skb->len=%d received from %s, dropped.\n", ++ skb->len, ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++ irs->protostuff.ipcompstuff.compp = (struct ipcomphdr *)(skb->data + irs->iphlen); ++ irs->said.spi = htonl((__u32)ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi)); ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_rcv_value ++ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs) ++{ ++ unsigned int flags = 0; ++ struct ipsec_sa *ipsp = irs->ipsp; ++ struct sk_buff *skb; ++ ++ skb=irs->skb; ++ ++ ipsec_xmit_dmp("ipcomp", skb->data, skb->len); ++ ++ if(ipsp == NULL) { ++ return IPSEC_RCV_SAIDNOTFOUND; ++ } ++ ++#if 0 ++ /* we want to check that this wasn't the first SA on the list, because ++ * we don't support bare IPCOMP, for unexplained reasons. MCR ++ */ ++ if (ipsp->ips_onext != NULL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "Incoming packet with outer IPCOMP header SA:%s: not yet supported by KLIPS, dropped\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ++ return IPSEC_RCV_IPCOMPALONE; ++ } ++#endif ++ ++ if(sysctl_ipsec_inbound_policy_check && ++ ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != ntohl(irs->said.spi)) && ++ (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */ ++ ))) { ++ char sa2[SATOT_BUF]; ++ size_t sa_len2 = 0; ++ ++ sa_len2 = satot(&ipsp->ips_said, 0, sa2, sizeof(sa2)); ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n", ++ irs->sa_len ? irs->sa : " (error)", ++ ipsp != NULL ? (sa_len2 ? sa2 : " (error)") : "NULL", ++ ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi), ++ (__u32)ntohl(irs->said.spi), ++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, ++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_SAIDNOTFOUND; ++ } ++ ++ ipsp->ips_comp_ratio_cbytes += ntohs(irs->ipp->tot_len); ++ irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh; ++ ++ skb = skb_decompress(skb, ipsp, &flags); ++ if (!skb || flags) { ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "skb_decompress() returned error flags=%x, dropped.\n", ++ flags); ++ if (irs->stats) { ++ if (flags) ++ irs->stats->rx_errors++; ++ else ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_IPCOMPFAILED; ++ } ++ ++ /* make sure we update the pointer */ ++ irs->skb = skb; ++ ++#ifdef NET_21 ++ irs->ipp = skb->nh.iph; ++#else /* NET_21 */ ++ irs->ipp = skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ ipsp->ips_comp_ratio_dbytes += ntohs(irs->ipp->tot_len); ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n", ++ irs->sa_len ? irs->sa : " (error)", ++ (__u32)ntohl(irs->said.spi), ++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0, ++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0, ++ irs->next_header); ++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->ipp); ++ ++ return IPSEC_RCV_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_ipcomp_setup(struct ipsec_xmit_state *ixs) ++{ ++ unsigned int flags = 0; ++#ifdef CONFIG_IPSEC_DEBUG ++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len); ++ ++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags); ++ ++#ifdef NET_21 ++ ixs->iph = ixs->skb->nh.iph; ++#else /* NET_21 */ ++ ixs->iph = ixs->skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len); ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_tunnel & DB_TN_CROUT) ++ { ++ if (old_tot_len > ntohs(ixs->iph->tot_len)) ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n", ++ old_tot_len, ntohs(ixs->iph->tot_len), ++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi), ++ ntohl(ixs->ipsp->ips_said.spi), ++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff)); ++ else ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "packet did not compress (flags = %d).\n", ++ flags); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ return IPSEC_XMIT_OK; ++} ++ ++struct xform_functions ipcomp_xform_funcs[]={ ++ {rcv_checks: ipsec_rcv_ipcomp_checks, ++ rcv_decrypt: ipsec_rcv_ipcomp_decomp, ++ xmit_setup: ipsec_xmit_ipcomp_setup, ++ xmit_headroom: 0, ++ xmit_needtailroom: 0, ++ }, ++}; ++ ++#if 0 ++/* We probably don't want to install a pure IPCOMP protocol handler, but ++ only want to handle IPCOMP if it is encapsulated inside an ESP payload ++ (which is already handled) */ ++#ifdef CONFIG_IPSEC_IPCOMP ++struct inet_protocol comp_protocol = ++{ ++ ipsec_rcv, /* COMP handler */ ++ NULL, /* COMP error control */ ++#ifdef NETDEV_25 ++ 1, /* no policy */ ++#else ++ 0, /* next */ ++ IPPROTO_COMP, /* protocol ID */ ++ 0, /* copy */ ++ NULL, /* data */ ++ "COMP" /* name */ ++#endif ++}; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++#endif ++ ++#endif /* CONFIG_IPSEC_IPCOMP */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_ipip.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,133 @@ ++/* ++ * processing code for IPIP ++ * Copyright (C) 2003 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_ipip_c_version[] = "RCSID $Id: ipsec_ipip.c,v 1.2 2004/04/06 02:49:26 mcr Exp $"; ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_xmit.h" ++ ++#include "openswan/ipsec_auth.h" ++#include "openswan/ipsec_ipip.h" ++#include "openswan/ipsec_param.h" ++ ++#include "openswan/ipsec_proto.h" ++ ++enum ipsec_xmit_value ++ipsec_xmit_ipip_setup(struct ipsec_xmit_state *ixs) ++{ ++ ixs->iph->version = 4; ++ ++ switch(sysctl_ipsec_tos) { ++ case 0: ++#ifdef NET_21 ++ ixs->iph->tos = ixs->skb->nh.iph->tos; ++#else /* NET_21 */ ++ ixs->iph->tos = ixs->skb->ip_hdr->tos; ++#endif /* NET_21 */ ++ break; ++ case 1: ++ ixs->iph->tos = 0; ++ break; ++ default: ++ break; ++ } ++#ifdef NET_21 ++#ifdef NETDEV_23 ++ ixs->iph->ttl = sysctl_ip_default_ttl; ++#else /* NETDEV_23 */ ++ ixs->iph->ttl = ip_statistics.IpDefaultTTL; ++#endif /* NETDEV_23 */ ++#else /* NET_21 */ ++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */ ++#endif /* NET_21 */ ++ ixs->iph->frag_off = 0; ++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr; ++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr; ++ ixs->iph->protocol = IPPROTO_IPIP; ++ ixs->iph->ihl = sizeof(struct iphdr) >> 2; ++ ++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb); ++ ++ ixs->newdst = (__u32)ixs->iph->daddr; ++ ixs->newsrc = (__u32)ixs->iph->saddr; ++ ++#ifdef NET_21 ++ ixs->skb->h.ipiph = ixs->skb->nh.iph; ++#endif /* NET_21 */ ++ return IPSEC_XMIT_OK; ++} ++ ++struct xform_functions ipip_xform_funcs[]={ ++ { rcv_checks: NULL, ++ rcv_setup_auth: NULL, ++ rcv_calc_auth: NULL, ++ rcv_decrypt: NULL, ++ ++ xmit_setup: ipsec_xmit_ipip_setup, ++ xmit_headroom: sizeof(struct iphdr), ++ xmit_needtailroom: 0, ++ }, ++}; ++ ++ ++ ++ ++ ++ ++ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_life.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,263 @@ ++/* ++ * @(#) lifetime structure utilities ++ * ++ * Copyright (C) 2001 Richard Guy Briggs ++ * and Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_life.c,v 1.12 2004/04/23 20:44:35 ken Exp $ ++ * ++ */ ++ ++/* ++ * This provides series of utility functions for dealing with lifetime ++ * structures. ++ * ++ * ipsec_check_lifetime - returns -1 hard lifetime exceeded ++ * 0 soft lifetime exceeded ++ * 1 everything is okay ++ * based upon whether or not the count exceeds hard/soft ++ * ++ */ ++ ++#define __NO_VERSION__ ++#include ++#include /* for CONFIG_IP_FORWARD */ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#include /* struct device, struct net_device_stats and other headers */ ++#include /* eth_type_trans */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_eroute.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++ ++#include "openswan/ipsec_sa.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_ipe4.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++#include "openswan/ipcomp.h" ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++ ++ ++enum ipsec_life_alive ++ipsec_lifetime_check(struct ipsec_lifetime64 *il64, ++ const char *lifename, ++ const char *saname, ++ enum ipsec_life_type ilt, ++ enum ipsec_direction idir, ++ struct ipsec_sa *ips) ++{ ++ __u64 count; ++ const char *dir; ++ ++ if(saname == NULL) { ++ saname = "unknown-SA"; ++ } ++ ++ if(idir == ipsec_incoming) { ++ dir = "incoming"; ++ } else { ++ dir = "outgoing"; ++ } ++ ++ ++ if(ilt == ipsec_life_timebased) { ++ count = jiffies/HZ - il64->ipl_count; ++ } else { ++ count = il64->ipl_count; ++ } ++ ++ if(il64->ipl_hard && ++ (count > il64->ipl_hard)) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_lifetime_check: " ++ "hard %s lifetime of SA:<%s%s%s> %s has been reached, SA expired, " ++ "%s packet dropped.\n", ++ lifename, ++ IPS_XFORM_NAME(ips), ++ saname, ++ dir); ++ ++ pfkey_expire(ips, 1); ++ return ipsec_life_harddied; ++ } ++ ++ if(il64->ipl_soft && ++ (count > il64->ipl_soft)) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_lifetime_check: " ++ "soft %s lifetime of SA:<%s%s%s> %s has been reached, SA expiring, " ++ "soft expire message sent up, %s packet still processed.\n", ++ lifename, ++ IPS_XFORM_NAME(ips), ++ saname, ++ dir); ++ ++ if(ips->ips_state != SADB_SASTATE_DYING) { ++ pfkey_expire(ips, 0); ++ } ++ ips->ips_state = SADB_SASTATE_DYING; ++ ++ return ipsec_life_softdied; ++ } ++ return ipsec_life_okay; ++} ++ ++ ++/* ++ * This function takes a buffer (with length), a lifetime name and type, ++ * and formats a string to represent the current values of the lifetime. ++ * ++ * It returns the number of bytes that the format took (or would take, ++ * if the buffer were large enough: snprintf semantics). ++ * This is used in /proc routines and in debug output. ++ */ ++int ++ipsec_lifetime_format(char *buffer, ++ int buflen, ++ char *lifename, ++ enum ipsec_life_type timebaselife, ++ struct ipsec_lifetime64 *lifetime) ++{ ++ int len = 0; ++ __u64 count; ++ ++ if(timebaselife == ipsec_life_timebased) { ++ count = jiffies/HZ - lifetime->ipl_count; ++ } else { ++ count = lifetime->ipl_count; ++ } ++ ++ if(lifetime->ipl_count > 1 || ++ lifetime->ipl_soft || ++ lifetime->ipl_hard) { ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) ++ len = ipsec_snprintf(buffer, buflen, ++ "%s(%Lu,%Lu,%Lu)", ++ lifename, ++ count, ++ lifetime->ipl_soft, ++ lifetime->ipl_hard); ++#else /* XXX high 32 bits are not displayed */ ++ len = ipsec_snprintf(buffer, buflen, ++ "%s(%lu,%lu,%lu)", ++ lifename, ++ (unsigned long)count, ++ (unsigned long)lifetime->ipl_soft, ++ (unsigned long)lifetime->ipl_hard); ++#endif ++ } ++ ++ return len; ++} ++ ++void ++ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime, ++ __u64 newvalue) ++{ ++ if(newvalue && ++ (!lifetime->ipl_hard || ++ (newvalue < lifetime->ipl_hard))) { ++ lifetime->ipl_hard = newvalue; ++ ++ if(!lifetime->ipl_soft && ++ (lifetime->ipl_hard < lifetime->ipl_soft)) { ++ lifetime->ipl_soft = lifetime->ipl_hard; ++ } ++ } ++} ++ ++void ++ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime, ++ __u64 newvalue) ++{ ++ if(newvalue && ++ (!lifetime->ipl_soft || ++ (newvalue < lifetime->ipl_soft))) { ++ lifetime->ipl_soft = newvalue; ++ ++ if(lifetime->ipl_hard && ++ (lifetime->ipl_hard < lifetime->ipl_soft)) { ++ lifetime->ipl_soft = lifetime->ipl_hard; ++ } ++ } ++} ++ ++ ++/* ++ * $Log: ipsec_life.c,v $ ++ * Revision 1.12 2004/04/23 20:44:35 ken ++ * Update comments ++ * ++ * Revision 1.11 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.10 2004/03/30 11:03:10 paul ++ * two more occurances of snprintf, found by Sam from a users oops msg. ++ * ++ * Revision 1.9 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.8.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.8 2003/02/06 02:00:10 rgb ++ * Fixed incorrect debugging text label ++ * ++ * Revision 1.7 2002/05/23 07:16:26 rgb ++ * Fixed absolute/relative reference to lifetime count printout. ++ * ++ * Revision 1.6 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.5 2002/04/24 07:36:28 mcr ++ * Moved from ./klips/net/ipsec/ipsec_life.c,v ++ * ++ * Revision 1.4 2002/01/29 17:17:55 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.3 2002/01/29 02:13:17 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.2 2001/11/26 09:16:14 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr ++ * lifetime structure created and common functions created. ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_mast.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1080 @@ ++/* ++ * IPSEC MAST code. ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_mast_c_version[] = "RCSID $Id: ipsec_mast.c,v 1.3 2003/10/31 02:27:55 mcr Exp $"; ++ ++#define __NO_VERSION__ ++#include ++#include /* for CONFIG_IP_FORWARD */ ++#include ++#include /* printk() */ ++ ++#include "freeswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include /* struct tcphdr */ ++#include /* struct udphdr */ ++#include ++#include ++#include ++#include ++#include ++#undef dev_kfree_skb ++#define dev_kfree_skb(a,b) kfree_skb(a) ++#define PHYSDEV_TYPE ++#include ++#include /* icmp_send() */ ++#include ++#include ++ ++#include ++ ++#include "freeswan/radij.h" ++#include "freeswan/ipsec_life.h" ++#include "freeswan/ipsec_xform.h" ++#include "freeswan/ipsec_eroute.h" ++#include "freeswan/ipsec_encap.h" ++#include "freeswan/ipsec_radij.h" ++#include "freeswan/ipsec_sa.h" ++#include "freeswan/ipsec_tunnel.h" ++#include "freeswan/ipsec_mast.h" ++#include "freeswan/ipsec_ipe4.h" ++#include "freeswan/ipsec_ah.h" ++#include "freeswan/ipsec_esp.h" ++ ++#include ++#include ++ ++#include "freeswan/ipsec_proto.h" ++ ++int ipsec_maxdevice_count = -1; ++ ++DEBUG_NO_STATIC int ++ipsec_mast_open(struct device *dev) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ /* ++ * Can't open until attached. ++ */ ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_open: " ++ "dev = %s, prv->dev = %s\n", ++ dev->name, prv->dev?prv->dev->name:"NONE"); ++ ++ if (prv->dev == NULL) ++ return -ENODEV; ++ ++ MOD_INC_USE_COUNT; ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_close(struct device *dev) ++{ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static inline int ipsec_mast_xmit2(struct sk_buff *skb) ++{ ++ return ip_send(skb); ++} ++ ++enum ipsec_xmit_value ++ipsec_mast_send(struct ipsec_xmit_state*ixs) ++{ ++ /* new route/dst cache code from James Morris */ ++ ixs->skb->dev = ixs->physdev; ++ /*skb_orphan(ixs->skb);*/ ++ if((ixs->error = ip_route_output(&ixs->route, ++ ixs->skb->nh.iph->daddr, ++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr, ++ RT_TOS(ixs->skb->nh.iph->tos), ++ ixs->physdev->iflink /* rgb: should this be 0? */))) { ++ ixs->stats->tx_errors++; ++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n", ++ ixs->error, ++ ixs->route->u.dst.dev->name); ++ return IPSEC_XMIT_ROUTEERR; ++ } ++ if(ixs->dev == ixs->route->u.dst.dev) { ++ ip_rt_put(ixs->route); ++ /* This is recursion, drop it. */ ++ ixs->stats->tx_errors++; ++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n", ++ ixs->dev->name); ++ return IPSEC_XMIT_RECURSDETECT; ++ } ++ dst_release(ixs->skb->dst); ++ ixs->skb->dst = &ixs->route->u.dst; ++ ixs->stats->tx_bytes += ixs->skb->len; ++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) { ++ ixs->stats->tx_errors++; ++ printk(KERN_WARNING ++ "klips_error:ipsec_xmit_send: " ++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n", ++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data), ++ ixs->skb->len); ++ return IPSEC_XMIT_PUSHPULLERR; ++ } ++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data); ++#ifdef SKB_RESET_NFCT ++ nf_conntrack_put(ixs->skb->nfct); ++ ixs->skb->nfct = NULL; ++#ifdef CONFIG_NETFILTER_DEBUG ++ ixs->skb->nf_debug = 0; ++#endif /* CONFIG_NETFILTER_DEBUG */ ++#endif /* SKB_RESET_NFCT */ ++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "...done, calling ip_send() on device:%s\n", ++ ixs->skb->dev ? ixs->skb->dev->name : "NULL"); ++ KLIPS_IP_PRINT(debug_mast & DB_MAST_XMIT, ixs->skb->nh.iph); ++ { ++ int err; ++ ++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev, ++ ipsec_mast_xmit2); ++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) { ++ if(net_ratelimit()) ++ printk(KERN_ERR ++ "klips_error:ipsec_xmit_send: " ++ "ip_send() failed, err=%d\n", ++ -err); ++ ixs->stats->tx_errors++; ++ ixs->stats->tx_aborted_errors++; ++ ixs->skb = NULL; ++ return IPSEC_XMIT_IPSENDFAILURE; ++ } ++ } ++ ixs->stats->tx_packets++; ++ ++ ixs->skb = NULL; ++ ++ return IPSEC_XMIT_OK; ++} ++ ++void ++ipsec_mast_cleanup(struct ipsec_xmit_state*ixs) ++{ ++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) ++ netif_wake_queue(ixs->dev); ++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ ++ ixs->dev->tbusy = 0; ++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ ++ if(ixs->saved_header) { ++ kfree(ixs->saved_header); ++ } ++ if(ixs->skb) { ++ dev_kfree_skb(ixs->skb, FREE_WRITE); ++ } ++ if(ixs->oskb) { ++ dev_kfree_skb(ixs->oskb, FREE_WRITE); ++ } ++ if (ixs->ips.ips_ident_s.data) { ++ kfree(ixs->ips.ips_ident_s.data); ++ } ++ if (ixs->ips.ips_ident_d.data) { ++ kfree(ixs->ips.ips_ident_d.data); ++ } ++} ++ ++#if 0 ++/* ++ * This function assumes it is being called from dev_queue_xmit() ++ * and that skb is filled properly by that function. ++ */ ++int ++ipsec_mast_start_xmit(struct sk_buff *skb, struct device *dev, IPsecSAref_t SAref) ++{ ++ struct ipsec_xmit_state ixs_mem; ++ struct ipsec_xmit_state *ixs = &ixs_mem; ++ enum ipsec_xmit_value stat = IPSEC_XMIT_OK; ++ ++ /* dev could be a mast device, but should be optional, I think... */ ++ /* SAref is also optional, but one of the two must be present. */ ++ /* I wonder if it could accept no device or saref and guess? */ ++ ++/* ipsec_xmit_sanity_check_dev(ixs); */ ++ ++ ipsec_xmit_sanity_check_skb(ixs); ++ ++ ipsec_xmit_adjust_hard_header(ixs); ++ ++ stat = ipsec_xmit_encap_bundle(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ /* SA processing failed */ ++ } ++ ++ ipsec_xmit_hard_header_restore(); ++} ++#endif ++ ++DEBUG_NO_STATIC struct net_device_stats * ++ipsec_mast_get_stats(struct device *dev) ++{ ++ return &(((struct ipsecpriv *)(dev->priv))->mystats); ++} ++ ++/* ++ * Revectored calls. ++ * For each of these calls, a field exists in our private structure. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_mast_hard_header(struct sk_buff *skb, struct device *dev, ++ unsigned short type, void *daddr, void *saddr, unsigned len) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ struct device *tmp; ++ int ret; ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(skb == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "no skb...\n"); ++ return -ENODATA; ++ } ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "no device...\n"); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "skb->dev=%s dev=%s.\n", ++ skb->dev ? skb->dev->name : "NULL", ++ dev->name); ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "no private space associated with dev=%s\n", ++ dev->name ? dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "no physical device associated with dev=%s\n", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ /* check if we have to send a IPv6 packet. It might be a Router ++ Solicitation, where the building of the packet happens in ++ reverse order: ++ 1. ll hdr, ++ 2. IPv6 hdr, ++ 3. ICMPv6 hdr ++ -> skb->nh.raw is still uninitialized when this function is ++ called!! If this is no IPv6 packet, we can print debugging ++ messages, otherwise we skip all debugging messages and just ++ build the ll header */ ++ if(type != ETH_P_IPV6) { ++ /* execute this only, if we don't have to build the ++ header for a IPv6 packet */ ++ if(!prv->hard_header) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ", ++ saddr, ++ daddr, ++ len, ++ type, ++ dev->name); ++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++#define da ((struct device *)(prv->dev))->dev_addr ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_hard_header: " ++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ", ++ saddr, ++ daddr, ++ len, ++ type, ++ dev->name, ++ prv->dev->name, ++ da[0], da[1], da[2], da[3], da[4], da[5]); ++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++ } else { ++ KLIPS_PRINT(debug_mast, ++ "klips_debug:ipsec_mast_hard_header: " ++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n"); ++ } ++ tmp = skb->dev; ++ skb->dev = prv->dev; ++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len); ++ skb->dev = tmp; ++ return ret; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_rebuild_header(struct sk_buff *skb) ++{ ++ struct ipsecpriv *prv = skb->dev->priv; ++ struct device *tmp; ++ int ret; ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(skb->dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_rebuild_header: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_rebuild_header: " ++ "no private space associated with dev=%s", ++ skb->dev->name ? skb->dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_rebuild_header: " ++ "no physical device associated with dev=%s", ++ skb->dev->name ? skb->dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ if(!prv->rebuild_header) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_rebuild_header: " ++ "physical device has been detached, packet dropped skb->dev=%s->NULL ", ++ skb->dev->name); ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast: " ++ "Revectored rebuild_header dev=%s->%s ", ++ skb->dev->name, prv->dev->name); ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++ tmp = skb->dev; ++ skb->dev = prv->dev; ++ ++ ret = prv->rebuild_header(skb); ++ skb->dev = tmp; ++ return ret; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_set_mac_address(struct device *dev, void *addr) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_set_mac_address: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_set_mac_address: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_set_mac_address: " ++ "no physical device associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ if(!prv->set_mac_address) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_set_mac_address: " ++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n", ++ dev->name); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_set_mac_address: " ++ "Revectored dev=%s->%s addr=0p%p\n", ++ dev->name, prv->dev->name, addr); ++ return prv->set_mac_address(prv->dev, addr); ++ ++} ++ ++DEBUG_NO_STATIC void ++ipsec_mast_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_cache_update: " ++ "no device..."); ++ return; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_cache_update: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_cache_update: " ++ "no physical device associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return; ++ } ++ ++ if(!prv->header_cache_update) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_cache_update: " ++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n", ++ dev->name); ++ return; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast: " ++ "Revectored cache_update\n"); ++ prv->header_cache_update(hh, prv->dev, haddr); ++ return; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_neigh_setup(struct neighbour *n) ++{ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_neigh_setup:\n"); ++ ++ if (n->nud_state == NUD_NONE) { ++ n->ops = &arp_broken_ops; ++ n->output = n->ops->output; ++ } ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_neigh_setup_dev(struct device *dev, struct neigh_parms *p) ++{ ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_neigh_setup_dev: " ++ "setting up %s\n", ++ dev ? dev->name : "NULL"); ++ ++ if (p->tbl->family == AF_INET) { ++ p->neigh_setup = ipsec_mast_neigh_setup; ++ p->ucast_probes = 0; ++ p->mcast_probes = 0; ++ } ++ return 0; ++} ++ ++/* ++ * We call the attach routine to attach another device. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_mast_attach(struct device *dev, struct device *physdev) ++{ ++ int i; ++ struct ipsecpriv *prv = dev->priv; ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_attach: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_attach: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODATA; ++ } ++ ++ prv->dev = physdev; ++ prv->hard_start_xmit = physdev->hard_start_xmit; ++ prv->get_stats = physdev->get_stats; ++ ++ if (physdev->hard_header) { ++ prv->hard_header = physdev->hard_header; ++ dev->hard_header = ipsec_mast_hard_header; ++ } else ++ dev->hard_header = NULL; ++ ++ if (physdev->rebuild_header) { ++ prv->rebuild_header = physdev->rebuild_header; ++ dev->rebuild_header = ipsec_mast_rebuild_header; ++ } else ++ dev->rebuild_header = NULL; ++ ++ if (physdev->set_mac_address) { ++ prv->set_mac_address = physdev->set_mac_address; ++ dev->set_mac_address = ipsec_mast_set_mac_address; ++ } else ++ dev->set_mac_address = NULL; ++ ++ if (physdev->header_cache_update) { ++ prv->header_cache_update = physdev->header_cache_update; ++ dev->header_cache_update = ipsec_mast_cache_update; ++ } else ++ dev->header_cache_update = NULL; ++ ++ dev->hard_header_len = physdev->hard_header_len; ++ ++/* prv->neigh_setup = physdev->neigh_setup; */ ++ dev->neigh_setup = ipsec_mast_neigh_setup_dev; ++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */ ++ prv->mtu = physdev->mtu; ++ ++#ifdef PHYSDEV_TYPE ++ dev->type = physdev->type; /* ARPHRD_MAST; */ ++#endif /* PHYSDEV_TYPE */ ++ ++ dev->addr_len = physdev->addr_len; ++ for (i=0; iaddr_len; i++) { ++ dev->dev_addr[i] = physdev->dev_addr[i]; ++ } ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_mast & DB_MAST_INIT) { ++ printk(KERN_INFO "klips_debug:ipsec_mast_attach: " ++ "physical device %s being attached has HW address: %2x", ++ physdev->name, physdev->dev_addr[0]); ++ for (i=1; i < physdev->addr_len; i++) { ++ printk(":%02x", physdev->dev_addr[i]); ++ } ++ printk("\n"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ return 0; ++} ++ ++/* ++ * We call the detach routine to detach the ipsec mast from another device. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_mast_detach(struct device *dev) ++{ ++ int i; ++ struct ipsecpriv *prv = dev->priv; ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_detach: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC, ++ "klips_debug:ipsec_mast_detach: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODATA; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_detach: " ++ "physical device %s being detached from virtual device %s\n", ++ prv->dev ? prv->dev->name : "NULL", ++ dev->name); ++ ++ prv->dev = NULL; ++ prv->hard_start_xmit = NULL; ++ prv->get_stats = NULL; ++ ++ prv->hard_header = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->hard_header = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ prv->rebuild_header = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->rebuild_header = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ prv->set_mac_address = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->set_mac_address = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ prv->header_cache_update = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->header_cache_update = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++#ifdef DETACH_AND_DOWN ++ dev->neigh_setup = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ dev->hard_header_len = 0; ++#ifdef DETACH_AND_DOWN ++ dev->mtu = 0; ++#endif /* DETACH_AND_DOWN */ ++ prv->mtu = 0; ++ for (i=0; idev_addr[i] = 0; ++ } ++ dev->addr_len = 0; ++#ifdef PHYSDEV_TYPE ++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ ++#endif /* PHYSDEV_TYPE */ ++ ++ return 0; ++} ++ ++/* ++ * We call the clear routine to detach all ipsec masts from other devices. ++ */ ++DEBUG_NO_STATIC int ++ipsec_mast_clear(void) ++{ ++ int i; ++ struct device *ipsecdev = NULL, *prvdev; ++ struct ipsecpriv *prv; ++ char name[9]; ++ int ret; ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_clear: .\n"); ++ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ sprintf(name, IPSEC_DEV_FORMAT, i); ++ if((ipsecdev = ipsec_dev_get(name)) != NULL) { ++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) { ++ prvdev = (struct device *)(prv->dev); ++ if(prvdev) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_clear: " ++ "physical device for device %s is %s\n", ++ name, prvdev->name); ++ if((ret = ipsec_mast_detach(ipsecdev))) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_clear: " ++ "error %d detatching device %s from device %s.\n", ++ ret, name, prvdev->name); ++ return ret; ++ } ++ } ++ } ++ } ++ } ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_mast_ioctl(struct device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct ipsecmastconf *cf = (struct ipsecmastconf *)&ifr->ifr_data; ++ struct ipsecpriv *prv = dev->priv; ++ struct device *them; /* physical device */ ++#ifdef CONFIG_IP_ALIAS ++ char *colon; ++ char realphysname[IFNAMSIZ]; ++#endif /* CONFIG_IP_ALIAS */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "device not supplied.\n"); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "tncfg service call #%d for dev=%s\n", ++ cmd, ++ dev->name ? dev->name : "NULL"); ++ switch (cmd) { ++ /* attach a virtual ipsec? device to a physical device */ ++ case IPSEC_SET_DEV: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "calling ipsec_mast_attatch...\n"); ++#ifdef CONFIG_IP_ALIAS ++ /* If this is an IP alias interface, get its real physical name */ ++ strncpy(realphysname, cf->cf_name, IFNAMSIZ); ++ realphysname[IFNAMSIZ-1] = 0; ++ colon = strchr(realphysname, ':'); ++ if (colon) *colon = 0; ++ them = ipsec_dev_get(realphysname); ++#else /* CONFIG_IP_ALIAS */ ++ them = ipsec_dev_get(cf->cf_name); ++#endif /* CONFIG_IP_ALIAS */ ++ ++ if (them == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "physical device %s requested is null\n", ++ cf->cf_name); ++ return -ENXIO; ++ } ++ ++#if 0 ++ if (them->flags & IFF_UP) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "physical device %s requested is not up.\n", ++ cf->cf_name); ++ return -ENXIO; ++ } ++#endif ++ ++ if (prv && prv->dev) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "virtual device is already connected to %s.\n", ++ prv->dev->name ? prv->dev->name : "NULL"); ++ return -EBUSY; ++ } ++ return ipsec_mast_attach(dev, them); ++ ++ case IPSEC_DEL_DEV: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "calling ipsec_mast_detatch.\n"); ++ if (! prv->dev) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "physical device not connected.\n"); ++ return -ENODEV; ++ } ++ return ipsec_mast_detach(dev); ++ ++ case IPSEC_CLR_DEV: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "calling ipsec_mast_clear.\n"); ++ return ipsec_mast_clear(); ++ ++ default: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_ioctl: " ++ "unknown command %d.\n", ++ cmd); ++ return -EOPNOTSUPP; ++ } ++} ++ ++int ++ipsec_mast_device_event(struct notifier_block *unused, unsigned long event, void *ptr) ++{ ++ struct device *dev = ptr; ++ struct device *ipsec_dev; ++ struct ipsecpriv *priv; ++ char name[9]; ++ int i; ++ ++ if (dev == NULL) { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "dev=NULL for event type %ld.\n", ++ event); ++ return(NOTIFY_DONE); ++ } ++ ++ /* check for loopback devices */ ++ if (dev && (dev->flags & IFF_LOOPBACK)) { ++ return(NOTIFY_DONE); ++ } ++ ++ switch (event) { ++ case NETDEV_DOWN: ++ /* look very carefully at the scope of these compiler ++ directives before changing anything... -- RGB */ ++ ++ case NETDEV_UNREGISTER: ++ switch (event) { ++ case NETDEV_DOWN: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_DOWN dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) { ++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n", ++ dev->name); ++ } ++ break; ++ case NETDEV_UNREGISTER: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_UNREGISTER dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ break; ++ } ++ ++ /* find the attached physical device and detach it. */ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ sprintf(name, IPSEC_DEV_FORMAT, i); ++ ipsec_dev = ipsec_dev_get(name); ++ if(ipsec_dev) { ++ priv = (struct ipsecpriv *)(ipsec_dev->priv); ++ if(priv) { ++ ; ++ if(((struct device *)(priv->dev)) == dev) { ++ /* dev_close(ipsec_dev); */ ++ /* return */ ipsec_mast_detach(ipsec_dev); ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "device '%s' has been detached.\n", ++ ipsec_dev->name); ++ break; ++ } ++ } else { ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "device '%s' has no private data space!\n", ++ ipsec_dev->name); ++ } ++ } ++ } ++ break; ++ case NETDEV_UP: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_UP dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_REBOOT: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_REBOOT dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGE: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_CHANGE dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ break; ++ case NETDEV_REGISTER: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_REGISTER dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGEMTU: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n", ++ dev->name, ++ dev->mtu); ++ break; ++ case NETDEV_CHANGEADDR: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_CHANGEADDR dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_GOING_DOWN: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_GOING_DOWN dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGENAME: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "NETDEV_CHANGENAME dev=%s\n", ++ dev->name); ++ break; ++ default: ++ KLIPS_PRINT(debug_mast & DB_MAST_INIT, ++ "klips_debug:ipsec_mast_device_event: " ++ "event type %ld unrecognised for dev=%s\n", ++ event, ++ dev->name); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++/* ++ * Called when an ipsec mast device is initialized. ++ * The ipsec mast device structure is passed to us. ++ */ ++ ++int ++ipsec_mast_init(struct device *dev) ++{ ++ int i; ++ ++ KLIPS_PRINT(debug_mast, ++ "klips_debug:ipsec_mast_init: " ++ "allocating %lu bytes initialising device: %s\n", ++ (unsigned long) sizeof(struct ipsecpriv), ++ dev->name ? dev->name : "NULL"); ++ ++ /* Add our mast functions to the device */ ++ dev->open = ipsec_mast_open; ++ dev->stop = ipsec_mast_close; ++ dev->hard_start_xmit = ipsec_mast_start_xmit; ++ dev->get_stats = ipsec_mast_get_stats; ++ ++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL); ++ if (dev->priv == NULL) ++ return -ENOMEM; ++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv)); ++ ++ for(i = 0; i < sizeof(zeroes); i++) { ++ ((__u8*)(zeroes))[i] = 0; ++ } ++ ++ dev->set_multicast_list = NULL; ++ dev->do_ioctl = ipsec_mast_ioctl; ++ dev->hard_header = NULL; ++ dev->rebuild_header = NULL; ++ dev->set_mac_address = NULL; ++ dev->header_cache_update= NULL; ++ dev->neigh_setup = ipsec_mast_neigh_setup_dev; ++ dev->hard_header_len = 0; ++ dev->mtu = 0; ++ dev->addr_len = 0; ++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ /* ARPHRD_ETHER; */ ++ dev->tx_queue_len = 10; /* Small queue */ ++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */ ++ ++ /* New-style flags. */ ++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */; ++ dev_init_buffers(dev); ++ ++ /* We're done. Have I forgotten anything? */ ++ return 0; ++} ++ ++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ++/* Module specific interface (but it links with the rest of IPSEC) */ ++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ++ ++int ++ipsec_mast_probe(struct device *dev) ++{ ++ ipsec_mast_init(dev); ++ return 0; ++} ++ ++int ++ipsec_mast_init_devices(void) ++{ ++ return 0; ++} ++ ++/* void */ ++int ++ipsec_mast_cleanup_devices(void) ++{ ++ int error = 0; ++ int i; ++ char name[10]; ++ struct device *dev_mast; ++ ++ for(i = 0; i < ipsec_mastdevice_count; i++) { ++ sprintf(name, MAST_DEV_FORMAT, i); ++ if((dev_mast = ipsec_dev_get(name)) == NULL) { ++ break; ++ } ++ unregister_netdev(dev_mast); ++ kfree(dev_mast->priv); ++ dev_mast->priv=NULL; ++ } ++ return error; ++} ++ ++/* ++ * $Log: ipsec_mast.c,v $ ++ * Revision 1.3 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.2.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.2 2003/06/22 20:06:17 mcr ++ * refactored mast code still had lots of ipsecX junk in it. ++ * ++ * Revision 1.1 2003/02/12 19:31:12 rgb ++ * Refactored from ipsec_tunnel.c ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_md5c.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,448 @@ ++/* ++ * RCSID $Id: ipsec_md5c.c,v 1.8 2004/04/06 02:49:26 mcr Exp $ ++ */ ++ ++/* ++ * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic ++ * changes to accomodate it in the kernel by ji. ++ */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_md5h.h" ++ ++/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm ++ */ ++ ++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All ++rights reserved. ++ ++License to copy and use this software is granted provided that it ++is identified as the "RSA Data Security, Inc. MD5 Message-Digest ++Algorithm" in all material mentioning or referencing this software ++or this function. ++ ++License is also granted to make and use derivative works provided ++that such works are identified as "derived from the RSA Data ++Security, Inc. MD5 Message-Digest Algorithm" in all material ++mentioning or referencing the derived work. ++ ++RSA Data Security, Inc. makes no representations concerning either ++the merchantability of this software or the suitability of this ++software for any particular purpose. It is provided "as is" ++without express or implied warranty of any kind. ++ ++These notices must be retained in any copies of any part of this ++documentation and/or software. ++ */ ++ ++/* ++ * Additions by JI ++ * ++ * HAVEMEMCOPY is defined if mem* routines are available ++ * ++ * HAVEHTON is defined if htons() and htonl() can be used ++ * for big/little endian conversions ++ * ++ */ ++ ++#define HAVEMEMCOPY ++#ifdef __LITTLE_ENDIAN ++#define LITTLENDIAN ++#endif ++#ifdef __BIG_ENDIAN ++#define BIGENDIAN ++#endif ++ ++/* Constants for MD5Transform routine. ++ */ ++ ++#define S11 7 ++#define S12 12 ++#define S13 17 ++#define S14 22 ++#define S21 5 ++#define S22 9 ++#define S23 14 ++#define S24 20 ++#define S31 4 ++#define S32 11 ++#define S33 16 ++#define S34 23 ++#define S41 6 ++#define S42 10 ++#define S43 15 ++#define S44 21 ++ ++static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); ++ ++#ifdef LITTLEENDIAN ++#define Encode MD5_memcpy ++#define Decode MD5_memcpy ++#else ++static void Encode PROTO_LIST ++ ((unsigned char *, UINT4 *, unsigned int)); ++static void Decode PROTO_LIST ++ ((UINT4 *, unsigned char *, unsigned int)); ++#endif ++ ++#ifdef HAVEMEMCOPY ++/* no need to include here; defines these */ ++#define MD5_memcpy memcpy ++#define MD5_memset memset ++#else ++#ifdef HAVEBCOPY ++#define MD5_memcpy(_a,_b,_c) bcopy((_b),(_a),(_c)) ++#define MD5_memset(_a,_b,_c) bzero((_a),(_c)) ++#else ++static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); ++static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); ++#endif ++#endif ++static unsigned char PADDING[64] = { ++ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* F, G, H and I are basic MD5 functions. ++ */ ++#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) ++#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) ++#define H(x, y, z) ((x) ^ (y) ^ (z)) ++#define I(x, y, z) ((y) ^ ((x) | (~z))) ++ ++/* ROTATE_LEFT rotates x left n bits. ++ */ ++#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) ++ ++/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. ++Rotation is separate from addition to prevent recomputation. ++ */ ++#define FF(a, b, c, d, x, s, ac) { \ ++ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ (a) += (b); \ ++ } ++#define GG(a, b, c, d, x, s, ac) { \ ++ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ (a) += (b); \ ++ } ++#define HH(a, b, c, d, x, s, ac) { \ ++ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ (a) += (b); \ ++ } ++#define II(a, b, c, d, x, s, ac) { \ ++ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ ++ (a) = ROTATE_LEFT ((a), (s)); \ ++ (a) += (b); \ ++ } ++ ++/* ++ * MD5 initialization. Begins an MD5 operation, writing a new context. ++ */ ++void MD5Init(void *vcontext) ++{ ++ MD5_CTX *context = vcontext; ++ ++ context->count[0] = context->count[1] = 0; ++ /* Load magic initialization constants. ++*/ ++ context->state[0] = 0x67452301; ++ context->state[1] = 0xefcdab89; ++ context->state[2] = 0x98badcfe; ++ context->state[3] = 0x10325476; ++} ++ ++/* MD5 block update operation. Continues an MD5 message-digest ++ operation, processing another message block, and updating the ++ context. ++ */ ++void MD5Update (vcontext, input, inputLen) ++ void *vcontext; ++ unsigned char *input; /* input block */ ++ __u32 inputLen; /* length of input block */ ++{ ++ MD5_CTX *context = vcontext; ++ __u32 i; ++ unsigned int index, partLen; ++ ++ /* Compute number of bytes mod 64 */ ++ index = (unsigned int)((context->count[0] >> 3) & 0x3F); ++ ++ /* Update number of bits */ ++ if ((context->count[0] += ((UINT4)inputLen << 3)) ++ < ((UINT4)inputLen << 3)) ++ context->count[1]++; ++ context->count[1] += ((UINT4)inputLen >> 29); ++ ++ partLen = 64 - index; ++ ++ /* Transform as many times as possible. ++*/ ++ if (inputLen >= partLen) { ++ MD5_memcpy ++ ((POINTER)&context->buffer[index], (POINTER)input, partLen); ++ MD5Transform (context->state, context->buffer); ++ ++ for (i = partLen; i + 63 < inputLen; i += 64) ++ MD5Transform (context->state, &input[i]); ++ ++ index = 0; ++ } ++ else ++ i = 0; ++ ++ /* Buffer remaining input */ ++ MD5_memcpy ++ ((POINTER)&context->buffer[index], (POINTER)&input[i], ++ inputLen-i); ++} ++ ++/* MD5 finalization. Ends an MD5 message-digest operation, writing the ++ the message digest and zeroizing the context. ++ */ ++void MD5Final (digest, vcontext) ++unsigned char digest[16]; /* message digest */ ++void *vcontext; /* context */ ++{ ++ MD5_CTX *context = vcontext; ++ unsigned char bits[8]; ++ unsigned int index, padLen; ++ ++ /* Save number of bits */ ++ Encode (bits, context->count, 8); ++ ++ /* Pad out to 56 mod 64. ++*/ ++ index = (unsigned int)((context->count[0] >> 3) & 0x3f); ++ padLen = (index < 56) ? (56 - index) : (120 - index); ++ MD5Update (context, PADDING, padLen); ++ ++ /* Append length (before padding) */ ++ MD5Update (context, bits, 8); ++ ++ if (digest != NULL) /* Bill Simpson's padding */ ++ { ++ /* store state in digest */ ++ Encode (digest, context->state, 16); ++ ++ /* Zeroize sensitive information. ++ */ ++ MD5_memset ((POINTER)context, 0, sizeof (*context)); ++ } ++} ++ ++/* MD5 basic transformation. Transforms state based on block. ++ */ ++static void MD5Transform (state, block) ++UINT4 state[4]; ++unsigned char block[64]; ++{ ++ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; ++ ++ Decode (x, block, 64); ++ ++ /* Round 1 */ ++ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ ++ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ ++ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ ++ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ ++ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ ++ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ ++ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ ++ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ ++ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ ++ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ ++ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ ++ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ ++ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ ++ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ ++ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ ++ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ ++ ++ /* Round 2 */ ++ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ ++ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ ++ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ ++ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ ++ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ ++ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ ++ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ ++ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ ++ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ ++ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ ++ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ ++ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ ++ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ ++ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ ++ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ ++ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ ++ ++ /* Round 3 */ ++ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ ++ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ ++ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ ++ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ ++ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ ++ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ ++ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ ++ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ ++ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ ++ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ ++ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ ++ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ ++ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ ++ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ ++ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ ++ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ ++ ++ /* Round 4 */ ++ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ ++ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ ++ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ ++ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ ++ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ ++ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ ++ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ ++ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ ++ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ ++ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ ++ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ ++ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ ++ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ ++ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ ++ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ ++ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ ++ ++ state[0] += a; ++ state[1] += b; ++ state[2] += c; ++ state[3] += d; ++ ++ /* Zeroize sensitive information. ++*/ ++ MD5_memset ((POINTER)x, 0, sizeof (x)); ++} ++ ++#ifndef LITTLEENDIAN ++ ++/* Encodes input (UINT4) into output (unsigned char). Assumes len is ++ a multiple of 4. ++ */ ++static void Encode (output, input, len) ++unsigned char *output; ++UINT4 *input; ++unsigned int len; ++{ ++ unsigned int i, j; ++ ++ for (i = 0, j = 0; j < len; i++, j += 4) { ++ output[j] = (unsigned char)(input[i] & 0xff); ++ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); ++ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); ++ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); ++ } ++} ++ ++/* Decodes input (unsigned char) into output (UINT4). Assumes len is ++ a multiple of 4. ++ */ ++static void Decode (output, input, len) ++UINT4 *output; ++unsigned char *input; ++unsigned int len; ++{ ++ unsigned int i, j; ++ ++ for (i = 0, j = 0; j < len; i++, j += 4) ++ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | ++ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); ++} ++ ++#endif ++ ++#ifndef HAVEMEMCOPY ++#ifndef HAVEBCOPY ++/* Note: Replace "for loop" with standard memcpy if possible. ++ */ ++ ++static void MD5_memcpy (output, input, len) ++POINTER output; ++POINTER input; ++unsigned int len; ++{ ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) ++ ++ output[i] = input[i]; ++} ++ ++/* Note: Replace "for loop" with standard memset if possible. ++ */ ++ ++static void MD5_memset (output, value, len) ++POINTER output; ++int value; ++unsigned int len; ++{ ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) ++ ((char *)output)[i] = (char)value; ++} ++#endif ++#endif ++ ++/* ++ * $Log: ipsec_md5c.c,v $ ++ * Revision 1.8 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.7 2002/09/10 01:45:14 mcr ++ * changed type of MD5_CTX and SHA1_CTX to void * so that ++ * the function prototypes would match, and could be placed ++ * into a pointer to a function. ++ * ++ * Revision 1.6 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.5 2002/04/24 07:36:28 mcr ++ * Moved from ./klips/net/ipsec/ipsec_md5c.c,v ++ * ++ * Revision 1.4 1999/12/13 13:59:12 rgb ++ * Quick fix to argument size to Update bugs. ++ * ++ * Revision 1.3 1999/05/21 18:09:28 henry ++ * unnecessary include causes trouble in 2.2 ++ * ++ * Revision 1.2 1999/04/06 04:54:26 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.1 1998/06/18 21:27:48 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.2 1998/04/23 20:54:02 rgb ++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when ++ * verified. ++ * ++ * Revision 1.1 1998/04/09 03:06:08 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.3 1996/11/20 14:48:53 ji ++ * Release update only. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_proc.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1127 @@ ++/* ++ * @(#) /proc file system interface code. ++ * ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs ++ * 2001 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * Split out from ipsec_init.c version 1.70. ++ */ ++ ++char ipsec_proc_c_version[] = "RCSID $Id: ipsec_proc.c,v 1.30 2004/04/25 21:23:11 ken Exp $"; ++ ++ ++#include ++#include ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include /* struct sockaddr_in */ ++#include ++#include ++#ifdef SPINLOCK ++#ifdef SPINLOCK_23 ++#include /* *lock* */ ++#else /* SPINLOCK_23 */ ++#include /* *lock* */ ++#endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++#include ++#include ++#endif /* NET_21 */ ++#include ++#include ++#ifdef CONFIG_PROC_FS ++#include ++#endif /* CONFIG_PROC_FS */ ++#ifdef NETLINK_SOCK ++#include ++#else ++#include ++#endif ++ ++#include "openswan/radij.h" ++ ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_stats.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_xmit.h" ++ ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++#include "openswan/ipcomp.h" ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#include "openswan/ipsec_proto.h" ++ ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++ ++#ifdef IPSEC_PROC_SUBDIRS ++static struct proc_dir_entry *proc_net_ipsec_dir = NULL; ++static struct proc_dir_entry *proc_eroute_dir = NULL; ++static struct proc_dir_entry *proc_spi_dir = NULL; ++static struct proc_dir_entry *proc_spigrp_dir = NULL; ++static struct proc_dir_entry *proc_birth_dir = NULL; ++static struct proc_dir_entry *proc_stats_dir = NULL; ++#endif ++ ++struct ipsec_birth_reply ipsec_ipv4_birth_packet; ++struct ipsec_birth_reply ipsec_ipv6_birth_packet; ++ ++#define DECREMENT_UNSIGNED(X, amount) ((amount < (X)) ? (X)-amount : 0) ++ ++extern int ipsec_xform_get_info(char *buffer, char **start, ++ off_t offset, int length IPSEC_PROC_LAST_ARG); ++ ++ ++/* ipsec_snprintf: like snprintf except ++ * - size is signed and a negative value is treated as if it were 0 ++ * - the returned result is never negative -- ++ * an error generates a "?" or null output (depending on space). ++ * (Our callers are too lazy to check for an error return.) ++ * ++ * @param buf String buffer ++ * @param size Size of the string ++ * @param fmt printf string ++ * @param ... Variables to be displayed in fmt ++ * @return int Return code ++ */ ++int ipsec_snprintf(char *buf, ssize_t size, const char *fmt, ...) ++{ ++ va_list args; ++ int i; ++ size_t possize = size < 0? 0 : size; ++ va_start(args, fmt); ++ i = vsnprintf(buf,possize,fmt,args); ++ va_end(args); ++ if (i < 0) { ++ /* create empty output in place of error */ ++ i = 0; ++ if (size > 0) { ++ *buf = '\0'; ++ } ++ } ++ return i; ++} ++ ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_eroute_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ struct wsbuf w = {buffer, length, offset, 0, 0}; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_radij & DB_RJ_DUMPTREES) ++ rj_dumptrees(); /* XXXXXXXXX */ ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_eroute_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ spin_lock_bh(&eroute_lock); ++ ++ rj_walktree(rnh, ipsec_rj_walker_procprint, &w); ++/* rj_walktree(mask_rjhead, ipsec_rj_walker_procprint, &w); */ ++ ++ spin_unlock_bh(&eroute_lock); ++ ++ *start = buffer + (offset - w.begin); /* Start of wanted data */ ++ return w.len - (offset - w.begin); ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_spi_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ const int max_content = length > 0? length-1 : 0; ++ int len = 0; ++ off_t begin = 0; ++ int i; ++ struct ipsec_sa *sa_p; ++ char sa[SATOT_BUF]; ++ char buf_s[SUBNETTOA_BUF]; ++ char buf_d[SUBNETTOA_BUF]; ++ size_t sa_len; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_spi_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ for (i = 0; i < SADB_HASHMOD; i++) { ++ for (sa_p = ipsec_sadb_hash[i]; ++ sa_p; ++ sa_p = sa_p->ips_hnext) { ++ atomic_inc(&sa_p->ips_refcount); ++ sa_len = satot(&sa_p->ips_said, 'x', sa, sizeof(sa)); ++ len += ipsec_snprintf(buffer+len, length-len, "%s ", ++ sa_len ? sa : " (error)"); ++ ++ len += ipsec_snprintf(buffer+len, length-len, "%s%s%s", ++ IPS_XFORM_NAME(sa_p)); ++ ++ len += ipsec_snprintf(buffer+len, length-len, ": dir=%s", ++ (sa_p->ips_flags & EMT_INBOUND) ? ++ "in " : "out"); ++ ++ if(sa_p->ips_addr_s) { ++ addrtoa(((struct sockaddr_in*)(sa_p->ips_addr_s))->sin_addr, ++ 0, buf_s, sizeof(buf_s)); ++ len += ipsec_snprintf(buffer+len, length-len, " src=%s", ++ buf_s); ++ } ++ ++ if((sa_p->ips_said.proto == IPPROTO_IPIP) ++ && (sa_p->ips_flags & SADB_X_SAFLAGS_INFLOW)) { ++ subnettoa(sa_p->ips_flow_s.u.v4.sin_addr, ++ sa_p->ips_mask_s.u.v4.sin_addr, ++ 0, ++ buf_s, ++ sizeof(buf_s)); ++ ++ subnettoa(sa_p->ips_flow_d.u.v4.sin_addr, ++ sa_p->ips_mask_d.u.v4.sin_addr, ++ 0, ++ buf_d, ++ sizeof(buf_d)); ++ ++ len += ipsec_snprintf(buffer+len, length-len, " policy=%s->%s", ++ buf_s, buf_d); ++ } ++ ++ if(sa_p->ips_iv_bits) { ++ int j; ++ len += ipsec_snprintf(buffer+len, length-len, " iv_bits=%dbits iv=0x", ++ sa_p->ips_iv_bits); ++ ++ for(j = 0; j < sa_p->ips_iv_bits / 8; j++) { ++ len += ipsec_snprintf(buffer+len, length-len, "%02x", ++ (__u32)((__u8*)(sa_p->ips_iv))[j]); ++ } ++ } ++ ++ if(sa_p->ips_encalg || sa_p->ips_authalg) { ++ if(sa_p->ips_replaywin) { ++ len += ipsec_snprintf(buffer+len, length-len, " ooowin=%d", ++ sa_p->ips_replaywin); ++ } ++ if(sa_p->ips_errs.ips_replaywin_errs) { ++ len += ipsec_snprintf(buffer+len, length-len, " ooo_errs=%d", ++ sa_p->ips_errs.ips_replaywin_errs); ++ } ++ if(sa_p->ips_replaywin_lastseq) { ++ len += ipsec_snprintf(buffer+len, length-len, " seq=%d", ++ sa_p->ips_replaywin_lastseq); ++ } ++ if(sa_p->ips_replaywin_bitmap) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) ++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%Lx", ++ sa_p->ips_replaywin_bitmap); ++#else ++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%x%08x", ++ (__u32)(sa_p->ips_replaywin_bitmap >> 32), ++ (__u32)sa_p->ips_replaywin_bitmap); ++#endif ++ } ++ if(sa_p->ips_replaywin_maxdiff) { ++ len += ipsec_snprintf(buffer+len, length-len, " max_seq_diff=%d", ++ sa_p->ips_replaywin_maxdiff); ++ } ++ } ++ if(sa_p->ips_flags & ~EMT_INBOUND) { ++ len += ipsec_snprintf(buffer+len, length-len, " flags=0x%x", ++ sa_p->ips_flags & ~EMT_INBOUND); ++ len += ipsec_snprintf(buffer+len, length-len, "<"); ++ /* flag printing goes here */ ++ len += ipsec_snprintf(buffer+len, length-len, ">"); ++ } ++ if(sa_p->ips_auth_bits) { ++ len += ipsec_snprintf(buffer+len, length-len, " alen=%d", ++ sa_p->ips_auth_bits); ++ } ++ if(sa_p->ips_key_bits_a) { ++ len += ipsec_snprintf(buffer+len, length-len, " aklen=%d", ++ sa_p->ips_key_bits_a); ++ } ++ if(sa_p->ips_errs.ips_auth_errs) { ++ len += ipsec_snprintf(buffer+len, length-len, " auth_errs=%d", ++ sa_p->ips_errs.ips_auth_errs); ++ } ++ if(sa_p->ips_key_bits_e) { ++ len += ipsec_snprintf(buffer+len, length-len, " eklen=%d", ++ sa_p->ips_key_bits_e); ++ } ++ if(sa_p->ips_errs.ips_encsize_errs) { ++ len += ipsec_snprintf(buffer+len, length-len, " encr_size_errs=%d", ++ sa_p->ips_errs.ips_encsize_errs); ++ } ++ if(sa_p->ips_errs.ips_encpad_errs) { ++ len += ipsec_snprintf(buffer+len, length-len, " encr_pad_errs=%d", ++ sa_p->ips_errs.ips_encpad_errs); ++ } ++ ++ len += ipsec_snprintf(buffer+len, length-len, " life(c,s,h)="); ++ ++ len += ipsec_lifetime_format(buffer + len, ++ length - len, ++ "alloc", ++ ipsec_life_countbased, ++ &sa_p->ips_life.ipl_allocations); ++ ++ len += ipsec_lifetime_format(buffer + len, ++ length - len, ++ "bytes", ++ ipsec_life_countbased, ++ &sa_p->ips_life.ipl_bytes); ++ ++ len += ipsec_lifetime_format(buffer + len, ++ length - len, ++ "addtime", ++ ipsec_life_timebased, ++ &sa_p->ips_life.ipl_addtime); ++ ++ len += ipsec_lifetime_format(buffer + len, ++ length - len, ++ "usetime", ++ ipsec_life_timebased, ++ &sa_p->ips_life.ipl_usetime); ++ ++ len += ipsec_lifetime_format(buffer + len, ++ length - len, ++ "packets", ++ ipsec_life_countbased, ++ &sa_p->ips_life.ipl_packets); ++ ++ if(sa_p->ips_life.ipl_usetime.ipl_last) { /* XXX-MCR should be last? */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) ++ len += ipsec_snprintf(buffer+len, length-len, " idle=%Ld", ++ jiffies / HZ - sa_p->ips_life.ipl_usetime.ipl_last); ++#else ++ len += ipsec_snprintf(buffer+len, length-len, " idle=%lu", ++ jiffies / HZ - (unsigned long)sa_p->ips_life.ipl_usetime.ipl_last); ++#endif ++ } ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++ if(sa_p->ips_said.proto == IPPROTO_COMP && ++ (sa_p->ips_comp_ratio_dbytes || ++ sa_p->ips_comp_ratio_cbytes)) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) ++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%Ld:%Ld", ++ sa_p->ips_comp_ratio_dbytes, ++ sa_p->ips_comp_ratio_cbytes); ++#else ++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%lu:%lu", ++ (unsigned long)sa_p->ips_comp_ratio_dbytes, ++ (unsigned long)sa_p->ips_comp_ratio_cbytes); ++#endif ++ } ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if(sa_p->ips_natt_type != 0) { ++ char *natttype_name; ++ ++ switch(sa_p->ips_natt_type) ++ { ++ case ESPINUDP_WITH_NON_IKE: ++ natttype_name="nonike"; ++ break; ++ case ESPINUDP_WITH_NON_ESP: ++ natttype_name="nonesp"; ++ break; ++ default: ++ natttype_name = "unknown"; ++ break; ++ } ++ ++ len += ipsec_snprintf(buffer + len, length-len, " natencap=%s", ++ natttype_name); ++ ++ len += ipsec_snprintf(buffer + len, length-len, " natsport=%d", ++ sa_p->ips_natt_sport); ++ ++ len += ipsec_snprintf(buffer + len,length-len, " natdport=%d", ++ sa_p->ips_natt_dport); ++ } ++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ ++ ++ len += ipsec_snprintf(buffer + len,length-len, " refcount=%d", ++ atomic_read(&sa_p->ips_refcount)); ++ ++ len += ipsec_snprintf(buffer+len, length-len, " ref=%d", ++ sa_p->ips_ref); ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_xform) { ++ len += ipsec_snprintf(buffer+len, length-len, " reftable=%lu refentry=%lu", ++ (unsigned long)IPsecSAref2table(sa_p->ips_ref), ++ (unsigned long)IPsecSAref2entry(sa_p->ips_ref)); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ len += ipsec_snprintf(buffer+len, length-len, "\n"); ++ ++ atomic_dec(&sa_p->ips_refcount); ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loops */ ++ len = max_content; /* truncate crap */ ++ goto done_spi_i; ++ } else { ++ const off_t pos = begin + len; /* file position of end of what we've generated */ ++ ++ if (pos <= offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ len = 0; ++ begin = pos; ++ } ++ } ++ } ++ } ++ ++done_spi_i: ++ spin_unlock_bh(&tdb_lock); ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ return len - (offset - begin); ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_spigrp_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ /* Limit of useful snprintf output */ ++ const int max_content = length > 0? length-1 : 0; ++ ++ int len = 0; ++ off_t begin = 0; ++ int i; ++ struct ipsec_sa *sa_p, *sa_p2; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_spigrp_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ for (i = 0; i < SADB_HASHMOD; i++) { ++ for (sa_p = ipsec_sadb_hash[i]; ++ sa_p != NULL; ++ sa_p = sa_p->ips_hnext) ++ { ++ atomic_inc(&sa_p->ips_refcount); ++ if(sa_p->ips_inext == NULL) { ++ sa_p2 = sa_p; ++ while(sa_p2 != NULL) { ++ atomic_inc(&sa_p2->ips_refcount); ++ sa_len = satot(&sa_p2->ips_said, ++ 'x', sa, sizeof(sa)); ++ ++ len += ipsec_snprintf(buffer+len, length-len, "%s ", ++ sa_len ? sa : " (error)"); ++ atomic_dec(&sa_p2->ips_refcount); ++ sa_p2 = sa_p2->ips_onext; ++ } ++ len += ipsec_snprintf(buffer+len, length-len, "\n"); ++ } ++ ++ atomic_dec(&sa_p->ips_refcount); ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loops */ ++ len = max_content; /* truncate crap */ ++ goto done_spigrp_i; ++ } else { ++ const off_t pos = begin + len; ++ ++ if (pos <= offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ len = 0; ++ begin = pos; ++ } ++ } ++ } ++ } ++ ++done_spigrp_i: ++ spin_unlock_bh(&tdb_lock); ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ return len - (offset - begin); ++} ++ ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_tncfg_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ /* limit of useful snprintf output */ ++ const int max_content = length > 0? length-1 : 0; ++ int len = 0; ++ off_t begin = 0; ++ int i; ++ char name[9]; ++ struct device *dev, *privdev; ++ struct ipsecpriv *priv; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_tncfg_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ ipsec_snprintf(name, (ssize_t) sizeof(name), IPSEC_DEV_FORMAT, i); ++ dev = __ipsec_dev_get(name); ++ if(dev) { ++ priv = (struct ipsecpriv *)(dev->priv); ++ len += ipsec_snprintf(buffer+len, length-len, "%s", ++ dev->name); ++ if(priv) { ++ privdev = (struct device *)(priv->dev); ++ len += ipsec_snprintf(buffer+len, length-len, " -> %s", ++ privdev ? privdev->name : "NULL"); ++ len += ipsec_snprintf(buffer+len, length-len, " mtu=%d(%d) -> %d", ++ dev->mtu, ++ priv->mtu, ++ privdev ? privdev->mtu : 0); ++ } else { ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_tncfg_get_info: device '%s' has no private data space!\n", ++ dev->name); ++ } ++ len += ipsec_snprintf(buffer+len, length-len, "\n"); ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loop */ ++ len = max_content; /* truncate crap */ ++ break; ++ } else { ++ const off_t pos = begin + len; ++ if (pos <= offset) { ++ len = 0; ++ begin = pos; ++ } ++ } ++ } ++ } ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ len -= (offset - begin); /* Start slop */ ++ if (len > length) ++ len = length; ++ return len; ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_version_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ int len = 0; ++ off_t begin = 0; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_version_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ len += ipsec_snprintf(buffer + len,length-len, "Openswan version: %s\n", ++ ipsec_version_code()); ++#if 0 ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_version_get_info: " ++ "ipsec_init version: %s\n", ++ ipsec_init_c_version); ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_version_get_info: " ++ "ipsec_tunnel version: %s\n", ++ ipsec_tunnel_c_version); ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_version_get_info: " ++ "ipsec_netlink version: %s\n", ++ ipsec_netlink_c_version); ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_version_get_info: " ++ "radij_c_version: %s\n", ++ radij_c_version); ++#endif ++ ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ len -= (offset - begin); /* Start slop */ ++ if (len > length) ++ len = length; ++ return len; ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_birth_info(char *page, ++ char **start, ++ off_t offset, ++ int count, ++ int *eof, ++ void *data) ++{ ++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data; ++ int len; ++ ++ if(offset >= ibr->packet_template_len) { ++ if(eof) { ++ *eof=1; ++ } ++ return 0; ++ } ++ ++ len = ibr->packet_template_len; ++ len -= offset; ++ if (len > count) ++ len = count; ++ ++ memcpy(page + offset, ibr->packet_template+offset, len); ++ ++ return len; ++} ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_birth_set(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data; ++ int len; ++ ++ MOD_INC_USE_COUNT; ++ if(count > IPSEC_BIRTH_TEMPLATE_MAXLEN) { ++ len = IPSEC_BIRTH_TEMPLATE_MAXLEN; ++ } else { ++ len = count; ++ } ++ ++ if(copy_from_user(ibr->packet_template, buffer, len)) { ++ MOD_DEC_USE_COUNT; ++ return -EFAULT; ++ } ++ ibr->packet_template_len = len; ++ ++ MOD_DEC_USE_COUNT; ++ ++ return len; ++} ++ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_klipsdebug_get_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length IPSEC_PROC_LAST_ARG) ++{ ++ int len = 0; ++ off_t begin = 0; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS, ++ "klips_debug:ipsec_klipsdebug_get_info: " ++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n", ++ buffer, ++ *start, ++ (int)offset, ++ length); ++ ++ len += ipsec_snprintf(buffer+len, length-len, "debug_tunnel=%08x.\n", debug_tunnel); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_xform=%08x.\n", debug_xform); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_eroute=%08x.\n", debug_eroute); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_spi=%08x.\n", debug_spi); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_radij=%08x.\n", debug_radij); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_esp=%08x.\n", debug_esp); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_ah=%08x.\n", debug_ah); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_rcv=%08x.\n", debug_rcv); ++ len += ipsec_snprintf(buffer+len, length-len, "debug_pfkey=%08x.\n", debug_pfkey); ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ len -= (offset - begin); /* Start slop */ ++ if (len > length) ++ len = length; ++ return len; ++} ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++IPSEC_PROCFS_DEBUG_NO_STATIC ++int ++ipsec_stats_get_int_info(char *buffer, ++ char **start, ++ off_t offset, ++ int length, ++ int *eof, ++ void *data) ++{ ++ ++ const int max_content = length > 0? length-1 : 0; ++ int len = 0; ++ int *thing; ++ ++ thing = (int *)data; ++ ++ len = ipsec_snprintf(buffer+len, length-len, "%08x\n", *thing); ++ ++ if (len >= max_content) ++ len = max_content; /* truncate crap */ ++ ++ *start = buffer + offset; /* Start of wanted data */ ++ return len > offset? len - offset : 0; ++ ++} ++ ++#ifndef PROC_FS_2325 ++struct proc_dir_entry ipsec_eroute = ++{ ++ 0, ++ 12, "ipsec_eroute", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_eroute_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++ ++struct proc_dir_entry ipsec_spi = ++{ ++ 0, ++ 9, "ipsec_spi", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_spi_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++ ++struct proc_dir_entry ipsec_spigrp = ++{ ++ 0, ++ 12, "ipsec_spigrp", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_spigrp_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++ ++struct proc_dir_entry ipsec_tncfg = ++{ ++ 0, ++ 11, "ipsec_tncfg", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_tncfg_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++ ++struct proc_dir_entry ipsec_version = ++{ ++ 0, ++ 13, "ipsec_version", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_version_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++struct proc_dir_entry ipsec_klipsdebug = ++{ ++ 0, ++ 16, "ipsec_klipsdebug", ++ S_IFREG | S_IRUGO, 1, 0, 0, 0, ++ &proc_net_inode_operations, ++ ipsec_klipsdebug_get_info, ++ NULL, NULL, NULL, NULL, NULL ++}; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* !PROC_FS_2325 */ ++#endif /* CONFIG_PROC_FS */ ++ ++#if defined(PROC_FS_2325) ++struct ipsec_proc_list { ++ char *name; ++ struct proc_dir_entry **parent; ++ struct proc_dir_entry **dir; ++ read_proc_t *readthing; ++ write_proc_t *writething; ++ void *data; ++}; ++static struct ipsec_proc_list proc_items[]={ ++#ifdef CONFIG_IPSEC_DEBUG ++ {"klipsdebug", &proc_net_ipsec_dir, NULL, ipsec_klipsdebug_get_info, NULL, NULL}, ++#endif ++ {"eroute", &proc_net_ipsec_dir, &proc_eroute_dir, NULL, NULL, NULL}, ++ {"all", &proc_eroute_dir, NULL, ipsec_eroute_get_info, NULL, NULL}, ++ {"spi", &proc_net_ipsec_dir, &proc_spi_dir, NULL, NULL, NULL}, ++ {"all", &proc_spi_dir, NULL, ipsec_spi_get_info, NULL, NULL}, ++ {"spigrp", &proc_net_ipsec_dir, &proc_spigrp_dir, NULL, NULL, NULL}, ++ {"all", &proc_spigrp_dir, NULL, ipsec_spigrp_get_info, NULL, NULL}, ++ {"birth", &proc_net_ipsec_dir, &proc_birth_dir, NULL, NULL, NULL}, ++ {"ipv4", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv4_birth_packet}, ++ {"ipv6", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv6_birth_packet}, ++ {"tncfg", &proc_net_ipsec_dir, NULL, ipsec_tncfg_get_info, NULL, NULL}, ++ {"xforms", &proc_net_ipsec_dir, NULL, ipsec_xform_get_info, NULL, NULL}, ++ {"stats", &proc_net_ipsec_dir, &proc_stats_dir, NULL, NULL, NULL}, ++ {"trap_count", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_count}, ++ {"trap_sendcount", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_sendcount}, ++ {"version", &proc_net_ipsec_dir, NULL, ipsec_version_get_info, NULL, NULL}, ++ {NULL, NULL, NULL, NULL, NULL, NULL} ++}; ++#endif ++ ++int ++ipsec_proc_init() ++{ ++ int error = 0; ++#ifdef IPSEC_PROC_SUBDIRS ++ struct proc_dir_entry *item; ++#endif ++ ++ /* ++ * just complain because pluto won't run without /proc! ++ */ ++#ifndef CONFIG_PROC_FS ++#error You must have PROC_FS built in to use KLIPS ++#endif ++ ++ /* for 2.0 kernels */ ++#if !defined(PROC_FS_2325) && !defined(PROC_FS_21) ++ error |= proc_register_dynamic(&proc_net, &ipsec_eroute); ++ error |= proc_register_dynamic(&proc_net, &ipsec_spi); ++ error |= proc_register_dynamic(&proc_net, &ipsec_spigrp); ++ error |= proc_register_dynamic(&proc_net, &ipsec_tncfg); ++ error |= proc_register_dynamic(&proc_net, &ipsec_version); ++#ifdef CONFIG_IPSEC_DEBUG ++ error |= proc_register_dynamic(&proc_net, &ipsec_klipsdebug); ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif ++ ++ /* for 2.2 kernels */ ++#if !defined(PROC_FS_2325) && defined(PROC_FS_21) ++ error |= proc_register(proc_net, &ipsec_eroute); ++ error |= proc_register(proc_net, &ipsec_spi); ++ error |= proc_register(proc_net, &ipsec_spigrp); ++ error |= proc_register(proc_net, &ipsec_tncfg); ++ error |= proc_register(proc_net, &ipsec_version); ++#ifdef CONFIG_IPSEC_DEBUG ++ error |= proc_register(proc_net, &ipsec_klipsdebug); ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif ++ ++ /* for 2.4 kernels */ ++#if defined(PROC_FS_2325) ++ /* create /proc/net/ipsec */ ++ ++ /* zero these out before we initialize /proc/net/ipsec/birth/stuff */ ++ memset(&ipsec_ipv4_birth_packet, 0, sizeof(struct ipsec_birth_reply)); ++ memset(&ipsec_ipv6_birth_packet, 0, sizeof(struct ipsec_birth_reply)); ++ ++ proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net); ++ if(proc_net_ipsec_dir == NULL) { ++ /* no point in continuing */ ++ return 1; ++ } ++ ++ { ++ struct ipsec_proc_list *it; ++ ++ it=proc_items; ++ while(it->name!=NULL) { ++ if(it->dir) { ++ /* make a dir instead */ ++ item = proc_mkdir(it->name, *it->parent); ++ *it->dir = item; ++ } else { ++ item = create_proc_entry(it->name, 0400, *it->parent); ++ } ++ if(item) { ++ item->read_proc = it->readthing; ++ item->write_proc = it->writething; ++ item->data = it->data; ++#ifdef MODULE ++ item->owner = THIS_MODULE; ++#endif ++ } else { ++ error |= 1; ++ } ++ it++; ++ } ++ } ++ ++ /* now create some symlinks to provide compatibility */ ++ proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all"); ++ proc_symlink("ipsec_spi", proc_net, "ipsec/spi/all"); ++ proc_symlink("ipsec_spigrp", proc_net, "ipsec/spigrp/all"); ++ proc_symlink("ipsec_tncfg", proc_net, "ipsec/tncfg"); ++ proc_symlink("ipsec_version",proc_net, "ipsec/version"); ++ proc_symlink("ipsec_klipsdebug",proc_net,"ipsec/klipsdebug"); ++ ++#endif /* !PROC_FS_2325 */ ++ ++ return error; ++} ++ ++void ++ipsec_proc_cleanup() ++{ ++ ++ /* for 2.0 and 2.2 kernels */ ++#if !defined(PROC_FS_2325) ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (proc_net_unregister(ipsec_klipsdebug.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_klipsdebug\n"); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ if (proc_net_unregister(ipsec_version.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_version\n"); ++ if (proc_net_unregister(ipsec_eroute.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_eroute\n"); ++ if (proc_net_unregister(ipsec_spi.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_spi\n"); ++ if (proc_net_unregister(ipsec_spigrp.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_spigrp\n"); ++ if (proc_net_unregister(ipsec_tncfg.low_ino) != 0) ++ printk("klips_debug:ipsec_cleanup: " ++ "cannot unregister /proc/net/ipsec_tncfg\n"); ++#endif ++ ++ /* for 2.4 kernels */ ++#if defined(PROC_FS_2325) ++ { ++ struct ipsec_proc_list *it; ++ ++ /* find end of list */ ++ it=proc_items; ++ while(it->name!=NULL) { ++ it++; ++ } ++ it--; ++ ++ do { ++ remove_proc_entry(it->name, *it->parent); ++ it--; ++ } while(it > proc_items); ++ } ++ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ remove_proc_entry("ipsec_klipsdebug", proc_net); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ remove_proc_entry("ipsec_eroute", proc_net); ++ remove_proc_entry("ipsec_spi", proc_net); ++ remove_proc_entry("ipsec_spigrp", proc_net); ++ remove_proc_entry("ipsec_tncfg", proc_net); ++ remove_proc_entry("ipsec_version", proc_net); ++ remove_proc_entry("ipsec", proc_net); ++#endif /* 2.4 kernel */ ++} ++ ++/* ++ * $Log: ipsec_proc.c,v $ ++ * Revision 1.30 2004/04/25 21:23:11 ken ++ * Pull in dhr's changes from FreeS/WAN 2.06 ++ * ++ * Revision 1.29 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.28 2004/03/28 20:29:58 paul ++ * ssize_t, not ssized_t ++ * ++ * Revision 1.27 2004/03/28 20:27:20 paul ++ * Included tested and confirmed fixes mcr made and dhr verified for ++ * snprint statements. Changed one other snprintf to use ipsec_snprintf ++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with ++ * dhr. (thanks dhr!) ++ * ++ * Revision 1.26 2004/02/09 22:07:06 mcr ++ * added information about nat-traversal setting to spi-output. ++ * ++ * Revision 1.25.4.1 2004/04/05 04:30:46 mcr ++ * patches for alg-branch to compile/work with 2.x openswan ++ * ++ * Revision 1.25 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.24.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.24 2003/06/20 01:42:21 mcr ++ * added counters to measure how many ACQUIREs we send to pluto, ++ * and how many are successfully sent. ++ * ++ * Revision 1.23 2003/04/03 17:38:09 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * ++ * Revision 1.22 2002/09/20 15:40:57 rgb ++ * Renamed saref macros for consistency and brevity. ++ * ++ * Revision 1.21 2002/09/20 05:01:35 rgb ++ * Print ref and reftable, refentry seperately. ++ * ++ * Revision 1.20 2002/09/19 02:35:39 mcr ++ * do not define structures needed by /proc/net/ipsec/ if we ++ * aren't going create that directory. ++ * ++ * Revision 1.19 2002/09/10 01:43:25 mcr ++ * fixed problem in /-* comment. ++ * ++ * Revision 1.18 2002/09/03 16:22:11 mcr ++ * fixed initialization of birth/stuff values - some simple ++ * screw ups in the code. ++ * removed debugging that was left in by mistake. ++ * ++ * Revision 1.17 2002/09/02 17:54:53 mcr ++ * changed how the table driven /proc entries are created so that ++ * making subdirs is now explicit rather than implicit. ++ * ++ * Revision 1.16 2002/08/30 01:23:37 mcr ++ * reorganized /proc creating code to clear up ifdefs, ++ * make the 2.4 code table driven, and put things into ++ * /proc/net/ipsec subdir. Symlinks are left for compatibility. ++ * ++ * Revision 1.15 2002/08/13 19:01:25 mcr ++ * patches from kenb to permit compilation of FreeSWAN on ia64. ++ * des library patched to use proper DES_LONG type for ia64. ++ * ++ * Revision 1.14 2002/07/26 08:48:31 rgb ++ * Added SA ref table code. ++ * ++ * Revision 1.13 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.12 2002/05/27 18:56:07 rgb ++ * Convert to dynamic ipsec device allocation. ++ * ++ * Revision 1.11 2002/05/23 07:14:50 rgb ++ * Added refcount code. ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * Convert "usecount" to "refcount" to remove ambiguity. ++ * ++ * Revision 1.10 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.9 2002/04/24 07:36:28 mcr ++ * Moved from ./klips/net/ipsec/ipsec_proc.c,v ++ * ++ * Revision 1.8 2002/01/29 17:17:55 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.7 2002/01/29 04:00:52 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.6 2002/01/29 02:13:17 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.5 2002/01/12 02:54:30 mcr ++ * beginnings of /proc/net/ipsec dir. ++ * ++ * Revision 1.4 2001/12/11 02:21:05 rgb ++ * Don't include module version here, fixing 2.2 compile bug. ++ * ++ * Revision 1.3 2001/12/05 07:19:44 rgb ++ * Fixed extraneous #include "version.c" bug causing modular KLIPS failure. ++ * ++ * Revision 1.2 2001/11/26 09:16:14 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.74 2001/11/22 05:44:11 henry ++ * new version stuff ++ * ++ * Revision 1.1.2.1 2001/09/25 02:19:40 mcr ++ * /proc manipulation code moved to new ipsec_proc.c ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_radij.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,869 @@ ++/* ++ * Interface between the IPSEC code and the radix (radij) tree code ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_radij.c,v 1.70 2004/04/25 21:10:52 ken Exp $ ++ */ ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, struct net_device_stats and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* 23_SPINLOCK */ ++# include /* *lock* */ ++# endif /* 23_SPINLOCK */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++#endif ++#include ++#include ++ ++#include "openswan/ipsec_eroute.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_tunnel.h" /* struct ipsecpriv */ ++#include "openswan/ipsec_xform.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_radij = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++struct radij_node_head *rnh = NULL; ++#ifdef SPINLOCK ++spinlock_t eroute_lock = SPIN_LOCK_UNLOCKED; ++#else /* SPINLOCK */ ++spinlock_t eroute_lock; ++#endif /* SPINLOCK */ ++ ++int ++ipsec_radijinit(void) ++{ ++ maj_keylen = sizeof (struct sockaddr_encap); ++ ++ rj_init(); ++ ++ if (rj_inithead((void **)&rnh, /*16*/offsetof(struct sockaddr_encap, sen_type) * sizeof(__u8)) == 0) /* 16 is bit offset of sen_type */ ++ return -1; ++ return 0; ++} ++ ++int ++ipsec_radijcleanup(void) ++{ ++ int error; ++ ++ spin_lock_bh(&eroute_lock); ++ ++ error = radijcleanup(); ++ ++ spin_unlock_bh(&eroute_lock); ++ ++ return error; ++} ++ ++int ++ipsec_cleareroutes(void) ++{ ++ int error; ++ ++ spin_lock_bh(&eroute_lock); ++ ++ error = radijcleartree(); ++ ++ spin_unlock_bh(&eroute_lock); ++ ++ return error; ++} ++ ++int ++ipsec_breakroute(struct sockaddr_encap *eaddr, ++ struct sockaddr_encap *emask, ++ struct sk_buff **first, ++ struct sk_buff **last) ++{ ++ struct eroute *ro; ++ struct radij_node *rn; ++ int error; ++#ifdef CONFIG_IPSEC_DEBUG ++ ++ if (debug_eroute) { ++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; ++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_breakroute: " ++ "attempting to delete eroute for %s:%d->%s:%d %d\n", ++ buf1, ntohs(eaddr->sen_sport), ++ buf2, ntohs(eaddr->sen_dport), eaddr->sen_proto); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ spin_lock_bh(&eroute_lock); ++ ++ if ((error = rj_delete(eaddr, emask, rnh, &rn)) != 0) { ++ spin_unlock_bh(&eroute_lock); ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_breakroute: " ++ "node not found, eroute delete failed.\n"); ++ return error; ++ } ++ ++ spin_unlock_bh(&eroute_lock); ++ ++ ro = (struct eroute *)rn; ++ ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_breakroute: " ++ "deleted eroute=0p%p, ident=0p%p->0p%p, first=0p%p, last=0p%p\n", ++ ro, ++ ro->er_ident_s.data, ++ ro->er_ident_d.data, ++ ro->er_first, ++ ro->er_last); ++ ++ if (ro->er_ident_s.data != NULL) { ++ kfree(ro->er_ident_s.data); ++ } ++ if (ro->er_ident_d.data != NULL) { ++ kfree(ro->er_ident_d.data); ++ } ++ if (ro->er_first != NULL) { ++#if 0 ++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_first->dev->priv))->mystats); ++ stats->tx_dropped--; ++#endif ++ *first = ro->er_first; ++ } ++ if (ro->er_last != NULL) { ++#if 0 ++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_last->dev->priv))->mystats); ++ stats->tx_dropped--; ++#endif ++ *last = ro->er_last; ++ } ++ ++ if (rn->rj_flags & (RJF_ACTIVE | RJF_ROOT)) ++ panic ("ipsec_breakroute RMT_DELEROUTE root or active node\n"); ++ memset((caddr_t)rn, 0, sizeof (struct eroute)); ++ kfree(rn); ++ ++ return 0; ++} ++ ++int ++ipsec_makeroute(struct sockaddr_encap *eaddr, ++ struct sockaddr_encap *emask, ++ ip_said said, ++ uint32_t pid, ++ struct sk_buff *skb, ++ struct ident *ident_s, ++ struct ident *ident_d) ++{ ++ struct eroute *retrt; ++ int error; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ ++ if (debug_eroute) { ++ ++ { ++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; ++ ++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ sa_len = satot(&said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "attempting to allocate %lu bytes to insert eroute for %s->%s, SA: %s, PID:%d, skb=0p%p, ident:%s->%s\n", ++ (unsigned long) sizeof(struct eroute), ++ buf1, ++ buf2, ++ sa_len ? sa : " (error)", ++ pid, ++ skb, ++ (ident_s ? (ident_s->data ? ident_s->data : "NULL") : "NULL"), ++ (ident_d ? (ident_d->data ? ident_d->data : "NULL") : "NULL")); ++ } ++ { ++ char buf1[sizeof(struct sockaddr_encap)*2 + 1], ++ buf2[sizeof(struct sockaddr_encap)*2 + 1]; ++ int i; ++ unsigned char *b1 = buf1, ++ *b2 = buf2, ++ *ea = (unsigned char *)eaddr, ++ *em = (unsigned char *)emask; ++ ++ ++ for (i=0; ier_eaddr = *eaddr; ++ retrt->er_emask = *emask; ++ retrt->er_said = said; ++ retrt->er_pid = pid; ++ retrt->er_count = 0; ++ retrt->er_lasttime = jiffies/HZ; ++ rd_key((&(retrt->er_rjt))) = &(retrt->er_eaddr); ++ ++ if (ident_s && ident_s->type != SADB_IDENTTYPE_RESERVED) { ++ int data_len = ident_s->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ ++ retrt->er_ident_s.type = ident_s->type; ++ retrt->er_ident_s.id = ident_s->id; ++ retrt->er_ident_s.len = ident_s->len; ++ if(data_len) { ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "attempting to allocate %u bytes for ident_s.\n", ++ data_len); ++ if(!(retrt->er_ident_s.data = kmalloc(data_len, GFP_KERNEL))) { ++ kfree(retrt); ++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len); ++ return ENOMEM; ++ } ++ memcpy(retrt->er_ident_s.data, ident_s->data, data_len); ++ } else { ++ retrt->er_ident_s.data = NULL; ++ } ++ } ++ ++ if (ident_d && ident_d->type != SADB_IDENTTYPE_RESERVED) { ++ int data_len = ident_d->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ ++ retrt->er_ident_d.type = ident_d->type; ++ retrt->er_ident_d.id = ident_d->id; ++ retrt->er_ident_d.len = ident_d->len; ++ if(data_len) { ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "attempting to allocate %u bytes for ident_d.\n", ++ data_len); ++ if(!(retrt->er_ident_d.data = kmalloc(data_len, GFP_KERNEL))) { ++ if (retrt->er_ident_s.data) ++ kfree(retrt->er_ident_s.data); ++ kfree(retrt); ++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len); ++ return ENOMEM; ++ } ++ memcpy(retrt->er_ident_d.data, ident_d->data, data_len); ++ } else { ++ retrt->er_ident_d.data = NULL; ++ } ++ } ++ retrt->er_first = skb; ++ retrt->er_last = NULL; ++ ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "calling rj_addroute now\n"); ++ ++ spin_lock_bh(&eroute_lock); ++ ++ error = rj_addroute(&(retrt->er_eaddr), &(retrt->er_emask), ++ rnh, retrt->er_rjt.rd_nodes); ++ ++ spin_unlock_bh(&eroute_lock); ++ ++ if(error) { ++ sa_len = satot(&said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "rj_addroute not able to insert eroute for SA:%s (error:%d)\n", ++ sa_len ? sa : " (error)", error); ++ if (retrt->er_ident_s.data) ++ kfree(retrt->er_ident_s.data); ++ if (retrt->er_ident_d.data) ++ kfree(retrt->er_ident_d.data); ++ ++ kfree(retrt); ++ ++ return error; ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_eroute) { ++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; ++/* ++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2)); ++*/ ++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_src, rd_mask((&(retrt->er_rjt)))->sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_dst, rd_mask((&(retrt->er_rjt)))->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ sa_len = satot(&retrt->er_said, 0, sa, sizeof(sa)); ++ ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "pid=%05d " ++ "count=%10d " ++ "lasttime=%6d " ++ "%-18s -> %-18s => %s\n", ++ retrt->er_pid, ++ retrt->er_count, ++ (int)(jiffies/HZ - retrt->er_lasttime), ++ buf1, ++ buf2, ++ sa_len ? sa : " (error)"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_makeroute: " ++ "succeeded.\n"); ++ return 0; ++} ++ ++struct eroute * ++ipsec_findroute(struct sockaddr_encap *eaddr) ++{ ++ struct radij_node *rn; ++#ifdef CONFIG_IPSEC_DEBUG ++ char buf1[ADDRTOA_BUF], buf2[ADDRTOA_BUF]; ++ ++ if (debug_radij & DB_RJ_FINDROUTE) { ++ addrtoa(eaddr->sen_ip_src, 0, buf1, sizeof(buf1)); ++ addrtoa(eaddr->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:ipsec_findroute: " ++ "%s:%d->%s:%d %d\n", ++ buf1, ntohs(eaddr->sen_sport), ++ buf2, ntohs(eaddr->sen_dport), ++ eaddr->sen_proto); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ rn = rj_match((caddr_t)eaddr, rnh); ++ if(rn) { ++ KLIPS_PRINT(debug_eroute && sysctl_ipsec_debug_verbose, ++ "klips_debug:ipsec_findroute: " ++ "found, points to proto=%d, spi=%x, dst=%x.\n", ++ ((struct eroute*)rn)->er_said.proto, ++ ntohl(((struct eroute*)rn)->er_said.spi), ++ ntohl(((struct eroute*)rn)->er_said.dst.u.v4.sin_addr.s_addr)); ++ } ++ return (struct eroute *)rn; ++} ++ ++#ifdef CONFIG_PROC_FS ++/** ipsec_rj_walker_procprint: print one line of eroute table output. ++ * ++ * Theoretical BUG: if w->length is less than the length ++ * of some line we should produce, that line will never ++ * be finished. In effect, the "file" will stop part way ++ * through that line. ++ */ ++int ++ipsec_rj_walker_procprint(struct radij_node *rn, void *w0) ++{ ++ struct eroute *ro = (struct eroute *)rn; ++ struct rjtentry *rd = (struct rjtentry *)rn; ++ struct wsbuf *w = (struct wsbuf *)w0; ++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; ++ char buf3[16]; ++ char sa[SATOT_BUF]; ++ size_t sa_len, buf_len; ++ struct sockaddr_encap *key, *mask; ++ ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:ipsec_rj_walker_procprint: " ++ "rn=0p%p, w0=0p%p\n", ++ rn, ++ w0); ++ if (rn->rj_b >= 0) { ++ return 0; ++ } ++ ++ key = rd_key(rd); ++ mask = rd_mask(rd); ++ ++ if (key == NULL || mask == NULL) { ++ return 0; ++ } ++ ++ buf_len = subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1)); ++ if(key->sen_sport != 0) { ++ sprintf(buf1+buf_len-1, ":%d", ntohs(key->sen_sport)); ++ } ++ ++ buf_len = subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ if(key->sen_dport != 0) { ++ sprintf(buf2+buf_len-1, ":%d", ntohs(key->sen_dport)); ++ } ++ ++ buf3[0]='\0'; ++ if(key->sen_proto != 0) { ++ sprintf(buf3, ":%d", key->sen_proto); ++ } ++ ++ sa_len = satot(&ro->er_said, 'x', sa, sizeof(sa)); ++ w->len += ipsec_snprintf(w->buffer + w->len, ++ w->length - w->len, ++ "%-10d " ++ "%-18s -> %-18s => %s%s\n", ++ ro->er_count, ++ buf1, ++ buf2, ++ sa_len ? sa : " (error)", ++ buf3); ++ ++ { ++ /* snprintf can only fill the last character with NUL ++ * so the maximum useful character is w->length-1. ++ * However, if w->length == 0, we cannot go back. ++ * (w->length surely cannot be negative.) ++ */ ++ int max_content = w->length > 0? w->length-1 : 0; ++ ++ if (w->len >= max_content) { ++ /* we've done all that can fit -- stop treewalking */ ++ w->len = max_content; /* truncate crap */ ++ return -ENOBUFS; ++ } else { ++ const off_t pos = w->begin + w->len; /* file position of end of what we've generated */ ++ ++ if (pos <= w->offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ w->len = 0; ++ w->begin = pos; ++ } ++ return 0; ++ } ++ } ++} ++#endif /* CONFIG_PROC_FS */ ++ ++int ++ipsec_rj_walker_delete(struct radij_node *rn, void *w0) ++{ ++ struct eroute *ro; ++ struct rjtentry *rd = (struct rjtentry *)rn; ++ struct radij_node *rn2; ++ int error; ++ struct sockaddr_encap *key, *mask; ++ ++ key = rd_key(rd); ++ mask = rd_mask(rd); ++ ++ if(!key || !mask) { ++ return -ENODATA; ++ } ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_radij) { ++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF]; ++ subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:ipsec_rj_walker_delete: " ++ "deleting: %s -> %s\n", ++ buf1, ++ buf2); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ if((error = rj_delete(key, mask, rnh, &rn2))) { ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:ipsec_rj_walker_delete: " ++ "rj_delete failed with error=%d.\n", error); ++ return error; ++ } ++ ++ if(rn2 != rn) { ++ printk("klips_debug:ipsec_rj_walker_delete: " ++ "tried to delete a different node?!? This should never happen!\n"); ++ } ++ ++ ro = (struct eroute *)rn; ++ ++ if (ro->er_ident_s.data) ++ kfree(ro->er_ident_s.data); ++ if (ro->er_ident_d.data) ++ kfree(ro->er_ident_d.data); ++ ++ memset((caddr_t)rn, 0, sizeof (struct eroute)); ++ kfree(rn); ++ ++ return 0; ++} ++ ++/* ++ * $Log: ipsec_radij.c,v $ ++ * Revision 1.70 2004/04/25 21:10:52 ken ++ * Pull in dhr's changes from FreeS/WAN 2.06 ++ * ++ * Revision 1.69 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.68 2004/03/28 20:27:20 paul ++ * Included tested and confirmed fixes mcr made and dhr verified for ++ * snprint statements. Changed one other snprintf to use ipsec_snprintf ++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with ++ * dhr. (thanks dhr!) ++ * ++ * Revision 1.67.4.1 2004/04/05 04:30:46 mcr ++ * patches for alg-branch to compile/work with 2.x openswan ++ * ++ * Revision 1.67 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.66.24.2 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.66.24.1 2003/09/21 13:59:56 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.66 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.65 2002/09/20 05:01:40 rgb ++ * Added memory allocation debugging. ++ * ++ * Revision 1.64 2002/05/31 01:46:05 mcr ++ * added && sysctl_ipsec_debug_verbose verbose to ipsec_findroute ++ * as requested in PR#14. ++ * ++ * Revision 1.63 2002/05/23 07:14:11 rgb ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.62 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.61 2002/04/24 07:36:29 mcr ++ * Moved from ./klips/net/ipsec/ipsec_radij.c,v ++ * ++ * Revision 1.60 2002/02/19 23:59:45 rgb ++ * Removed redundant compiler directives. ++ * ++ * Revision 1.59 2002/02/06 04:13:47 mcr ++ * missing #ifdef CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.58 2002/01/29 17:17:56 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.57 2002/01/29 04:00:52 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.56 2002/01/29 02:13:17 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.55 2001/11/26 09:23:48 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.53.2.1 2001/09/25 02:26:32 mcr ++ * headers adjusted for new usage. ++ * ++ * Revision 1.54 2001/10/18 04:45:20 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.53 2001/09/19 17:19:40 rgb ++ * Debug output bugfix for NetCelo's PF_KEY ident patch. ++ * ++ * Revision 1.52 2001/09/19 16:33:37 rgb ++ * Temporarily disable ident fields to /proc/net/ipsec_eroute. ++ * ++ * Revision 1.51 2001/09/15 16:24:04 rgb ++ * Re-inject first and last HOLD packet when an eroute REPLACE is done. ++ * ++ * Revision 1.50 2001/09/14 16:58:36 rgb ++ * Added support for storing the first and last packets through a HOLD. ++ * ++ * Revision 1.49 2001/09/08 21:13:32 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.48 2001/06/15 04:12:56 rgb ++ * Fixed kernel memory allocation error return code polarity bug. ++ * ++ * Revision 1.47 2001/06/14 19:35:09 rgb ++ * Update copyright date. ++ * ++ * Revision 1.46 2001/06/08 08:47:18 rgb ++ * Fixed for debug disabled. ++ * ++ * Revision 1.45 2001/05/27 06:12:11 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.44 2001/05/03 19:41:01 rgb ++ * Initialise error return variable. ++ * Use more appropriate return value for ipsec_rj_walker_delete(). ++ * ++ * Revision 1.43 2001/02/27 22:24:54 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.42 2001/02/27 06:21:57 rgb ++ * Added findroute success instrumentation. ++ * ++ * Revision 1.41 2000/11/06 04:32:08 rgb ++ * Ditched spin_lock_irqsave in favour of spin_lock_bh. ++ * ++ * Revision 1.40 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.39 2000/08/30 05:25:20 rgb ++ * Correct debug text in ipsec_breakroute() from incorrect ++ * "ipsec_callback". ++ * ++ * Revision 1.38 2000/07/28 14:58:31 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.37 2000/03/16 14:02:50 rgb ++ * Fixed debug scope to enable compilation with debug off. ++ * ++ * Revision 1.36 2000/01/21 06:14:46 rgb ++ * Added debugging text to ipsec_rj_walker_delete(). ++ * Set return code to negative for consistency. ++ * ++ * Revision 1.35 1999/11/23 23:05:24 rgb ++ * Use provided macro ADDRTOA_BUF instead of hardcoded value. ++ * ++ * Revision 1.34 1999/11/18 04:13:56 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * Added CONFIG_PROC_FS compiler directives in case it is shut off. ++ * ++ * Revision 1.33 1999/11/17 15:53:39 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.32 1999/10/26 13:58:33 rgb ++ * Put spinlock flags variable declaration outside the debug compiler ++ * directive to enable compilation with debug shut off. ++ * ++ * Revision 1.31 1999/10/15 22:13:29 rgb ++ * Clean out cruft. ++ * Align /proc/net/ipsec_eroute output for easier readability. ++ * Fix double linefeed in radij debug output. ++ * Fix double locking bug that locks up 2.0.36 but not 2.0.38. ++ * ++ * Revision 1.30 1999/10/08 18:37:33 rgb ++ * Fix end-of-line spacing to sate whining PHMs. ++ * ++ * Revision 1.29 1999/10/03 18:52:45 rgb ++ * Spinlock support for 2.0.xx. ++ * Dumb return code spin_unlock fix. ++ * ++ * Revision 1.28 1999/10/01 16:22:24 rgb ++ * Switch from assignment init. to functional init. of spinlocks. ++ * ++ * Revision 1.27 1999/10/01 15:44:53 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.26 1999/10/01 00:01:23 rgb ++ * Added eroute structure locking. ++ * ++ * Revision 1.25 1999/06/10 16:07:30 rgb ++ * Silence delete eroute on no debug. ++ * ++ * Revision 1.24 1999/05/09 03:25:36 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.23 1999/05/05 22:02:31 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.22 1999/04/29 15:17:23 rgb ++ * Add return values to init and cleanup functions. ++ * Add sanity checking for null pointer arguments. ++ * ++ * Revision 1.21 1999/04/11 00:28:58 henry ++ * GPL boilerplate ++ * ++ * Revision 1.20 1999/04/06 04:54:26 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.19 1999/02/17 16:50:35 rgb ++ * Clean out unused cruft. ++ * Consolidate for space and speed efficiency. ++ * Convert DEBUG_IPSEC to KLIPS_PRINT ++ * ++ * Revision 1.18 1999/01/22 06:22:06 rgb ++ * Cruft clean-out. ++ * 64-bit clean-up. ++ * ++ * Revision 1.17 1998/12/02 03:09:39 rgb ++ * Clean up debug printing conditionals to compile with debugging off. ++ * ++ * Revision 1.16 1998/12/01 13:49:39 rgb ++ * Wrap version info printing in debug switches. ++ * ++ * Revision 1.15 1998/11/30 13:22:54 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.14 1998/10/31 06:48:17 rgb ++ * Fixed up comments in #endif directives. ++ * ++ * Revision 1.13 1998/10/27 13:48:09 rgb ++ * Cleaned up /proc/net/ipsec_* filesystem for easy parsing by scripts. ++ * Fixed less(1) truncated output bug. ++ * Code clean-up. ++ * ++ * Revision 1.12 1998/10/25 02:41:36 rgb ++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an ++ * argument to be able to transmit more infomation about errors. ++ * Fix cut-and-paste debug statement identifier. ++ * ++ * Revision 1.11 1998/10/22 06:45:39 rgb ++ * Cleaned up cruft. ++ * Convert to use satoa for printk. ++ * ++ * Revision 1.10 1998/10/19 14:44:28 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.9 1998/10/09 04:30:52 rgb ++ * Added 'klips_debug' prefix to all klips printk debug statements. ++ * Deleted old commented out cruft. ++ * ++ * Revision 1.8 1998/08/06 17:24:23 rgb ++ * Fix addrtoa return code bug from stale manpage advice preventing packets ++ * from being erouted. ++ * ++ * Revision 1.7 1998/08/06 07:44:59 rgb ++ * Fixed /proc/net/ipsec_eroute subnettoa and addrtoa return value bug that ++ * ended up in nothing being printed. ++ * ++ * Revision 1.6 1998/08/05 22:16:41 rgb ++ * Cleanup to prevent cosmetic errors (ie. debug output) from being fatal. ++ * ++ * Revision 1.5 1998/07/29 20:38:44 rgb ++ * Debug and fix subnettoa and addrtoa output. ++ * ++ * Revision 1.4 1998/07/28 00:02:39 rgb ++ * Converting to exclusive use of addrtoa. ++ * Fix eroute delete. ++ * ++ * Revision 1.3 1998/07/14 18:21:26 rgb ++ * Add function to clear the eroute table. ++ * ++ * Revision 1.2 1998/06/23 02:59:14 rgb ++ * Added debugging output to eroute add/delete routines. ++ * ++ * Revision 1.9 1998/06/18 21:29:06 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid kernel ++ * build scripts happier in presence of symbolic links ++ * ++ * Revision 1.8 1998/06/05 02:32:26 rgb ++ * Fix spi ntoh kernel debug output. ++ * ++ * Revision 1.7 1998/05/25 20:30:37 rgb ++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions. ++ * ++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and ++ * add ipsec_rj_walker_delete. ++ * ++ * Revision 1.6 1998/05/21 13:08:57 rgb ++ * Rewrote procinfo subroutines to avoid *bad things* when more that 3k of ++ * information is available for printout. ++ * ++ * Revision 1.5 1998/05/18 21:35:55 rgb ++ * Clean up output for numerical consistency and readability. Zero freed ++ * eroute memory. ++ * ++ * Revision 1.4 1998/04/21 21:28:58 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.3 1998/04/14 17:30:39 rgb ++ * Fix up compiling errors for radij tree memory reclamation. ++ * ++ * Revision 1.2 1998/04/12 22:03:23 rgb ++ * Updated ESP-3DES-HMAC-MD5-96, ++ * ESP-DES-HMAC-MD5-96, ++ * AH-HMAC-MD5-96, ++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository ++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts. ++ * ++ * Fixed eroute references in /proc/net/ipsec*. ++ * ++ * Started to patch module unloading memory leaks in ipsec_netlink and ++ * radij tree unloading. ++ * ++ * Revision 1.1 1998/04/09 03:06:10 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_rcv.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1922 @@ ++/* ++ * receive code ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998-2003 Richard Guy Briggs. ++ * Copyright (C) 2004 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_rcv_c_version[] = "RCSID $Id: ipsec_rcv.c,v 1.143.4.2 2004/08/22 03:29:06 mcr Exp $"; ++ ++#include ++#include ++ ++#define __NO_VERSION__ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define proto_priv cb ++#endif /* NET21 */ ++#include ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++ ++#include "openswan/ipsec_auth.h" ++ ++#include "openswan/ipsec_esp.h" ++ ++#ifdef CONFIG_IPSEC_AH ++#include "openswan/ipsec_ah.h" ++#endif /* CONFIG_IPSEC_AH */ ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++#include "openswan/ipsec_ipcomp.h" ++#endif /* CONFIG_IPSEC_COMP */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_rcv = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++int sysctl_ipsec_inbound_policy_check = 1; ++ ++/* This is a private use protocol, and AT&T should be ashamed. They should have ++ * used protocol # 59, which is "no next header" instead of 0xFE. ++ */ ++#ifndef IPPROTO_ATT_HEARTBEAT ++#define IPPROTO_ATT_HEARTBEAT 0xFE ++#endif ++ ++#ifdef CONFIG_IPSEC_DEBUG ++void ++ipsec_dmp(char *s, caddr_t bb, int len) ++{ ++ int i; ++ unsigned char *b = bb; ++ ++ ++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: " ++ "at %s, len=%d:", ++ s, ++ len); ++ for (i=0; i < len; i++) { ++ if(!(i%16)){ ++ printk("\nklips_debug: "); ++ } ++ printk(" %02x", *b++); ++ } ++ printk("\n"); ++} ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++/* ++ * Check-replay-window routine, adapted from the original ++ * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt ++ * ++ * This is a routine that implements a 64 packet window. This is intend- ++ * ed on being an implementation sample. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_checkreplaywindow(struct ipsec_sa*ipsp, __u32 seq) ++{ ++ __u32 diff; ++ ++ if (ipsp->ips_replaywin == 0) /* replay shut off */ ++ return 1; ++ if (seq == 0) ++ return 0; /* first == 0 or wrapped */ ++ ++ /* new larger sequence number */ ++ if (seq > ipsp->ips_replaywin_lastseq) { ++ return 1; /* larger is good */ ++ } ++ diff = ipsp->ips_replaywin_lastseq - seq; ++ ++ /* too old or wrapped */ /* if wrapped, kill off SA? */ ++ if (diff >= ipsp->ips_replaywin) { ++ return 0; ++ } ++ /* this packet already seen */ ++ if (ipsp->ips_replaywin_bitmap & (1 << diff)) ++ return 0; ++ return 1; /* out of order but good */ ++} ++ ++DEBUG_NO_STATIC int ++ipsec_updatereplaywindow(struct ipsec_sa*ipsp, __u32 seq) ++{ ++ __u32 diff; ++ ++ if (ipsp->ips_replaywin == 0) /* replay shut off */ ++ return 1; ++ if (seq == 0) ++ return 0; /* first == 0 or wrapped */ ++ ++ /* new larger sequence number */ ++ if (seq > ipsp->ips_replaywin_lastseq) { ++ diff = seq - ipsp->ips_replaywin_lastseq; ++ ++ /* In win, set bit for this pkt */ ++ if (diff < ipsp->ips_replaywin) ++ ipsp->ips_replaywin_bitmap = ++ (ipsp->ips_replaywin_bitmap << diff) | 1; ++ else ++ /* This packet has way larger seq num */ ++ ipsp->ips_replaywin_bitmap = 1; ++ ++ if(seq - ipsp->ips_replaywin_lastseq - 1 > ipsp->ips_replaywin_maxdiff) { ++ ipsp->ips_replaywin_maxdiff = seq - ipsp->ips_replaywin_lastseq - 1; ++ } ++ ipsp->ips_replaywin_lastseq = seq; ++ return 1; /* larger is good */ ++ } ++ diff = ipsp->ips_replaywin_lastseq - seq; ++ ++ /* too old or wrapped */ /* if wrapped, kill off SA? */ ++ if (diff >= ipsp->ips_replaywin) { ++/* ++ if(seq < 0.25*max && ipsp->ips_replaywin_lastseq > 0.75*max) { ++ ipsec_sa_delchain(ipsp); ++ } ++*/ ++ return 0; ++ } ++ /* this packet already seen */ ++ if (ipsp->ips_replaywin_bitmap & (1 << diff)) ++ return 0; ++ ipsp->ips_replaywin_bitmap |= (1 << diff); /* mark as seen */ ++ return 1; /* out of order but good */ ++} ++ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++struct auth_alg ipsec_rcv_md5[]={ ++ {MD5Init, MD5Update, MD5Final, AHMD596_ALEN} ++}; ++ ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++struct auth_alg ipsec_rcv_sha1[]={ ++ {SHA1Init, SHA1Update, SHA1Final, AHSHA196_ALEN} ++}; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++ ++enum ipsec_rcv_value ++ipsec_rcv_decap_once(struct ipsec_rcv_state *irs, struct xform_functions *proto_funcs) ++{ ++ int iphlen; ++ unsigned char *dat; ++ __u8 proto; ++ struct in_addr ipsaddr; ++ struct in_addr ipdaddr; ++ int replay = 0; /* replay value in AH or ESP packet */ ++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */ ++ struct ipsec_sa *newipsp; ++ struct iphdr *ipp; ++ struct sk_buff *skb; ++#ifdef CONFIG_IPSEC_ALG ++ struct ipsec_alg_auth *ixt_a=NULL; ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ skb = irs->skb; ++ irs->len = skb->len; ++ dat = skb->data; ++ ipp = irs->ipp; ++ proto = ipp->protocol; ++ ipsaddr.s_addr = ipp->saddr; ++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); ++ ipdaddr.s_addr = ipp->daddr; ++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); ++ ++ iphlen = ipp->ihl << 2; ++ irs->iphlen=iphlen; ++ ipp->check = 0; /* we know the sum is good */ ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv_decap_once: " ++ "decap (%d) from %s -> %s\n", ++ proto, irs->ipsaddr_txt, irs->ipdaddr_txt); ++ ++ /* ++ * Find tunnel control block and (indirectly) call the ++ * appropriate tranform routine. The resulting sk_buf ++ * is a valid IP packet ready to go through input processing. ++ */ ++ ++ irs->said.dst.u.v4.sin_addr.s_addr = ipp->daddr; ++ irs->said.dst.u.v4.sin_family = AF_INET; ++ ++ if(proto_funcs->rcv_checks) { ++ enum ipsec_rcv_value retval = ++ (*proto_funcs->rcv_checks)(irs, skb); ++ ++ if(retval < 0) { ++ return retval; ++ } ++ } ++ ++ irs->said.proto = proto; ++ irs->sa_len = satot(&irs->said, 0, irs->sa, sizeof(irs->sa)); ++ if(irs->sa_len == 0) { ++ strcpy(irs->sa, "(error)"); ++ } ++ ++ newipsp = ipsec_sa_getbyid(&irs->said); ++ if (newipsp == NULL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "no ipsec_sa for SA:%s: incoming packet with no SA dropped\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_SAIDNOTFOUND; ++ } ++ ++ /* MCR - XXX this is bizarre. ipsec_sa_getbyid returned it, having incremented the refcount, ++ * why in the world would we decrement it here? ++ ++ ipsec_sa_put(irs->ipsp);*/ /* incomplete */ ++ ++ /* If it is in larval state, drop the packet, we cannot process yet. */ ++ if(newipsp->ips_state == SADB_SASTATE_LARVAL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "ipsec_sa in larval state, cannot be used yet, dropping packet.\n"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_SAIDNOTLIVE; ++ } ++ ++ if(newipsp->ips_state == SADB_SASTATE_DEAD) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "ipsec_sa in dead state, cannot be used any more, dropping packet.\n"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_SAIDNOTLIVE; ++ } ++ ++ if(sysctl_ipsec_inbound_policy_check) { ++ if(irs->ipp->saddr != ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n", ++ irs->sa_len ? irs->sa : " (error)", ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_FAILEDINBOUND; ++ } ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, src=%s of pkt agrees with expected SA source address policy.\n", ++ irs->sa_len ? irs->sa : " (error)", ++ irs->ipsaddr_txt); ++ ++ /* ++ * at this point, we have looked up a new SA, and we want to make sure that if this ++ * isn't the first SA in the list, that the previous SA actually points at this one. ++ */ ++ if(irs->ipsp) { ++ if(irs->ipsp->ips_inext != newipsp) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "unexpected SA:%s: does not agree with ips->inext policy, dropped\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_FAILEDINBOUND; ++ } ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s grouping from previous SA is OK.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ } else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s First SA in group.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ } ++ ++ /* ++ * previously, at this point, we checked if the back pointer from the new SA that ++ * we just found matched the back pointer. But, we won't do this check anymore, ++ * because we want to be able to nest SAs ++ */ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "natt_type=%u tdbp->ips_natt_type=%u : %s\n", ++ irs->natt_type, newipsp->ips_natt_type, ++ (irs->natt_type==newipsp->ips_natt_type)?"ok":"bad"); ++ if (irs->natt_type != newipsp->ips_natt_type) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s does not agree with expected NAT-T policy.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_FAILEDINBOUND; ++ } ++#endif ++ } ++ ++ /* okay, SA checks out, so free any previous SA, and record a new one */ ++ ++ if(irs->ipsp) { ++ ipsec_sa_put(irs->ipsp); ++ } ++ irs->ipsp=newipsp; ++ ++ /* note that the outer code will free the irs->ipsp if there is an error */ ++ ++ ++ /* now check the lifetimes */ ++ if(ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes", ++ irs->sa, ipsec_life_countbased, ipsec_incoming, ++ irs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "addtime", ++ irs->sa, ipsec_life_timebased, ipsec_incoming, ++ irs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "usetime", ++ irs->sa, ipsec_life_timebased, ipsec_incoming, ++ irs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets, "packets", ++ irs->sa, ipsec_life_countbased, ipsec_incoming, ++ irs->ipsp) == ipsec_life_harddied) { ++ ipsec_sa_delchain(irs->ipsp); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv_decap_once: " ++ "decap (%d) failed lifetime check\n", ++ proto); ++ ++ return IPSEC_RCV_LIFETIMEFAILED; ++ } ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if ((irs->natt_type) && ++ ( (irs->ipp->saddr != (((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr)) || ++ (irs->natt_sport != newipsp->ips_natt_sport) ++ )) { ++ struct sockaddr sipaddr; ++ /** Advertise NAT-T addr change to pluto **/ ++ sipaddr.sa_family = AF_INET; ++ ((struct sockaddr_in*)&sipaddr)->sin_addr.s_addr = irs->ipp->saddr; ++ ((struct sockaddr_in*)&sipaddr)->sin_port = htons(irs->natt_sport); ++ pfkey_nat_t_new_mapping(newipsp, &sipaddr, irs->natt_sport); ++ /** ++ * Then allow or block packet depending on ++ * sysctl_ipsec_inbound_policy_check. ++ * ++ * In all cases, pluto will update SA if new mapping is ++ * accepted. ++ */ ++ if (sysctl_ipsec_inbound_policy_check) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, src=%s:%u of pkt does not agree with expected " ++ "SA source address policy (pluto has been informed).\n", ++ irs->sa_len ? irs->sa : " (error)", ++ irs->ipsaddr_txt, irs->natt_sport); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ ipsec_sa_put(newipsp); ++ return IPSEC_RCV_FAILEDINBOUND; ++ } ++ } ++#endif ++ ++ irs->authfuncs=NULL; ++ /* authenticate, if required */ ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_a=irs->ipsp->ips_alg_auth)) { ++ irs->authlen = AHHMAC_HASHLEN; ++ irs->authfuncs = NULL; ++ irs->ictx = NULL; ++ irs->octx = NULL; ++ irs->ictx_len = 0; ++ irs->octx_len = 0; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "authalg=%d authlen=%d\n", ++ irs->ipsp->ips_authalg, ++ irs->authlen); ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(irs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ irs->authlen = AHHMAC_HASHLEN; ++ irs->authfuncs = ipsec_rcv_md5; ++ irs->ictx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx; ++ irs->octx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx; ++ irs->ictx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx); ++ irs->octx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ irs->authlen = AHHMAC_HASHLEN; ++ irs->authfuncs = ipsec_rcv_sha1; ++ irs->ictx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx; ++ irs->octx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx; ++ irs->ictx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx); ++ irs->octx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ irs->authlen = 0; ++ irs->authfuncs = NULL; ++ irs->ictx = NULL; ++ irs->octx = NULL; ++ irs->ictx_len = 0; ++ irs->octx_len = 0; ++ break; ++ default: ++ irs->ipsp->ips_errs.ips_alg_errs += 1; ++ if(irs->stats) { ++ irs->stats->rx_errors++; ++ } ++ return IPSEC_RCV_BADAUTH; ++ } ++ ++ irs->ilen = irs->len - iphlen - irs->authlen; ++ if(irs->ilen <= 0) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "runt %s packet with no data, dropping.\n", ++ (proto == IPPROTO_ESP ? "esp" : "ah")); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_BADLEN; ++ } ++ ++#ifdef CONFIG_IPSEC_ALG ++ if(irs->authfuncs || ixt_a) { ++#else ++ if(irs->authfuncs) { ++#endif ++ unsigned char *authenticator = NULL; ++ ++ if(proto_funcs->rcv_setup_auth) { ++ enum ipsec_rcv_value retval ++ = (*proto_funcs->rcv_setup_auth)(irs, skb, ++ &replay, ++ &authenticator); ++ if(retval < 0) { ++ return retval; ++ } ++ } ++ ++ if(!authenticator) { ++ irs->ipsp->ips_errs.ips_auth_errs += 1; ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_BADAUTH; ++ } ++ ++ if(!ipsec_checkreplaywindow(irs->ipsp, replay)) { ++ irs->ipsp->ips_errs.ips_replaywin_errs += 1; ++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY, ++ "klips_debug:ipsec_rcv: " ++ "duplicate frame from %s, packet dropped\n", ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_REPLAYFAILED; ++ } ++ ++ /* ++ * verify authenticator ++ */ ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "encalg = %d, authalg = %d.\n", ++ irs->ipsp->ips_encalg, ++ irs->ipsp->ips_authalg); ++ ++ /* calculate authenticator */ ++ if(proto_funcs->rcv_calc_auth == NULL) { ++ return IPSEC_RCV_BADAUTH; ++ } ++ (*proto_funcs->rcv_calc_auth)(irs, skb); ++ ++ if (memcmp(irs->hash, authenticator, irs->authlen)) { ++ irs->ipsp->ips_errs.ips_auth_errs += 1; ++ KLIPS_PRINT(debug_rcv & DB_RX_INAU, ++ "klips_debug:ipsec_rcv: " ++ "auth failed on incoming packet from %s: hash=%08x%08x%08x auth=%08x%08x%08x, dropped\n", ++ irs->ipsaddr_txt, ++ ntohl(*(__u32*)&irs->hash[0]), ++ ntohl(*(__u32*)&irs->hash[4]), ++ ntohl(*(__u32*)&irs->hash[8]), ++ ntohl(*(__u32*)authenticator), ++ ntohl(*((__u32*)authenticator + 1)), ++ ntohl(*((__u32*)authenticator + 2))); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_AUTHFAILED; ++ } else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "authentication successful.\n"); ++ } ++ ++ /* Crypto hygiene: clear memory used to calculate autheticator. ++ * The length varies with the algorithm. ++ */ ++ memset(irs->hash, 0, irs->authlen); ++ ++ /* If the sequence number == 0, expire SA, it had rolled */ ++ if(irs->ipsp->ips_replaywin && !replay /* !irs->ipsp->ips_replaywin_lastseq */) { ++ ipsec_sa_delchain(irs->ipsp); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "replay window counter rolled, expiring SA.\n"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_REPLAYROLLED; ++ } ++ ++ /* now update the replay counter */ ++ if (!ipsec_updatereplaywindow(irs->ipsp, replay)) { ++ irs->ipsp->ips_errs.ips_replaywin_errs += 1; ++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY, ++ "klips_debug:ipsec_rcv: " ++ "duplicate frame from %s, packet dropped\n", ++ irs->ipsaddr_txt); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_REPLAYROLLED; ++ } ++ } ++ ++ if(proto_funcs->rcv_decrypt) { ++ enum ipsec_rcv_value retval = ++ (*proto_funcs->rcv_decrypt)(irs); ++ ++ if(retval != IPSEC_RCV_OK) { ++ return retval; ++ } ++ } ++ ++ /* ++ * Adjust pointers ++ */ ++ skb = irs->skb; ++ irs->len = skb->len; ++ dat = skb->data; ++ ++#ifdef NET_21 ++/* skb->h.ipiph=(struct iphdr *)skb->data; */ ++ skb->nh.raw = skb->data; ++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2); ++ ++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); ++#else /* NET_21 */ ++ skb->h.iph=(struct iphdr *)skb->data; ++ skb->ip_hdr=(struct iphdr *)skb->data; ++ memset(skb->proto_priv, 0, sizeof(struct options)); ++#endif /* NET_21 */ ++ ++ ipp = (struct iphdr *)dat; ++ ipsaddr.s_addr = ipp->saddr; ++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt)); ++ ipdaddr.s_addr = ipp->daddr; ++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt)); ++ /* ++ * Discard the original ESP/AH header ++ */ ++ ipp->protocol = irs->next_header; ++ ++ ipp->check = 0; /* NOTE: this will be included in checksum */ ++ ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2); ++ ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "after <%s%s%s>, SA:%s:\n", ++ IPS_XFORM_NAME(irs->ipsp), ++ irs->sa_len ? irs->sa : " (error)"); ++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp); ++ ++ skb->protocol = htons(ETH_P_IP); ++ skb->ip_summed = 0; ++ ++ ipsnext = irs->ipsp->ips_inext; ++ if(sysctl_ipsec_inbound_policy_check) { ++ if(ipsnext) { ++ if( ++ ipp->protocol != IPPROTO_AH ++ && ipp->protocol != IPPROTO_ESP ++#ifdef CONFIG_IPSEC_IPCOMP ++ && ipp->protocol != IPPROTO_COMP ++ && (ipsnext->ips_said.proto != IPPROTO_COMP ++ || ipsnext->ips_inext) ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ && ipp->protocol != IPPROTO_IPIP ++ && ipp->protocol != 0xFE /* added to support heartbeats to AT&T SIG/GIG */ ++ ) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "packet with incomplete policy dropped, last successful SA:%s.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ if(irs->stats) { ++ irs->stats->rx_dropped++; ++ } ++ return IPSEC_RCV_FAILEDINBOUND; ++ } ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, Another IPSEC header to process.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ } else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "No ips_inext from this SA:%s.\n", ++ irs->sa_len ? irs->sa : " (error)"); ++ } ++ } ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++ /* update ipcomp ratio counters, even if no ipcomp packet is present */ ++ if (ipsnext ++ && ipsnext->ips_said.proto == IPPROTO_COMP ++ && ipp->protocol != IPPROTO_COMP) { ++ ipsnext->ips_comp_ratio_cbytes += ntohs(ipp->tot_len); ++ ipsnext->ips_comp_ratio_dbytes += ntohs(ipp->tot_len); ++ } ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++ irs->ipsp->ips_life.ipl_bytes.ipl_count += irs->len; ++ irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len; ++ ++ if(!irs->ipsp->ips_life.ipl_usetime.ipl_count) { ++ irs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; ++ } ++ irs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; ++ irs->ipsp->ips_life.ipl_packets.ipl_count += 1; ++ ++#ifdef CONFIG_NETFILTER ++ if(proto == IPPROTO_ESP || proto == IPPROTO_AH) { ++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK)))) ++ | IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp)); ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "%s SA sets skb->nfmark=0x%x.\n", ++ proto == IPPROTO_ESP ? "ESP" : "AH", ++ (unsigned)skb->nfmark); ++ } ++#endif /* CONFIG_NETFILTER */ ++ ++ return IPSEC_RCV_OK; ++} ++ ++ ++int ++#ifdef PROTO_HANDLER_SINGLE_PARM ++ipsec_rcv(struct sk_buff *skb) ++#else /* PROTO_HANDLER_SINGLE_PARM */ ++#ifdef NET_21 ++ipsec_rcv(struct sk_buff *skb, unsigned short xlen) ++#else /* NET_21 */ ++ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, ++ __u32 daddr_unused, unsigned short xlen, __u32 saddr, ++ int redo, struct inet_protocol *protocol) ++#endif /* NET_21 */ ++#endif /* PROTO_HANDLER_SINGLE_PARM */ ++{ ++#ifdef NET_21 ++#ifdef CONFIG_IPSEC_DEBUG ++ struct device *dev = skb->dev; ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* NET_21 */ ++ unsigned char protoc; ++ struct iphdr *ipp; ++ struct ipsec_sa *ipsp = NULL; ++ struct net_device_stats *stats = NULL; /* This device's statistics */ ++ struct device *ipsecdev = NULL, *prvdev; ++ struct ipsecpriv *prv; ++ char name[9]; ++ int i; ++ struct in_addr ipsaddr; ++ struct in_addr ipdaddr; ++ ++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */ ++ struct ipsec_rcv_state irs; ++ ++ /* Don't unlink in the middle of a turnaround */ ++ MOD_INC_USE_COUNT; ++ ++ memset(&irs, 0, sizeof(struct ipsec_rcv_state)); ++ ++ if (skb == NULL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NULL skb passed in.\n"); ++ goto rcvleave; ++ } ++ ++ if (skb->data == NULL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NULL skb->data passed in, packet is bogus, dropping.\n"); ++ goto rcvleave; ++ } ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) { ++ /** ++ * Packet comes from udp_queue_rcv_skb so it is already defrag, ++ * checksum verified, ... (ie safe to use) ++ * ++ * If the packet is not for us, return -1 and udp_queue_rcv_skb ++ * will continue to handle it (do not kfree skb !!). ++ */ ++#ifndef UDP_OPT_IN_SOCK ++ struct udp_opt { ++ __u32 esp_in_udp; ++ }; ++ struct udp_opt *tp = (struct udp_opt *)&(skb->sk->tp_pinfo.af_tcp); ++#else ++ struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp); ++#endif ++ struct iphdr *ip = (struct iphdr *)skb->nh.iph; ++ struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl); ++ __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr); ++ __u32 *udpdata32 = (__u32 *)udpdata; ++ ++ irs.natt_sport = ntohs(udp->source); ++ irs.natt_dport = ntohs(udp->dest); ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "suspected ESPinUDP packet (NAT-Traversal) [%d].\n", ++ tp->esp_in_udp); ++ KLIPS_IP_PRINT(debug_rcv, ip); ++ ++ if (udpdata < skb->tail) { ++ unsigned int len = skb->tail - udpdata; ++ if ((len==1) && (udpdata[0]==0xff)) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ /* not IPv6 compliant message */ ++ "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr)); ++ goto rcvleave; ++ } ++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) && ++ (len > (2*sizeof(__u32) + sizeof(struct esphdr))) && ++ (udpdata32[0]==0) && (udpdata32[1]==0) ) { ++ /* ESP Packet with Non-IKE header */ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "ESPinUDP pkt with Non-IKE - spi=0x%x\n", ++ ntohl(udpdata32[2])); ++ irs.natt_type = ESPINUDP_WITH_NON_IKE; ++ irs.natt_len = sizeof(struct udphdr)+(2*sizeof(__u32)); ++ } ++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) && ++ (len > sizeof(struct esphdr)) && ++ (udpdata32[0]!=0) ) { ++ /* ESP Packet without Non-ESP header */ ++ irs.natt_type = ESPINUDP_WITH_NON_ESP; ++ irs.natt_len = sizeof(struct udphdr); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "ESPinUDP pkt without Non-ESP - spi=0x%x\n", ++ ntohl(udpdata32[0])); ++ } ++ else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "IKE packet - not handled here\n"); ++ MOD_DEC_USE_COUNT; ++ return -1; ++ } ++ } ++ else { ++ MOD_DEC_USE_COUNT; ++ return -1; ++ } ++ } ++#endif ++ ++#ifdef IPH_is_SKB_PULLED ++ /* In Linux 2.4.4, the IP header has been skb_pull()ed before the ++ packet is passed to us. So we'll skb_push() to get back to it. */ ++ if (skb->data == skb->h.raw) { ++ skb_push(skb, skb->h.raw - skb->nh.raw); ++ } ++#endif /* IPH_is_SKB_PULLED */ ++ ++ /* dev->hard_header_len is unreliable and should not be used */ ++ irs.hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0; ++ if((irs.hard_header_len < 0) || (irs.hard_header_len > skb_headroom(skb))) ++ irs.hard_header_len = 0; ++ ++#ifdef NET_21 ++ /* if skb was cloned (most likely due to a packet sniffer such as ++ tcpdump being momentarily attached to the interface), make ++ a copy of our own to modify */ ++ if(skb_cloned(skb)) { ++ /* include any mac header while copying.. */ ++ if(skb_headroom(skb) < irs.hard_header_len) { ++ printk(KERN_WARNING "klips_error:ipsec_rcv: " ++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n", ++ irs.hard_header_len, ++ skb_headroom(skb)); ++ goto rcvleave; ++ } ++ skb_push(skb, irs.hard_header_len); ++ if ++#ifdef SKB_COW_NEW ++ (skb_cow(skb, skb_headroom(skb)) != 0) ++#else /* SKB_COW_NEW */ ++ ((skb = skb_cow(skb, skb_headroom(skb))) == NULL) ++#endif /* SKB_COW_NEW */ ++ { ++ goto rcvleave; ++ } ++ if(skb->len < irs.hard_header_len) { ++ printk(KERN_WARNING "klips_error:ipsec_rcv: " ++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", ++ irs.hard_header_len, ++ skb->len); ++ goto rcvleave; ++ } ++ skb_pull(skb, irs.hard_header_len); ++ } ++ ++#endif /* NET_21 */ ++ ++#if IP_FRAGMENT_LINEARIZE ++ /* In Linux 2.4.4, we may have to reassemble fragments. They are ++ not assembled automatically to save TCP from having to copy ++ twice. ++ */ ++ if (skb_is_nonlinear(skb)) { ++ if (skb_linearize(skb, GFP_ATOMIC) != 0) { ++ goto rcvleave; ++ } ++ } ++#endif /* IP_FRAGMENT_LINEARIZE */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (irs.natt_len) { ++ /** ++ * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from ++ * packet and modify protocol to ESP. ++ */ ++ if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) && ++ ((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) { ++ unsigned int _len = (unsigned char *)skb->data - ++ (unsigned char *)skb->nh.iph; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n", ++ _len); ++ skb_push(skb, _len); ++ } ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "removing %d bytes from ESPinUDP packet\n", irs.natt_len); ++ ipp = (struct iphdr *)skb->data; ++ irs.iphlen = ipp->ihl << 2; ++ ipp->tot_len = htons(ntohs(ipp->tot_len) - irs.natt_len); ++ if (skb->len < irs.iphlen + irs.natt_len) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_rcv: " ++ "ESPinUDP packet is too small (%d < %d+%d). " ++ "This should never happen, please report.\n", ++ (int)(skb->len), irs.iphlen, irs.natt_len); ++ goto rcvleave; ++ } ++ memmove(skb->data + irs.natt_len, skb->data, irs.iphlen); ++ skb_pull(skb, irs.natt_len); ++ ++ /* update nh.iph */ ++ ipp = skb->nh.iph = (struct iphdr *)skb->data; ++ ++ /* modify protocol */ ++ ipp->protocol = IPPROTO_ESP; ++ ++ skb->sk = NULL; ++ ++ KLIPS_IP_PRINT(debug_rcv, skb->nh.iph); ++ } ++#endif ++ ++ ipp = skb->nh.iph; ++ ipsaddr.s_addr = ipp->saddr; ++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt)); ++ ipdaddr.s_addr = ipp->daddr; ++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt)); ++ irs.iphlen = ipp->ihl << 2; ++ ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "<<< Info -- "); ++ KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ", ++ skb->dev->name ? skb->dev->name : "NULL"); ++ KLIPS_PRINTMORE(debug_rcv && dev, "dev=%s ", ++ dev->name ? dev->name : "NULL"); ++ KLIPS_PRINTMORE(debug_rcv, "\n"); ++ ++ KLIPS_PRINT(debug_rcv && !(skb->dev && dev && (skb->dev == dev)), ++ "klips_debug:ipsec_rcv: " ++ "Informational -- **if this happens, find out why** skb->dev:%s is not equal to dev:%s\n", ++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL", ++ dev ? (dev->name ? dev->name : "NULL") : "NULL"); ++ ++ protoc = ipp->protocol; ++#ifndef NET_21 ++ if((!protocol) || (protocol->protocol != protoc)) { ++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA, ++ "klips_debug:ipsec_rcv: " ++ "protocol arg is NULL or unequal to the packet contents, this is odd, using value in packet.\n"); ++ } ++#endif /* !NET_21 */ ++ ++ if( (protoc != IPPROTO_AH) && ++#ifdef CONFIG_IPSEC_IPCOMP_disabled_until_we_register_IPCOMP_HANDLER ++ (protoc != IPPROTO_COMP) && ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ (protoc != IPPROTO_ESP) ) { ++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA, ++ "klips_debug:ipsec_rcv: Why the hell is someone " ++ "passing me a non-ipsec protocol = %d packet? -- dropped.\n", ++ protoc); ++ goto rcvleave; ++ } ++ ++ if(skb->dev) { ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ sprintf(name, IPSEC_DEV_FORMAT, i); ++ if(!strcmp(name, skb->dev->name)) { ++ prv = (struct ipsecpriv *)(skb->dev->priv); ++ if(prv) { ++ stats = (struct net_device_stats *) &(prv->mystats); ++ } ++ ipsecdev = skb->dev; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "Info -- pkt already proc'ed a group of ipsec headers, processing next group of ipsec headers.\n"); ++ break; ++ } ++ if((ipsecdev = __ipsec_dev_get(name)) == NULL) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_error:ipsec_rcv: " ++ "device %s does not exist\n", ++ name); ++ } ++ prv = ipsecdev ? (struct ipsecpriv *)(ipsecdev->priv) : NULL; ++ prvdev = prv ? (struct device *)(prv->dev) : NULL; ++ ++#if 0 ++ KLIPS_PRINT(debug_rcv && prvdev, ++ "klips_debug:ipsec_rcv: " ++ "physical device for device %s is %s\n", ++ name, ++ prvdev->name); ++#endif ++ if(prvdev && skb->dev && ++ !strcmp(prvdev->name, skb->dev->name)) { ++ stats = prv ? ((struct net_device_stats *) &(prv->mystats)) : NULL; ++ skb->dev = ipsecdev; ++ KLIPS_PRINT(debug_rcv && prvdev, ++ "klips_debug:ipsec_rcv: " ++ "assigning packet ownership to virtual device %s from physical device %s.\n", ++ name, prvdev->name); ++ if(stats) { ++ stats->rx_packets++; ++ } ++ break; ++ } ++ } ++ } else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "device supplied with skb is NULL\n"); ++ } ++ ++ if(stats == NULL) { ++ KLIPS_PRINT((debug_rcv), ++ "klips_error:ipsec_rcv: " ++ "packet received from physical I/F (%s) not connected to ipsec I/F. Cannot record stats. May not have SA for decoding. Is IPSEC traffic expected on this I/F? Check routing.\n", ++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL"); ++ } ++ ++ KLIPS_IP_PRINT(debug_rcv, ipp); ++ ++ /* begin decapsulating loop here */ ++ ++ /* ++ The spinlock is to prevent any other process from ++ accessing or deleting the ipsec_sa hash table or any of the ++ ipsec_sa s while we are using and updating them. ++ ++ This is not optimal, but was relatively straightforward ++ at the time. A better way to do it has been planned for ++ more than a year, to lock the hash table and put reference ++ counts on each ipsec_sa instead. This is not likely to happen ++ in KLIPS1 unless a volunteer contributes it, but will be ++ designed into KLIPS2. ++ */ ++ spin_lock(&tdb_lock); ++ ++ /* set up for decap loop */ ++ irs.stats= stats; ++ irs.ipp = ipp; ++ irs.ipsp = NULL; ++ irs.ilen = 0; ++ irs.authlen=0; ++ irs.authfuncs=NULL; ++ irs.skb = skb; ++ ++ do { ++ int decap_stat; ++ struct xform_functions *proto_funcs; ++ ++ switch(irs.ipp->protocol) { ++ case IPPROTO_ESP: ++ proto_funcs = esp_xform_funcs; ++ break; ++ ++#ifdef CONFIG_IPSEC_AH ++ case IPPROTO_AH: ++ proto_funcs = ah_xform_funcs; ++ break; ++#endif /* !CONFIG_IPSEC_AH */ ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++ case IPPROTO_COMP: ++ proto_funcs = ipcomp_xform_funcs; ++ break; ++#endif /* !CONFIG_IPSEC_IPCOMP */ ++ default: ++ if(irs.stats) { ++ irs.stats->rx_errors++; ++ } ++ decap_stat = IPSEC_RCV_BADPROTO; ++ goto rcvleave; ++ } ++ ++ decap_stat = ipsec_rcv_decap_once(&irs, proto_funcs); ++ ++ if(decap_stat != IPSEC_RCV_OK) { ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: decap_once failed: %d\n", ++ decap_stat); ++ ++ goto rcvleave; ++ } ++ /* end decapsulation loop here */ ++ } while( (irs.ipp->protocol == IPPROTO_ESP ) ++ || (irs.ipp->protocol == IPPROTO_AH ) ++#ifdef CONFIG_IPSEC_IPCOMP ++ || (irs.ipp->protocol == IPPROTO_COMP) ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ); ++ ++ /* set up for decap loop */ ++ ipp =irs.ipp; ++ ipsp =irs.ipsp; ++ ipsnext = ipsp->ips_inext; ++ skb = irs.skb; ++ ++ /* if there is an IPCOMP, but we don't have an IPPROTO_COMP, ++ * then we can just skip it ++ */ ++#ifdef CONFIG_IPSEC_IPCOMP ++ if(ipsnext && ipsnext->ips_said.proto == IPPROTO_COMP) { ++ ipsp = ipsnext; ++ ipsnext = ipsp->ips_inext; ++ } ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if ((irs.natt_type) && (ipp->protocol != IPPROTO_IPIP)) { ++ /** ++ * NAT-Traversal and Transport Mode: ++ * we need to correct TCP/UDP checksum ++ * ++ * If we've got NAT-OA, we can fix checksum without recalculation. ++ */ ++ __u32 natt_oa = ipsp->ips_natt_oa ? ++ ((struct sockaddr_in*)(ipsp->ips_natt_oa))->sin_addr.s_addr : 0; ++ __u16 pkt_len = skb->tail - (unsigned char *)ipp; ++ __u16 data_len = pkt_len - (ipp->ihl << 2); ++ ++ switch (ipp->protocol) { ++ case IPPROTO_TCP: ++ if (data_len >= sizeof(struct tcphdr)) { ++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ipp+ipp->ihl); ++ if (natt_oa) { ++ __u32 buff[2] = { ~natt_oa, ipp->saddr }; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: " ++ "fix TCP checksum using NAT-OA\n"); ++ tcp->check = csum_fold( ++ csum_partial((unsigned char *)buff, sizeof(buff), ++ tcp->check^0xffff)); ++ } ++ else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: recalc TCP checksum\n"); ++ if (pkt_len > (ntohs(ipp->tot_len))) ++ data_len -= (pkt_len - ntohs(ipp->tot_len)); ++ tcp->check = 0; ++ tcp->check = csum_tcpudp_magic(ipp->saddr, ipp->daddr, ++ data_len, IPPROTO_TCP, ++ csum_partial((unsigned char *)tcp, data_len, 0)); ++ } ++ } ++ else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: can't fix TCP checksum\n"); ++ } ++ break; ++ case IPPROTO_UDP: ++ if (data_len >= sizeof(struct udphdr)) { ++ struct udphdr *udp = (struct udphdr *)((__u32 *)ipp+ipp->ihl); ++ if (udp->check == 0) { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: UDP checksum already 0\n"); ++ } ++ else if (natt_oa) { ++ __u32 buff[2] = { ~natt_oa, ipp->saddr }; ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: " ++ "fix UDP checksum using NAT-OA\n"); ++ udp->check = csum_fold( ++ csum_partial((unsigned char *)buff, sizeof(buff), ++ udp->check^0xffff)); ++ } ++ else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: zero UDP checksum\n"); ++ udp->check = 0; ++ } ++ } ++ else { ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: can't fix UDP checksum\n"); ++ } ++ break; ++ default: ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n"); ++ break; ++ } ++ } ++#endif ++ ++ /* ++ * XXX this needs to be locked from when it was first looked ++ * up in the decapsulation loop. Perhaps it is better to put ++ * the IPIP decap inside the loop. ++ */ ++ if(ipsnext) { ++ ipsp = ipsnext; ++ irs.sa_len = satot(&irs.said, 0, irs.sa, sizeof(irs.sa)); ++ if((ipp->protocol != IPPROTO_IPIP) && ++ ( 0xFE != ipp->protocol)) { /* added to support AT&T heartbeats to SIG/GIG */ ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, Hey! How did this get through? Dropped.\n", ++ irs.sa_len ? irs.sa : " (error)"); ++ if(stats) { ++ stats->rx_dropped++; ++ } ++ goto rcvleave; ++ } ++ if(sysctl_ipsec_inbound_policy_check) { ++ if((ipsnext = ipsp->ips_inext)) { ++ char sa2[SATOT_BUF]; ++ size_t sa_len2; ++ sa_len2 = satot(&ipsnext->ips_said, 0, sa2, sizeof(sa2)); ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "unexpected SA:%s after IPIP SA:%s\n", ++ sa_len2 ? sa2 : " (error)", ++ irs.sa_len ? irs.sa : " (error)"); ++ if(stats) { ++ stats->rx_dropped++; ++ } ++ goto rcvleave; ++ } ++ if(ipp->saddr != ((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr.s_addr) { ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n", ++ irs.sa_len ? irs.sa : " (error)", ++ irs.ipsaddr_txt); ++ if(stats) { ++ stats->rx_dropped++; ++ } ++ goto rcvleave; ++ } ++ } ++ ++ if(ipp->protocol == IPPROTO_IPIP) /* added to support AT&T heartbeats to SIG/GIG */ ++ { ++ /* ++ * XXX this needs to be locked from when it was first looked ++ * up in the decapsulation loop. Perhaps it is better to put ++ * the IPIP decap inside the loop. ++ */ ++ ipsp->ips_life.ipl_bytes.ipl_count += skb->len; ++ ipsp->ips_life.ipl_bytes.ipl_last = skb->len; ++ ++ if(!ipsp->ips_life.ipl_usetime.ipl_count) { ++ ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; ++ } ++ ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; ++ ipsp->ips_life.ipl_packets.ipl_count += 1; ++ ++ if(skb->len < irs.iphlen) { ++ spin_unlock(&tdb_lock); ++ printk(KERN_WARNING "klips_debug:ipsec_rcv: " ++ "tried to skb_pull iphlen=%d, %d available. This should never happen, please report.\n", ++ irs.iphlen, ++ (int)(skb->len)); ++ ++ goto rcvleave; ++ } ++ skb_pull(skb, irs.iphlen); ++ ++#ifdef NET_21 ++ ipp = (struct iphdr *)skb->nh.raw = skb->data; ++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2); ++ ++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); ++#else /* NET_21 */ ++ ipp = skb->ip_hdr = skb->h.iph = (struct iphdr *)skb->data; ++ ++ memset(skb->proto_priv, 0, sizeof(struct options)); ++#endif /* NET_21 */ ++ ipsaddr.s_addr = ipp->saddr; ++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt)); ++ ipdaddr.s_addr = ipp->daddr; ++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt)); ++ ++ skb->protocol = htons(ETH_P_IP); ++ skb->ip_summed = 0; ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "IPIP tunnel stripped.\n"); ++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp); ++ } ++ ++ if(sysctl_ipsec_inbound_policy_check ++ /* ++ Note: "xor" (^) logically replaces "not equal" ++ (!=) and "bitwise or" (|) logically replaces ++ "boolean or" (||). This is done to speed up ++ execution by doing only bitwise operations and ++ no branch operations ++ */ ++ && (((ipp->saddr & ipsp->ips_mask_s.u.v4.sin_addr.s_addr) ++ ^ ipsp->ips_flow_s.u.v4.sin_addr.s_addr) ++ | ((ipp->daddr & ipsp->ips_mask_d.u.v4.sin_addr.s_addr) ++ ^ ipsp->ips_flow_d.u.v4.sin_addr.s_addr)) ) ++ { ++ char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF]; ++ ++ subnettoa(ipsp->ips_flow_s.u.v4.sin_addr, ++ ipsp->ips_mask_s.u.v4.sin_addr, ++ 0, sflow_txt, sizeof(sflow_txt)); ++ subnettoa(ipsp->ips_flow_d.u.v4.sin_addr, ++ ipsp->ips_mask_d.u.v4.sin_addr, ++ 0, dflow_txt, sizeof(dflow_txt)); ++ spin_unlock(&tdb_lock); ++ KLIPS_PRINT(debug_rcv, ++ "klips_debug:ipsec_rcv: " ++ "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n", ++ irs.sa_len ? irs.sa : " (error)", ++ sflow_txt, ++ dflow_txt, ++ irs.ipsaddr_txt, ++ irs.ipdaddr_txt); ++ if(stats) { ++ stats->rx_dropped++; ++ } ++ goto rcvleave; ++ } ++#ifdef CONFIG_NETFILTER ++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_TABLE_MASK)))) ++ | IPsecSAref2NFmark(IPsecSA2SAref(ipsp)); ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "IPIP SA sets skb->nfmark=0x%x.\n", ++ (unsigned)skb->nfmark); ++#endif /* CONFIG_NETFILTER */ ++ } ++ ++ spin_unlock(&tdb_lock); ++ ++#ifdef NET_21 ++ if(stats) { ++ stats->rx_bytes += skb->len; ++ } ++ if(skb->dst) { ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ } ++ skb->pkt_type = PACKET_HOST; ++ if(irs.hard_header_len && ++ (skb->mac.raw != (skb->data - irs.hard_header_len)) && ++ (irs.hard_header_len <= skb_headroom(skb))) { ++ /* copy back original MAC header */ ++ memmove(skb->data - irs.hard_header_len, skb->mac.raw, irs.hard_header_len); ++ skb->mac.raw = skb->data - irs.hard_header_len; ++ } ++#endif /* NET_21 */ ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++ if(ipp->protocol == IPPROTO_COMP) { ++ unsigned int flags = 0; ++ ++ if(sysctl_ipsec_inbound_policy_check) { ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "inbound policy checking enabled, IPCOMP follows IPIP, dropped.\n"); ++ if (stats) { ++ stats->rx_errors++; ++ } ++ goto rcvleave; ++ } ++ /* ++ XXX need a ipsec_sa for updating ratio counters but it is not ++ following policy anyways so it is not a priority ++ */ ++ skb = skb_decompress(skb, NULL, &flags); ++ if (!skb || flags) { ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "skb_decompress() returned error flags: %d, dropped.\n", ++ flags); ++ if (stats) { ++ stats->rx_errors++; ++ } ++ goto rcvleave; ++ } ++ } ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#ifdef SKB_RESET_NFCT ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = NULL; ++#ifdef CONFIG_NETFILTER_DEBUG ++ skb->nf_debug = 0; ++#endif /* CONFIG_NETFILTER_DEBUG */ ++#endif /* SKB_RESET_NFCT */ ++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX, ++ "klips_debug:ipsec_rcv: " ++ "netif_rx() called.\n"); ++ netif_rx(skb); ++ ++ MOD_DEC_USE_COUNT; ++ return(0); ++ ++ rcvleave: ++ if(skb) { ++ ipsec_kfree_skb(skb); ++ } ++ ++ MOD_DEC_USE_COUNT; ++ return(0); ++} ++ ++/* ++ * $Log: ipsec_rcv.c,v $ ++ * Revision 1.143.4.2 2004/08/22 03:29:06 mcr ++ * include udp.h regardless of nat-t support. ++ * ++ * Revision 1.143.4.1 2004/08/21 02:14:58 ken ++ * Patch from Jochen Eisinger for AT&T MTS Heartbeat packet support ++ * ++ * Revision 1.143 2004/05/10 22:27:00 mcr ++ * fix for ESP-3DES-noauth test case. ++ * ++ * Revision 1.142 2004/05/10 22:25:57 mcr ++ * reformat of calls to ipsec_lifetime_check(). ++ * ++ * Revision 1.141 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.140 2004/02/03 03:12:53 mcr ++ * removed erroneously, double patched code. ++ * ++ * Revision 1.139 2004/01/05 23:21:29 mcr ++ * initialize sin_family in ipsec_rcv.c ++ * ++ * Revision 1.138 2003/12/24 19:46:52 mcr ++ * if sock.h patch has not been applied, then define appropriate ++ * structure so we can use it. This is serious inferior, and ++ * depends upon the concept that the structure in question is ++ * smaller than the other members of that union. ++ * getting rid of differing methods is a better solution. ++ * ++ * Revision 1.137 2003/12/22 19:40:57 mcr ++ * NAT-T patches 0.6c. ++ * ++ * Revision 1.136 2003/12/15 18:13:12 mcr ++ * when compiling with NAT traversal, don't assume that the ++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP ++ * is set. ++ * ++ * Revision 1.135 2003/12/13 19:10:21 mcr ++ * refactored rcv and xmit code - same as FS 2.05. ++ * ++ * Revision 1.134.2.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.134 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.133 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.132.2.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.132 2003/09/02 19:51:48 mcr ++ * fixes for PR#252. ++ * ++ * Revision 1.131 2003/07/31 22:47:16 mcr ++ * preliminary (untested by FS-team) 2.5 patches. ++ * ++ * Revision 1.130 2003/04/03 17:38:25 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * Clarified logic for non-connected devices. ++ * ++ * Revision 1.129 2003/02/06 02:21:34 rgb ++ * ++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . ++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". ++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. ++ * ++ * Revision 1.128 2002/12/13 20:58:03 rgb ++ * Relegated MCR's recent "_dmp" routine to debug_verbose. ++ * Cleaned up printing of source and destination addresses in debug output. ++ * ++ * Revision 1.127 2002/12/04 16:00:16 rgb ++ * ++ * Fixed AH decapsulation pointer update bug and added some comments and ++ * debugging. ++ * This bug was caught by west-ah-0[12]. ++ * ++ * Revision 1.126 2002/11/04 05:03:43 mcr ++ * fixes for IPCOMP. There were two problems: ++ * 1) the irs->ipp pointer was not being updated properly after ++ * the ESP descryption. The meant nothing for IPIP, as the ++ * later IP header overwrote the earlier one. ++ * 2) the more serious problem was that skb_decompress will ++ * usually allocate a new SKB, so we have to make sure that ++ * it doesn't get lost. ++ * #2 meant removing the skb argument from the ->decrypt routine ++ * and moving it to the irs->skb, so it could be value/result. ++ * ++ * Revision 1.125 2002/11/01 01:53:35 dhr ++ * ++ * fix typo ++ * ++ * Revision 1.124 2002/10/31 22:49:01 dhr ++ * ++ * - eliminate unused variable "hash" ++ * - reduce scope of variable "authenticator" ++ * - add comment on a couple of tricky bits ++ * ++ * Revision 1.123 2002/10/31 22:39:56 dhr ++ * ++ * use correct type for result of function calls ++ * ++ * Revision 1.122 2002/10/31 22:36:25 dhr ++ * ++ * simplify complex test ++ * ++ * Revision 1.121 2002/10/31 22:34:04 dhr ++ * ++ * ipsprev is never used: ditch it ++ * ++ * Revision 1.120 2002/10/31 22:30:21 dhr ++ * ++ * eliminate redundant assignments ++ * ++ * Revision 1.119 2002/10/31 22:27:43 dhr ++ * ++ * make whitespace canonical ++ * ++ * Revision 1.118 2002/10/30 05:47:17 rgb ++ * Fixed cut-and-paste error mis-identifying comp runt as ah. ++ * ++ * Revision 1.117 2002/10/17 16:37:45 rgb ++ * Remove compp intermediate variable and in-line its contents ++ * where used ++ * ++ * Revision 1.116 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.115 2002/10/07 19:06:58 rgb ++ * Minor fixups and activation to west-rcv-nfmark-set-01 test to check for SA reference properly set on incoming. ++ * ++ * Revision 1.114 2002/10/07 18:31:31 rgb ++ * Set saref on incoming packets. ++ * ++ * Revision 1.113 2002/09/16 21:28:12 mcr ++ * adjust hash length for HMAC calculation - must look at whether ++ * it is MD5 or SHA1. ++ * ++ * Revision 1.112 2002/09/16 21:19:15 mcr ++ * fixes for west-ah-icmp-01 - length of AH header must be ++ * calculated properly, and next_header field properly copied. ++ * ++ * Revision 1.111 2002/09/10 02:45:56 mcr ++ * re-factored the ipsec_rcv function into several functions, ++ * ipsec_rcv_decap_once, and a set of functions for AH, ESP and IPCOMP. ++ * In addition, the MD5 and SHA1 functions are replaced with pointers. ++ * ++ * Revision 1.110 2002/08/30 06:34:33 rgb ++ * Fix scope of shift in AH header length check. ++ * ++ * Revision 1.109 2002/08/27 16:49:20 rgb ++ * Fixed ESP short packet DOS (and AH and IPCOMP). ++ * ++ * Revision 1.108 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.107 2002/05/27 18:58:18 rgb ++ * Convert to dynamic ipsec device allocation. ++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. ++ * ++ * Revision 1.106 2002/05/23 07:15:21 rgb ++ * Pointer clean-up. ++ * Added refcount code. ++ * ++ * Revision 1.105 2002/05/14 02:35:06 rgb ++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, ++ * ipsec_sa or ipsec_sa. ++ * Change references to _TDB to _IPSA. ++ * ++ * Revision 1.104 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.103 2002/04/24 07:36:30 mcr ++ * Moved from ./klips/net/ipsec/ipsec_rcv.c,v ++ * ++ * Revision 1.102 2002/01/29 17:17:56 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.101 2002/01/29 04:00:52 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.100 2002/01/29 02:13:17 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.99 2002/01/28 21:40:59 mcr ++ * should use #if to test boolean option rather than #ifdef. ++ * ++ * Revision 1.98 2002/01/20 20:19:36 mcr ++ * renamed option to IP_FRAGMENT_LINEARIZE. ++ * ++ * Revision 1.97 2002/01/12 02:55:36 mcr ++ * fix for post-2.4.4 to linearize skb's when ESP packet ++ * was assembled from fragments. ++ * ++ * Revision 1.96 2001/11/26 09:23:49 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.93.2.2 2001/10/22 20:54:07 mcr ++ * include des.h, removed phony prototypes and fixed calling ++ * conventions to match real prototypes. ++ * ++ * Revision 1.93.2.1 2001/09/25 02:22:22 mcr ++ * struct tdb -> struct ipsec_sa. ++ * lifetime checks moved to ipsec_life.c ++ * some sa(tdb) manipulation functions renamed. ++ * ++ * Revision 1.95 2001/11/06 19:49:07 rgb ++ * Added variable descriptions. ++ * Removed unauthenticated sequence==0 check to prevent DoS. ++ * ++ * Revision 1.94 2001/10/18 04:45:20 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.93 2001/09/07 22:17:24 rgb ++ * Fix for removal of transport layer protocol handler arg in 2.4.4. ++ * Fix to accomodate peer non-conformance to IPCOMP rfc2393. ++ * ++ * Revision 1.92 2001/08/27 19:44:41 rgb ++ * Fix error in comment. ++ * ++ * Revision 1.91 2001/07/20 19:31:48 dhr ++ * [DHR] fix source and destination subnets of policy in diagnostic ++ * ++ * Revision 1.90 2001/07/06 19:51:09 rgb ++ * Added inbound policy checking code for IPIP SAs. ++ * Renamed unused function argument for ease and intuitive naming. ++ * ++ * Revision 1.89 2001/06/22 19:35:23 rgb ++ * Disable ipcomp processing if we are handed a ipcomp packet with no esp ++ * or ah header. ++ * Print protocol if we are handed a non-ipsec packet. ++ * ++ * Revision 1.88 2001/06/20 06:30:47 rgb ++ * Fixed transport mode IPCOMP policy check bug. ++ * ++ * Revision 1.87 2001/06/13 20:58:40 rgb ++ * Added parentheses around assignment used as truth value to silence ++ * compiler. ++ * ++ * Revision 1.86 2001/06/07 22:25:23 rgb ++ * Added a source address policy check for tunnel mode. It still does ++ * not check client addresses and masks. ++ * Only decapsulate IPIP if it is expected. ++ * ++ * Revision 1.85 2001/05/30 08:14:02 rgb ++ * Removed vestiges of esp-null transforms. ++ * ++ * Revision 1.84 2001/05/27 06:12:11 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.83 2001/05/04 16:45:47 rgb ++ * Remove unneeded code. ipp is not used after this point. ++ * ++ * Revision 1.82 2001/05/04 16:36:00 rgb ++ * Fix skb_cow() call for 2.4.4. (SS) ++ * ++ * Revision 1.81 2001/05/02 14:46:53 rgb ++ * Fix typo for compiler directive to pull IPH back. ++ * ++ * Revision 1.80 2001/04/30 19:46:34 rgb ++ * Update for 2.4.4. We now receive the skb with skb->data pointing to ++ * h.raw. ++ * ++ * Revision 1.79 2001/04/23 15:01:15 rgb ++ * Added spin_lock() check to prevent double-locking for multiple ++ * transforms and hence kernel lock-ups with SMP kernels. ++ * Minor spin_unlock() adjustments to unlock before non-dependant prints ++ * and IPSEC device stats updates. ++ * ++ * Revision 1.78 2001/04/21 23:04:24 rgb ++ * Check if soft expire has already been sent before sending another to ++ * prevent ACQUIRE flooding. ++ * ++ * Revision 1.77 2001/03/16 07:35:20 rgb ++ * Ditch extra #if 1 around now permanent policy checking code. ++ * ++ * Revision 1.76 2001/02/27 22:24:54 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.75 2001/02/19 22:28:30 rgb ++ * Minor change to virtual device discovery code to assert which I/F has ++ * been found. ++ * ++ * Revision 1.74 2000/11/25 03:50:36 rgb ++ * Oops fix by minor re-arrangement of code to avoid accessing a freed tdb. ++ * ++ * Revision 1.73 2000/11/09 20:52:15 rgb ++ * More spinlock shuffling, locking earlier and unlocking later in rcv to ++ * include ipcomp and prevent races, renaming some tdb variables that got ++ * forgotten, moving some unlocks to include tdbs and adding a missing ++ * unlock. Thanks to Svenning for some of these. ++ * ++ * Revision 1.72 2000/11/09 20:11:22 rgb ++ * Minor shuffles to fix non-standard kernel config option selection. ++ * ++ * Revision 1.71 2000/11/06 04:36:18 rgb ++ * Ditched spin_lock_irqsave in favour of spin_lock. ++ * Minor initial protocol check rewrite. ++ * Clean up debug printing. ++ * Clean up tdb handling on ipcomp. ++ * Fixed transport mode null pointer de-reference without ipcomp. ++ * Add Svenning's adaptive content compression. ++ * Disabled registration of ipcomp handler. ++ * ++ * Revision 1.70 2000/10/30 23:41:43 henry ++ * Hans-Joerg Hoexer's null-pointer fix ++ * ++ * Revision 1.69 2000/10/10 18:54:16 rgb ++ * Added a fix for incoming policy check with ipcomp enabled but ++ * uncompressible. ++ * ++ * Revision 1.68 2000/09/22 17:53:12 rgb ++ * Fixed ipcomp tdb pointers update for policy checking. ++ * ++ * Revision 1.67 2000/09/21 03:40:58 rgb ++ * Added more debugging to try and track down the cpi outward copy problem. ++ * ++ * Revision 1.66 2000/09/20 04:00:10 rgb ++ * Changed static functions to DEBUG_NO_STATIC to reveal function names for ++ * debugging oopsen. ++ * ++ * Revision 1.65 2000/09/19 07:07:16 rgb ++ * Added debugging to inbound policy check for ipcomp. ++ * Added missing spin_unlocks (thanks Svenning!). ++ * Fixed misplaced tdbnext pointers causing mismatched ipip policy check. ++ * Protect ipcomp policy check following ipip decap with sysctl switch. ++ * ++ * Revision 1.64 2000/09/18 21:27:29 rgb ++ * 2.0 fixes. ++ * ++ * Revision 1.63 2000/09/18 02:35:50 rgb ++ * Added policy checking to ipcomp and re-enabled policy checking by ++ * default. ++ * Optimised satoa calls. ++ * ++ * Revision 1.62 2000/09/17 21:02:32 rgb ++ * Clean up debugging, removing slow timestamp debug code. ++ * ++ * Revision 1.61 2000/09/16 01:07:55 rgb ++ * Fixed erroneous ref from struct ipcomp to struct ipcomphdr. ++ * ++ * Revision 1.60 2000/09/15 11:37:01 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.59 2000/09/15 04:56:20 rgb ++ * Remove redundant satoa() call, reformat comment. ++ * ++ * Revision 1.58 2000/09/13 08:00:52 rgb ++ * Flick on inbound policy checking. ++ * ++ * Revision 1.57 2000/09/12 03:22:19 rgb ++ * Converted inbound_policy_check to sysctl. ++ * Re-enabled policy backcheck. ++ * Moved policy checks to top and within tdb lock. ++ * ++ * Revision 1.56 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.55 2000/08/28 18:15:46 rgb ++ * Added MB's nf-debug reset patch. ++ * ++ * Revision 1.54 2000/08/27 01:41:26 rgb ++ * More minor tweaks to the bad padding debug code. ++ * ++ * Revision 1.53 2000/08/24 16:54:16 rgb ++ * Added KLIPS_PRINTMORE macro to continue lines without KERN_INFO level ++ * info. ++ * Tidied up device reporting at the start of ipsec_rcv. ++ * Tidied up bad padding debugging and processing. ++ * ++ * Revision 1.52 2000/08/20 21:36:03 rgb ++ * Activated pfkey_expire() calls. ++ * Added a hard/soft expiry parameter to pfkey_expire(). ++ * Added sanity checking to avoid propagating zero or smaller-length skbs ++ * from a bogus decryption. ++ * Re-arranged the order of soft and hard expiry to conform to RFC2367. ++ * Clean up references to CONFIG_IPSEC_PFKEYv2. ++ * ++ * Revision 1.51 2000/08/18 21:23:30 rgb ++ * Improve bad padding warning so that the printk buffer doesn't get ++ * trampled. ++ * ++ * Revision 1.50 2000/08/01 14:51:51 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.49 2000/07/28 13:50:53 rgb ++ * Changed enet_statistics to net_device_stats and added back compatibility ++ * for pre-2.1.19. ++ * ++ * Revision 1.48 2000/05/10 19:14:40 rgb ++ * Only check usetime against soft and hard limits if the tdb has been ++ * used. ++ * Cast output of ntohl so that the broken prototype doesn't make our ++ * compile noisy. ++ * ++ * Revision 1.47 2000/05/09 17:45:43 rgb ++ * Fix replay bitmap corruption bug upon receipt of bogus packet ++ * with correct SPI. This was a DoS. ++ * ++ * Revision 1.46 2000/03/27 02:31:58 rgb ++ * Fixed authentication failure printout bug. ++ * ++ * Revision 1.45 2000/03/22 16:15:37 rgb ++ * Fixed renaming of dev_get (MB). ++ * ++ * Revision 1.44 2000/03/16 08:17:24 rgb ++ * Hardcode PF_KEYv2 support. ++ * Fixed minor bug checking AH header length. ++ * ++ * Revision 1.43 2000/03/14 12:26:59 rgb ++ * Added skb->nfct support for clearing netfilter conntrack bits (MB). ++ * ++ * Revision 1.42 2000/01/26 10:04:04 rgb ++ * Fixed inbound policy checking on transport mode bug. ++ * Fixed noisy 2.0 printk arguments. ++ * ++ * Revision 1.41 2000/01/24 20:58:02 rgb ++ * Improve debugging/reporting support for (disabled) inbound ++ * policy checking. ++ * ++ * Revision 1.40 2000/01/22 23:20:10 rgb ++ * Fixed up inboud policy checking code. ++ * Cleaned out unused crud. ++ * ++ * Revision 1.39 2000/01/21 06:15:29 rgb ++ * Added sanity checks on skb_push(), skb_pull() to prevent panics. ++ * Fixed cut-and-paste debug_tunnel to debug_rcv. ++ * Added inbound policy checking code, disabled. ++ * Simplified output code by updating ipp to post-IPIP decapsulation. ++ * ++ * elided pre-2000 comments. Use "cvs log" ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_sa.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1383 @@ ++/* ++ * Common routines for IPsec SA maintenance routines. ++ * ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_sa.c,v 1.23 2004/04/06 02:49:26 mcr Exp $ ++ * ++ * This is the file formerly known as "ipsec_xform.h" ++ * ++ */ ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* vmalloc() */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include ++#ifdef SPINLOCK ++#ifdef SPINLOCK_23 ++#include /* *lock* */ ++#else /* SPINLOCK_23 */ ++#include /* *lock* */ ++#endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++#include ++#include ++#endif ++#include ++#include ++ ++#include "openswan/radij.h" ++ ++#include "openswan/ipsec_stats.h" ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_sa.h" ++#include "openswan/ipsec_xform.h" ++ ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_ipe4.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_xform = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD]; ++#ifdef SPINLOCK ++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED; ++#else /* SPINLOCK */ ++spinlock_t tdb_lock; ++#endif /* SPINLOCK */ ++ ++struct ipsec_sadb ipsec_sadb; ++ ++#if IPSEC_SA_REF_CODE ++ ++/* the sub table must be narrower (or equal) in bits than the variable type ++ in the main table to count the number of unused entries in it. */ ++typedef struct { ++ int testSizeOf_refSubTable : ++ ((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_IDX_WIDTH ? -1 : 1); ++} dummy; ++ ++ ++/* The field where the saref will be hosted in the skb must be wide enough to ++ accomodate the information it needs to store. */ ++typedef struct { ++ int testSizeOf_refField : ++ (IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_IDX_WIDTH ? -1 : 1 ); ++} dummy2; ++ ++ ++#define IPS_HASH(said) (((said)->spi + (said)->dst.u.v4.sin_addr.s_addr + (said)->proto) % SADB_HASHMOD) ++ ++ ++void ++ipsec_SAtest(void) ++{ ++ IPsecSAref_t SAref = 258; ++ struct ipsec_sa ips; ++ ips.ips_ref = 772; ++ ++ printk("klips_debug:ipsec_SAtest: " ++ "IPSEC_SA_REF_SUBTABLE_IDX_WIDTH=%u\n" ++ "IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES=%u\n" ++ "IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES=%u\n" ++ "IPSEC_SA_REF_HOST_FIELD_WIDTH=%lu\n" ++ "IPSEC_SA_REF_TABLE_MASK=%x\n" ++ "IPSEC_SA_REF_ENTRY_MASK=%x\n" ++ "IPsecSAref2table(%d)=%u\n" ++ "IPsecSAref2entry(%d)=%u\n" ++ "IPsecSAref2NFmark(%d)=%u\n" ++ "IPsecSAref2SA(%d)=%p\n" ++ "IPsecSA2SAref(%p)=%d\n" ++ , ++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH, ++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES, ++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES, ++ (unsigned long) IPSEC_SA_REF_HOST_FIELD_WIDTH, ++ IPSEC_SA_REF_TABLE_MASK, ++ IPSEC_SA_REF_ENTRY_MASK, ++ SAref, IPsecSAref2table(SAref), ++ SAref, IPsecSAref2entry(SAref), ++ SAref, IPsecSAref2NFmark(SAref), ++ SAref, IPsecSAref2SA(SAref), ++ (&ips), IPsecSA2SAref((&ips)) ++ ); ++ return; ++} ++ ++int ++ipsec_SAref_recycle(void) ++{ ++ int table; ++ int entry; ++ int error = 0; ++ ++ ipsec_sadb.refFreeListHead = -1; ++ ipsec_sadb.refFreeListTail = -1; ++ ++ if(ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_recycle: " ++ "end of table reached, continuing at start..\n"); ++ ipsec_sadb.refFreeListCont = 0; ++ } ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_recycle: " ++ "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n", ++ ipsec_sadb.refFreeListCont, ++ (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb.refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL, ++ IPsecSAref2table(ipsec_sadb.refFreeListCont), ++ IPsecSAref2entry(ipsec_sadb.refFreeListCont)); ++ ++ for(table = IPsecSAref2table(ipsec_sadb.refFreeListCont); ++ table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; ++ table++) { ++ if(ipsec_sadb.refTable[table] == NULL) { ++ error = ipsec_SArefSubTable_alloc(table); ++ if(error) { ++ return error; ++ } ++ } ++ for(entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont); ++ entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; ++ entry++) { ++ if(ipsec_sadb.refTable[table]->entry[entry] == NULL) { ++ ipsec_sadb.refFreeList[++ipsec_sadb.refFreeListTail] = IPsecSArefBuild(table, entry); ++ if(ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_NUM_ENTRIES - 1)) { ++ ipsec_sadb.refFreeListHead = 0; ++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1; ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_recycle: " ++ "SArefFreeList refilled.\n"); ++ return 0; ++ } ++ } ++ } ++ } ++ ++ if(ipsec_sadb.refFreeListTail == -1) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_recycle: " ++ "out of room in the SArefTable.\n"); ++ ++ return(-ENOSPC); ++ } ++ ++ ipsec_sadb.refFreeListHead = 0; ++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1; ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_recycle: " ++ "SArefFreeList partly refilled to %d of %d.\n", ++ ipsec_sadb.refFreeListTail, ++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES); ++ return 0; ++} ++ ++int ++ipsec_SArefSubTable_alloc(unsigned table) ++{ ++ unsigned entry; ++ struct IPsecSArefSubTable* SArefsub; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SArefSubTable_alloc: " ++ "allocating %lu bytes for table %u of %u.\n", ++ (unsigned long) (IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)), ++ table, ++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); ++ ++ /* allocate another sub-table */ ++ SArefsub = vmalloc(IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)); ++ if(SArefsub == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SArefSubTable_alloc: " ++ "error allocating memory for table %u of %u!\n", ++ table, ++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES); ++ return -ENOMEM; ++ } ++ ++ /* add this sub-table to the main table */ ++ ipsec_sadb.refTable[table] = SArefsub; ++ ++ /* initialise each element to NULL */ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SArefSubTable_alloc: " ++ "initialising %u elements (2 ^ %u) of table %u.\n", ++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES, ++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH, ++ table); ++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { ++ SArefsub->entry[entry] = NULL; ++ } ++ ++ return 0; ++} ++#endif /* IPSEC_SA_REF_CODE */ ++ ++int ++ipsec_saref_freelist_init(void) ++{ ++ int i; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_saref_freelist_init: " ++ "initialising %u elements of FreeList.\n", ++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES); ++ ++ for(i = 0; i < IPSEC_SA_REF_FREELIST_NUM_ENTRIES; i++) { ++ ipsec_sadb.refFreeList[i] = IPSEC_SAREF_NULL; ++ } ++ ipsec_sadb.refFreeListHead = -1; ++ ipsec_sadb.refFreeListCont = 0; ++ ipsec_sadb.refFreeListTail = -1; ++ ++ return 0; ++} ++ ++int ++ipsec_sadb_init(void) ++{ ++ int error = 0; ++ unsigned i; ++ ++ for(i = 0; i < SADB_HASHMOD; i++) { ++ ipsec_sadb_hash[i] = NULL; ++ } ++ /* parts above are for the old style SADB hash table */ ++ ++ ++#if IPSEC_SA_REF_CODE ++ /* initialise SA reference table */ ++ ++ /* initialise the main table */ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_init: " ++ "initialising main table of size %u (2 ^ %u).\n", ++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES, ++ IPSEC_SA_REF_MAINTABLE_IDX_WIDTH); ++ { ++ unsigned table; ++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { ++ ipsec_sadb.refTable[table] = NULL; ++ } ++ } ++ ++ /* allocate the first sub-table */ ++ error = ipsec_SArefSubTable_alloc(0); ++ if(error) { ++ return error; ++ } ++ ++ error = ipsec_saref_freelist_init(); ++#endif /* IPSEC_SA_REF_CODE */ ++ return error; ++} ++ ++#if IPSEC_SA_REF_CODE ++IPsecSAref_t ++ipsec_SAref_alloc(int*error) /* pass in error var by pointer */ ++{ ++ IPsecSAref_t SAref; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_alloc: " ++ "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n", ++ ipsec_sadb.refFreeListHead, ++ ipsec_sadb.refFreeListCont, ++ ipsec_sadb.refFreeListTail, ++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES); ++ ++ if(ipsec_sadb.refFreeListHead == -1) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_alloc: " ++ "FreeList empty, recycling...\n"); ++ *error = ipsec_SAref_recycle(); ++ if(*error) { ++ return IPSEC_SAREF_NULL; ++ } ++ } ++ ++ SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead]; ++ if(SAref == IPSEC_SAREF_NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_alloc: " ++ "unexpected error, refFreeListHead = %d points to invalid entry.\n", ++ ipsec_sadb.refFreeListHead); ++ *error = -ESPIPE; ++ return IPSEC_SAREF_NULL; ++ } ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_alloc: " ++ "allocating SAref=%d, table=%u, entry=%u of %u.\n", ++ SAref, ++ IPsecSAref2table(SAref), ++ IPsecSAref2entry(SAref), ++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES); ++ ++ ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = IPSEC_SAREF_NULL; ++ ipsec_sadb.refFreeListHead++; ++ if(ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_SAref_alloc: " ++ "last FreeList entry allocated, resetting list head to empty.\n"); ++ ipsec_sadb.refFreeListHead = -1; ++ } ++ ++ return SAref; ++} ++#endif /* IPSEC_SA_REF_CODE */ ++ ++int ++ipsec_sa_print(struct ipsec_sa *ips) ++{ ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ printk(KERN_INFO "klips_debug: SA:"); ++ if(ips == NULL) { ++ printk("NULL\n"); ++ return -ENOENT; ++ } ++ printk(" ref=%d", ips->ips_ref); ++ printk(" refcount=%d", atomic_read(&ips->ips_refcount)); ++ if(ips->ips_hnext != NULL) { ++ printk(" hnext=0p%p", ips->ips_hnext); ++ } ++ if(ips->ips_inext != NULL) { ++ printk(" inext=0p%p", ips->ips_inext); ++ } ++ if(ips->ips_onext != NULL) { ++ printk(" onext=0p%p", ips->ips_onext); ++ } ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ printk(" said=%s", sa_len ? sa : " (error)"); ++ if(ips->ips_seq) { ++ printk(" seq=%u", ips->ips_seq); ++ } ++ if(ips->ips_pid) { ++ printk(" pid=%u", ips->ips_pid); ++ } ++ if(ips->ips_authalg) { ++ printk(" authalg=%u", ips->ips_authalg); ++ } ++ if(ips->ips_encalg) { ++ printk(" encalg=%u", ips->ips_encalg); ++ } ++ printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips)); ++ if(ips->ips_replaywin) { ++ printk(" ooowin=%u", ips->ips_replaywin); ++ } ++ if(ips->ips_flags) { ++ printk(" flags=%u", ips->ips_flags); ++ } ++ if(ips->ips_addr_s) { ++ char buf[SUBNETTOA_BUF]; ++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr, ++ 0, buf, sizeof(buf)); ++ printk(" src=%s", buf); ++ } ++ if(ips->ips_addr_d) { ++ char buf[SUBNETTOA_BUF]; ++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr, ++ 0, buf, sizeof(buf)); ++ printk(" dst=%s", buf); ++ } ++ if(ips->ips_addr_p) { ++ char buf[SUBNETTOA_BUF]; ++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_p))->sin_addr, ++ 0, buf, sizeof(buf)); ++ printk(" proxy=%s", buf); ++ } ++ if(ips->ips_key_bits_a) { ++ printk(" key_bits_a=%u", ips->ips_key_bits_a); ++ } ++ if(ips->ips_key_bits_e) { ++ printk(" key_bits_e=%u", ips->ips_key_bits_e); ++ } ++ ++ printk("\n"); ++ return 0; ++} ++ ++struct ipsec_sa* ++ipsec_sa_alloc(int*error) /* pass in error var by pointer */ ++{ ++ struct ipsec_sa* ips; ++ ++ if((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_alloc: " ++ "memory allocation error\n"); ++ *error = -ENOMEM; ++ return NULL; ++ } ++ memset((caddr_t)ips, 0, sizeof(*ips)); ++#if IPSEC_SA_REF_CODE ++ ips->ips_ref = ipsec_SAref_alloc(error); /* pass in error return by pointer */ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_alloc: " ++ "allocated %lu bytes for ipsec_sa struct=0p%p ref=%d.\n", ++ (unsigned long) sizeof(*ips), ++ ips, ++ ips->ips_ref); ++ if(ips->ips_ref == IPSEC_SAREF_NULL) { ++ kfree(ips); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_alloc: " ++ "SAref allocation error\n"); ++ return NULL; ++ } ++ ++ atomic_inc(&ips->ips_refcount); ++ IPsecSAref2SA(ips->ips_ref) = ips; ++#endif /* IPSEC_SA_REF_CODE */ ++ ++ *error = 0; ++ return(ips); ++} ++ ++int ++ipsec_sa_free(struct ipsec_sa* ips) ++{ ++ return ipsec_sa_wipe(ips); ++} ++ ++struct ipsec_sa * ++ipsec_sa_getbyid(ip_said *said) ++{ ++ int hashval; ++ struct ipsec_sa *ips; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ if(said == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_getbyid: " ++ "null pointer passed in!\n"); ++ return NULL; ++ } ++ ++ sa_len = satot(said, 0, sa, sizeof(sa)); ++ ++ hashval = IPS_HASH(said); ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_getbyid: " ++ "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n", ++ hashval, ++ sa_len ? sa : " (error)"); ++ ++ if((ips = ipsec_sadb_hash[hashval]) == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_getbyid: " ++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n", ++ hashval, ++ sa_len ? sa : " (error)"); ++ return NULL; ++ } ++ ++ for (; ips; ips = ips->ips_hnext) { ++ if ((ips->ips_said.spi == said->spi) && ++ (ips->ips_said.dst.u.v4.sin_addr.s_addr == said->dst.u.v4.sin_addr.s_addr) && ++ (ips->ips_said.proto == said->proto)) { ++ atomic_inc(&ips->ips_refcount); ++ return ips; ++ } ++ } ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_getbyid: " ++ "no entry in linked list for hash=%d of SA:%s.\n", ++ hashval, ++ sa_len ? sa : " (error)"); ++ return NULL; ++} ++ ++int ++ipsec_sa_put(struct ipsec_sa *ips) ++{ ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ if(ips == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_put: " ++ "null pointer passed in!\n"); ++ return -1; ++ } ++ ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_put: " ++ "ipsec_sa SA:%s, ref:%d reference count decremented.\n", ++ sa_len ? sa : " (error)", ++ ips->ips_ref); ++ ++ atomic_dec(&ips->ips_refcount); ++ ++ return 0; ++} ++ ++/* ++ The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen ++*/ ++int ++ipsec_sa_add(struct ipsec_sa *ips) ++{ ++ int error = 0; ++ unsigned int hashval; ++ ++ if(ips == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_add: " ++ "null pointer passed in!\n"); ++ return -ENODATA; ++ } ++ hashval = IPS_HASH(&ips->ips_said); ++ ++ atomic_inc(&ips->ips_refcount); ++ spin_lock_bh(&tdb_lock); ++ ++ ips->ips_hnext = ipsec_sadb_hash[hashval]; ++ ipsec_sadb_hash[hashval] = ips; ++ ++ spin_unlock_bh(&tdb_lock); ++ ++ return error; ++} ++ ++/* ++ The ipsec_sa table better be locked before it is handed in, or races might happen ++*/ ++int ++ipsec_sa_del(struct ipsec_sa *ips) ++{ ++ unsigned int hashval; ++ struct ipsec_sa *ipstp; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ if(ips == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_del: " ++ "null pointer passed in!\n"); ++ return -ENODATA; ++ } ++ ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ if(ips->ips_inext || ips->ips_onext) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_del: " ++ "SA:%s still linked!\n", ++ sa_len ? sa : " (error)"); ++ return -EMLINK; ++ } ++ ++ hashval = IPS_HASH(&ips->ips_said); ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_del: " ++ "deleting SA:%s, hashval=%d.\n", ++ sa_len ? sa : " (error)", ++ hashval); ++ if(ipsec_sadb_hash[hashval] == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_del: " ++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n", ++ hashval, ++ sa_len ? sa : " (error)"); ++ return -ENOENT; ++ } ++ ++ if (ips == ipsec_sadb_hash[hashval]) { ++ ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext; ++ ips->ips_hnext = NULL; ++ atomic_dec(&ips->ips_refcount); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_del: " ++ "successfully deleted first ipsec_sa in chain.\n"); ++ return 0; ++ } else { ++ for (ipstp = ipsec_sadb_hash[hashval]; ++ ipstp; ++ ipstp = ipstp->ips_hnext) { ++ if (ipstp->ips_hnext == ips) { ++ ipstp->ips_hnext = ips->ips_hnext; ++ ips->ips_hnext = NULL; ++ atomic_dec(&ips->ips_refcount); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_del: " ++ "successfully deleted link in ipsec_sa chain.\n"); ++ return 0; ++ } ++ } ++ } ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_del: " ++ "no entries in linked list for hash=%d of SA:%s.\n", ++ hashval, ++ sa_len ? sa : " (error)"); ++ return -ENOENT; ++} ++ ++/* ++ The ipsec_sa table better be locked before it is handed in, or races ++ might happen ++*/ ++int ++ipsec_sa_delchain(struct ipsec_sa *ips) ++{ ++ struct ipsec_sa *ipsdel; ++ int error = 0; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ if(ips == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_error:ipsec_sa_delchain: " ++ "null pointer passed in!\n"); ++ return -ENODATA; ++ } ++ ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_delchain: " ++ "passed SA:%s\n", ++ sa_len ? sa : " (error)"); ++ while(ips->ips_onext != NULL) { ++ ips = ips->ips_onext; ++ } ++ ++ while(ips) { ++ /* XXX send a pfkey message up to advise of deleted ipsec_sa */ ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_delchain: " ++ "unlinking and delting SA:%s", ++ sa_len ? sa : " (error)"); ++ ipsdel = ips; ++ ips = ips->ips_inext; ++ if(ips != NULL) { ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", inext=%s", ++ sa_len ? sa : " (error)"); ++ atomic_dec(&ipsdel->ips_refcount); ++ ipsdel->ips_inext = NULL; ++ atomic_dec(&ips->ips_refcount); ++ ips->ips_onext = NULL; ++ } ++ KLIPS_PRINT(debug_xform, ++ ".\n"); ++ if((error = ipsec_sa_del(ipsdel))) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_delchain: " ++ "ipsec_sa_del returned error %d.\n", -error); ++ return error; ++ } ++ if((error = ipsec_sa_wipe(ipsdel))) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_delchain: " ++ "ipsec_sa_wipe returned error %d.\n", -error); ++ return error; ++ } ++ } ++ return error; ++} ++ ++int ++ipsec_sadb_cleanup(__u8 proto) ++{ ++ unsigned i; ++ int error = 0; ++ struct ipsec_sa *ips, **ipsprev, *ipsdel; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "cleaning up proto=%d.\n", ++ proto); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ for (i = 0; i < SADB_HASHMOD; i++) { ++ ipsprev = &(ipsec_sadb_hash[i]); ++ ips = ipsec_sadb_hash[i]; ++ if(ips != NULL) { ++ atomic_inc(&ips->ips_refcount); ++ } ++ for(; ips != NULL;) { ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "checking SA:%s, hash=%d, ref=%d", ++ sa_len ? sa : " (error)", ++ i, ++ ips->ips_ref); ++ ipsdel = ips; ++ ips = ipsdel->ips_hnext; ++ if(ips != NULL) { ++ atomic_inc(&ips->ips_refcount); ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", hnext=%s", ++ sa_len ? sa : " (error)"); ++ } ++ if(*ipsprev != NULL) { ++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", *ipsprev=%s", ++ sa_len ? sa : " (error)"); ++ if((*ipsprev)->ips_hnext) { ++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", *ipsprev->ips_hnext=%s", ++ sa_len ? sa : " (error)"); ++ } ++ } ++ KLIPS_PRINT(debug_xform, ++ ".\n"); ++ if(proto == 0 || (proto == ipsdel->ips_said.proto)) { ++ sa_len = satot(&ipsdel->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "deleting SA chain:%s.\n", ++ sa_len ? sa : " (error)"); ++ if((error = ipsec_sa_delchain(ipsdel))) { ++ SENDERR(-error); ++ } ++ ipsprev = &(ipsec_sadb_hash[i]); ++ ips = ipsec_sadb_hash[i]; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "deleted SA chain:%s", ++ sa_len ? sa : " (error)"); ++ if(ips != NULL) { ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", ipsec_sadb_hash[%d]=%s", ++ i, ++ sa_len ? sa : " (error)"); ++ } ++ if(*ipsprev != NULL) { ++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", *ipsprev=%s", ++ sa_len ? sa : " (error)"); ++ if((*ipsprev)->ips_hnext != NULL) { ++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ ", *ipsprev->ips_hnext=%s", ++ sa_len ? sa : " (error)"); ++ } ++ } ++ KLIPS_PRINT(debug_xform, ++ ".\n"); ++ } else { ++ ipsprev = &ipsdel; ++ } ++ if(ipsdel != NULL) { ++ ipsec_sa_put(ipsdel); ++ } ++ } ++ } ++ errlab: ++ ++ spin_unlock_bh(&tdb_lock); ++ ++ ++#if IPSEC_SA_REF_CODE ++ /* clean up SA reference table */ ++ ++ /* go through the ref table and clean out all the SAs */ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "removing SAref entries and tables."); ++ { ++ unsigned table, entry; ++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "cleaning SAref table=%u.\n", ++ table); ++ if(ipsec_sadb.refTable[table] == NULL) { ++ printk("\n"); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_cleanup: " ++ "cleaned %u used refTables.\n", ++ table); ++ break; ++ } ++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { ++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) { ++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]); ++ ipsec_sadb.refTable[table]->entry[entry] = NULL; ++ } ++ } ++ } ++ } ++#endif /* IPSEC_SA_REF_CODE */ ++ ++ return(error); ++} ++ ++int ++ipsec_sadb_free(void) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_free: " ++ "freeing SArefTable memory.\n"); ++ ++ /* clean up SA reference table */ ++ ++ /* go through the ref table and clean out all the SAs if any are ++ left and free table memory */ ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_free: " ++ "removing SAref entries and tables.\n"); ++ { ++ unsigned table, entry; ++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_free: " ++ "removing SAref table=%u.\n", ++ table); ++ if(ipsec_sadb.refTable[table] == NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sadb_free: " ++ "removed %u used refTables.\n", ++ table); ++ break; ++ } ++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) { ++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) { ++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]); ++ ipsec_sadb.refTable[table]->entry[entry] = NULL; ++ } ++ } ++ vfree(ipsec_sadb.refTable[table]); ++ ipsec_sadb.refTable[table] = NULL; ++ } ++ } ++ ++ return(error); ++} ++ ++int ++ipsec_sa_wipe(struct ipsec_sa *ips) ++{ ++ if(ips == NULL) { ++ return -ENODATA; ++ } ++ ++ /* if(atomic_dec_and_test(ips)) { ++ }; */ ++ ++#if IPSEC_SA_REF_CODE ++ /* remove me from the SArefTable */ ++ { ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa)); ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_wipe: " ++ "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n", ++ sa_len ? sa : " (error)", ++ ips, ++ ips->ips_ref, ++ IPsecSAref2table(IPsecSA2SAref(ips)), ++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))], ++ IPsecSAref2entry(IPsecSA2SAref(ips))); ++ } ++ if(ips->ips_ref == IPSEC_SAREF_NULL) { ++ KLIPS_PRINT(debug_xform, ++ "klips_debug:ipsec_sa_wipe: " ++ "why does this SA not have a valid SAref?.\n"); ++ } ++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))]->entry[IPsecSAref2entry(IPsecSA2SAref(ips))] = NULL; ++ ips->ips_ref = IPSEC_SAREF_NULL; ++ ipsec_sa_put(ips); ++#endif /* IPSEC_SA_REF_CODE */ ++ ++ /* paranoid clean up */ ++ if(ips->ips_addr_s != NULL) { ++ memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size); ++ kfree(ips->ips_addr_s); ++ } ++ ips->ips_addr_s = NULL; ++ ++ if(ips->ips_addr_d != NULL) { ++ memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size); ++ kfree(ips->ips_addr_d); ++ } ++ ips->ips_addr_d = NULL; ++ ++ if(ips->ips_addr_p != NULL) { ++ memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size); ++ kfree(ips->ips_addr_p); ++ } ++ ips->ips_addr_p = NULL; ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if(ips->ips_natt_oa) { ++ memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size); ++ kfree(ips->ips_natt_oa); ++ } ++ ips->ips_natt_oa = NULL; ++#endif ++ ++ if(ips->ips_key_a != NULL) { ++ memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size); ++ kfree(ips->ips_key_a); ++ } ++ ips->ips_key_a = NULL; ++ ++ if(ips->ips_key_e != NULL) { ++#ifdef CONFIG_IPSEC_ALG ++ if (ips->ips_alg_enc&&ips->ips_alg_enc->ixt_e_destroy_key) { ++ ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc, ++ ips->ips_key_e); ++ } else { ++#endif /* CONFIG_IPSEC_ALG */ ++ memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size); ++ kfree(ips->ips_key_e); ++#ifdef CONFIG_IPSEC_ALG ++ } ++#endif /* CONFIG_IPSEC_ALG */ ++ } ++ ips->ips_key_e = NULL; ++ ++ if(ips->ips_iv != NULL) { ++ memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size); ++ kfree(ips->ips_iv); ++ } ++ ips->ips_iv = NULL; ++ ++ if(ips->ips_ident_s.data != NULL) { ++ memset((caddr_t)(ips->ips_ident_s.data), ++ 0, ++ ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); ++ kfree(ips->ips_ident_s.data); ++ } ++ ips->ips_ident_s.data = NULL; ++ ++ if(ips->ips_ident_d.data != NULL) { ++ memset((caddr_t)(ips->ips_ident_d.data), ++ 0, ++ ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident)); ++ kfree(ips->ips_ident_d.data); ++ } ++ ips->ips_ident_d.data = NULL; ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (ips->ips_alg_enc||ips->ips_alg_auth) { ++ ipsec_alg_sa_wipe(ips); ++ } ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ memset((caddr_t)ips, 0, sizeof(*ips)); ++ kfree(ips); ++ ips = NULL; ++ ++ return 0; ++} ++ ++/* ++ * $Log: ipsec_sa.c,v $ ++ * Revision 1.23 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.22.2.1 2003/12/22 15:25:52 jjo ++ * . Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.22 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.21 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.20.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.20 2003/02/06 01:50:34 rgb ++ * Fixed initialisation bug for first sadb hash bucket that would only manifest itself on platforms where NULL != 0. ++ * ++ * Revision 1.19 2003/01/30 02:32:22 rgb ++ * ++ * Rename SAref table macro names for clarity. ++ * Transmit error code through to caller from callee for better diagnosis of problems. ++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. ++ * ++ * Revision 1.18 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.17 2002/10/07 18:31:43 rgb ++ * Move field width sanity checks to ipsec_sa.c ++ * ++ * Revision 1.16 2002/09/20 15:41:02 rgb ++ * Re-wrote most of the SAref code to eliminate Entry pointers. ++ * Added SAref code compiler directive switch. ++ * Added a saref test function for testing macros. ++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc(). ++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem ++ * of freeing newly created structures when clearing the reftable upon startup ++ * to start from a known state. ++ * Place all ipsec sadb globals into one struct. ++ * Rework saref freelist. ++ * Added memory allocation debugging. ++ * ++ * Revision 1.15 2002/09/20 05:01:44 rgb ++ * Update copyright date. ++ * ++ * Revision 1.14 2002/08/13 19:01:25 mcr ++ * patches from kenb to permit compilation of FreeSWAN on ia64. ++ * des library patched to use proper DES_LONG type for ia64. ++ * ++ * Revision 1.13 2002/07/29 03:06:20 mcr ++ * get rid of variable not used warnings. ++ * ++ * Revision 1.12 2002/07/26 08:48:31 rgb ++ * Added SA ref table code. ++ * ++ * Revision 1.11 2002/06/04 16:48:49 rgb ++ * Tidied up pointer code for processor independance. ++ * ++ * Revision 1.10 2002/05/23 07:16:17 rgb ++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount. ++ * Pointer clean-up. ++ * Added refcount code. ++ * Convert "usecount" to "refcount" to remove ambiguity. ++ * ++ * Revision 1.9 2002/05/14 02:34:49 rgb ++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion ++ * with "put" usage in the kernel. ++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, ++ * ipsec_sa or ipsec_sa. ++ * Added some preliminary refcount code. ++ * ++ * Revision 1.8 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.7 2002/04/24 07:36:30 mcr ++ * Moved from ./klips/net/ipsec/ipsec_sa.c,v ++ * ++ * Revision 1.6 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.5 2002/01/29 17:17:56 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.4 2002/01/29 04:00:52 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.3 2002/01/29 02:13:18 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.2 2001/11/26 09:16:15 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.1.2.2 2001/10/22 21:05:41 mcr ++ * removed phony prototype for des_set_key. ++ * ++ * Revision 1.1.2.1 2001/09/25 02:24:57 mcr ++ * struct tdb -> struct ipsec_sa. ++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c ++ * ipsec_xform.c removed. header file still contains useful things. ++ * ++ * ++ * ++ * CLONED from ipsec_xform.c: ++ * Revision 1.53 2001/09/08 21:13:34 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.52 2001/06/14 19:35:11 rgb ++ * Update copyright date. ++ * ++ * Revision 1.51 2001/05/30 08:14:03 rgb ++ * Removed vestiges of esp-null transforms. ++ * ++ * Revision 1.50 2001/05/03 19:43:18 rgb ++ * Initialise error return variable. ++ * Update SENDERR macro. ++ * Fix sign of error return code for ipsec_tdbcleanup(). ++ * Use more appropriate return code for ipsec_tdbwipe(). ++ * ++ * Revision 1.49 2001/04/19 18:56:17 rgb ++ * Fixed tdb table locking comments. ++ * ++ * Revision 1.48 2001/02/27 22:24:55 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.47 2000/11/06 04:32:08 rgb ++ * Ditched spin_lock_irqsave in favour of spin_lock_bh. ++ * ++ * Revision 1.46 2000/09/20 16:21:57 rgb ++ * Cleaned up ident string alloc/free. ++ * ++ * Revision 1.45 2000/09/08 19:16:51 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Removed all references to CONFIG_IPSEC_PFKEYv2. ++ * ++ * Revision 1.44 2000/08/30 05:29:04 rgb ++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c. ++ * ++ * Revision 1.43 2000/08/18 21:30:41 rgb ++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear. ++ * ++ * Revision 1.42 2000/08/01 14:51:51 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.41 2000/07/28 14:58:31 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.40 2000/06/28 05:50:11 rgb ++ * Actually set iv_bits. ++ * ++ * Revision 1.39 2000/05/10 23:11:09 rgb ++ * Added netlink debugging output. ++ * Added a cast to quiet down the ntohl bug. ++ * ++ * Revision 1.38 2000/05/10 19:18:42 rgb ++ * Cast output of ntohl so that the broken prototype doesn't make our ++ * compile noisy. ++ * ++ * Revision 1.37 2000/03/16 14:04:59 rgb ++ * Hardwired CONFIG_IPSEC_PFKEYv2 on. ++ * ++ * Revision 1.36 2000/01/26 10:11:28 rgb ++ * Fixed spacing in error text causing run-in words. ++ * ++ * Revision 1.35 2000/01/21 06:17:16 rgb ++ * Tidied up compiler directive indentation for readability. ++ * Added ictx,octx vars for simplification.(kravietz) ++ * Added macros for HMAC padding magic numbers.(kravietz) ++ * Fixed missing key length reporting bug. ++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in. ++ * ++ * Revision 1.34 1999/12/08 00:04:19 rgb ++ * Fixed SA direction overwriting bug for netlink users. ++ * ++ * Revision 1.33 1999/12/01 22:16:44 rgb ++ * Minor formatting changes in ESP MD5 initialisation. ++ * ++ * Revision 1.32 1999/11/25 09:06:36 rgb ++ * Fixed error return messages, should be returning negative numbers. ++ * Implemented SENDERR macro for propagating error codes. ++ * Added debug message and separate error code for algorithms not compiled ++ * in. ++ * ++ * Revision 1.31 1999/11/23 23:06:26 rgb ++ * Sort out pfkey and freeswan headers, putting them in a library path. ++ * ++ * Revision 1.30 1999/11/18 04:09:20 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.29 1999/11/17 15:53:40 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.28 1999/10/18 20:04:01 rgb ++ * Clean-out unused cruft. ++ * ++ * Revision 1.27 1999/10/03 19:01:03 rgb ++ * Spinlock support for 2.3.xx and 2.0.xx kernels. ++ * ++ * Revision 1.26 1999/10/01 16:22:24 rgb ++ * Switch from assignment init. to functional init. of spinlocks. ++ * ++ * Revision 1.25 1999/10/01 15:44:54 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.24 1999/10/01 00:03:46 rgb ++ * Added tdb structure locking. ++ * Minor formatting changes. ++ * Add function to initialize tdb hash table. ++ * ++ * Revision 1.23 1999/05/25 22:42:12 rgb ++ * Add deltdbchain() debugging. ++ * ++ * Revision 1.22 1999/05/25 21:24:31 rgb ++ * Add debugging statements to deltdbchain(). ++ * ++ * Revision 1.21 1999/05/25 03:51:48 rgb ++ * Refix error return code. ++ * ++ * Revision 1.20 1999/05/25 03:34:07 rgb ++ * Fix error return for flush. ++ * ++ * Revision 1.19 1999/05/09 03:25:37 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.18 1999/05/05 22:02:32 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.17 1999/04/29 15:20:16 rgb ++ * Change gettdb parameter to a pointer to reduce stack loading and ++ * facilitate parameter sanity checking. ++ * Add sanity checking for null pointer arguments. ++ * Add debugging instrumentation. ++ * Add function deltdbchain() which will take care of unlinking, ++ * zeroing and deleting a chain of tdbs. ++ * Add a parameter to tdbcleanup to be able to delete a class of SAs. ++ * tdbwipe now actually zeroes the tdb as well as any of its pointed ++ * structures. ++ * ++ * Revision 1.16 1999/04/16 15:36:29 rgb ++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing. ++ * ++ * Revision 1.15 1999/04/11 00:29:01 henry ++ * GPL boilerplate ++ * ++ * Revision 1.14 1999/04/06 04:54:28 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.13 1999/02/19 18:23:01 rgb ++ * Nix debug off compile warning. ++ * ++ * Revision 1.12 1999/02/17 16:52:16 rgb ++ * Consolidate satoa()s for space and speed efficiency. ++ * Convert DEBUG_IPSEC to KLIPS_PRINT ++ * Clean out unused cruft. ++ * Ditch NET_IPIP dependancy. ++ * Loop for 3des key setting. ++ * ++ * Revision 1.11 1999/01/26 02:09:05 rgb ++ * Remove ah/esp/IPIP switching on include files. ++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro. ++ * Removed dead code. ++ * Clean up debug code when switched off. ++ * Remove references to INET_GET_PROTOCOL. ++ * Added code exclusion macros to reduce code from unused algorithms. ++ * ++ * Revision 1.10 1999/01/22 06:28:55 rgb ++ * Cruft clean-out. ++ * Put random IV generation in kernel. ++ * Added algorithm switch code. ++ * Enhanced debugging. ++ * 64-bit clean-up. ++ * ++ * Revision 1.9 1998/11/30 13:22:55 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.8 1998/11/25 04:59:06 rgb ++ * Add conditionals for no IPIP tunnel code. ++ * Delete commented out code. ++ * ++ * Revision 1.7 1998/10/31 06:50:41 rgb ++ * Convert xform ASCII names to no spaces. ++ * Fixed up comments in #endif directives. ++ * ++ * Revision 1.6 1998/10/19 14:44:28 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.5 1998/10/09 04:32:19 rgb ++ * Added 'klips_debug' prefix to all klips printk debug statements. ++ * ++ * Revision 1.4 1998/08/12 00:11:31 rgb ++ * Added new xform functions to the xform table. ++ * Fixed minor debug output spelling error. ++ * ++ * Revision 1.3 1998/07/09 17:45:31 rgb ++ * Clarify algorithm not available message. ++ * ++ * Revision 1.2 1998/06/23 03:00:51 rgb ++ * Check for presence of IPIP protocol if it is setup one way (we don't ++ * know what has been set up the other way and can only assume it will be ++ * symmetrical with the exception of keys). ++ * ++ * Revision 1.1 1998/06/18 21:27:51 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.3 1998/06/11 05:54:59 rgb ++ * Added transform version string pointer to xformsw initialisations. ++ * ++ * Revision 1.2 1998/04/21 21:28:57 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.1 1998/04/09 03:06:13 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.5 1997/06/03 04:24:48 ji ++ * Added ESP-3DES-MD5-96 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * Added new transforms. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_sha1.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,219 @@ ++/* ++ * RCSID $Id: ipsec_sha1.c,v 1.9 2004/04/06 02:49:26 mcr Exp $ ++ */ ++ ++/* ++ * The rest of the code is derived from sha1.c by Steve Reid, which is ++ * public domain. ++ * Minor cosmetic changes to accomodate it in the Linux kernel by ji. ++ */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_sha1.h" ++ ++#if defined(rol) ++#undef rol ++#endif ++ ++#define SHA1HANDSOFF ++ ++#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) ++ ++/* blk0() and blk() perform the initial expand. */ ++/* I got the idea of expanding during the round function from SSLeay */ ++#ifdef __LITTLE_ENDIAN ++#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ ++ |(rol(block->l[i],8)&0x00FF00FF)) ++#else ++#define blk0(i) block->l[i] ++#endif ++#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ++ ^block->l[(i+2)&15]^block->l[i&15],1)) ++ ++/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ ++#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); ++#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); ++#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); ++#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); ++#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); ++ ++ ++/* Hash a single 512-bit block. This is the core of the algorithm. */ ++ ++void SHA1Transform(__u32 state[5], __u8 buffer[64]) ++{ ++__u32 a, b, c, d, e; ++typedef union { ++ unsigned char c[64]; ++ __u32 l[16]; ++} CHAR64LONG16; ++CHAR64LONG16* block; ++#ifdef SHA1HANDSOFF ++static unsigned char workspace[64]; ++ block = (CHAR64LONG16*)workspace; ++ memcpy(block, buffer, 64); ++#else ++ block = (CHAR64LONG16*)buffer; ++#endif ++ /* Copy context->state[] to working vars */ ++ a = state[0]; ++ b = state[1]; ++ c = state[2]; ++ d = state[3]; ++ e = state[4]; ++ /* 4 rounds of 20 operations each. Loop unrolled. */ ++ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); ++ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); ++ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); ++ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); ++ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); ++ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); ++ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); ++ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); ++ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); ++ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); ++ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); ++ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); ++ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); ++ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); ++ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); ++ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); ++ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); ++ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); ++ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); ++ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); ++ /* Add the working vars back into context.state[] */ ++ state[0] += a; ++ state[1] += b; ++ state[2] += c; ++ state[3] += d; ++ state[4] += e; ++ /* Wipe variables */ ++ a = b = c = d = e = 0; ++} ++ ++ ++/* SHA1Init - Initialize new context */ ++ ++void SHA1Init(void *vcontext) ++{ ++ SHA1_CTX* context = vcontext; ++ ++ /* SHA1 initialization constants */ ++ context->state[0] = 0x67452301; ++ context->state[1] = 0xEFCDAB89; ++ context->state[2] = 0x98BADCFE; ++ context->state[3] = 0x10325476; ++ context->state[4] = 0xC3D2E1F0; ++ context->count[0] = context->count[1] = 0; ++} ++ ++ ++/* Run your data through this. */ ++ ++void SHA1Update(void *vcontext, unsigned char* data, __u32 len) ++{ ++ SHA1_CTX* context = vcontext; ++ __u32 i, j; ++ ++ j = context->count[0]; ++ if ((context->count[0] += len << 3) < j) ++ context->count[1]++; ++ context->count[1] += (len>>29); ++ j = (j >> 3) & 63; ++ if ((j + len) > 63) { ++ memcpy(&context->buffer[j], data, (i = 64-j)); ++ SHA1Transform(context->state, context->buffer); ++ for ( ; i + 63 < len; i += 64) { ++ SHA1Transform(context->state, &data[i]); ++ } ++ j = 0; ++ } ++ else i = 0; ++ memcpy(&context->buffer[j], &data[i], len - i); ++} ++ ++ ++/* Add padding and return the message digest. */ ++ ++void SHA1Final(unsigned char digest[20], void *vcontext) ++{ ++ __u32 i, j; ++ unsigned char finalcount[8]; ++ SHA1_CTX* context = vcontext; ++ ++ for (i = 0; i < 8; i++) { ++ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] ++ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ ++ } ++ SHA1Update(context, (unsigned char *)"\200", 1); ++ while ((context->count[0] & 504) != 448) { ++ SHA1Update(context, (unsigned char *)"\0", 1); ++ } ++ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ ++ for (i = 0; i < 20; i++) { ++ digest[i] = (unsigned char) ++ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); ++ } ++ /* Wipe variables */ ++ i = j = 0; ++ memset(context->buffer, 0, 64); ++ memset(context->state, 0, 20); ++ memset(context->count, 0, 8); ++ memset(&finalcount, 0, 8); ++#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ ++ SHA1Transform(context->state, context->buffer); ++#endif ++} ++ ++ ++/* ++ * $Log: ipsec_sha1.c,v $ ++ * Revision 1.9 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.8 2002/09/10 01:45:14 mcr ++ * changed type of MD5_CTX and SHA1_CTX to void * so that ++ * the function prototypes would match, and could be placed ++ * into a pointer to a function. ++ * ++ * Revision 1.7 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.6 2002/04/24 07:36:30 mcr ++ * Moved from ./klips/net/ipsec/ipsec_sha1.c,v ++ * ++ * Revision 1.5 1999/12/13 13:59:13 rgb ++ * Quick fix to argument size to Update bugs. ++ * ++ * Revision 1.4 1999/04/11 00:29:00 henry ++ * GPL boilerplate ++ * ++ * Revision 1.3 1999/04/06 04:54:27 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.2 1999/01/22 06:55:50 rgb ++ * 64-bit clean-up. ++ * ++ * Revision 1.1 1998/06/18 21:27:50 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.2 1998/04/23 20:54:04 rgb ++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when ++ * verified. ++ * ++ * Revision 1.1 1998/04/09 03:06:11 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * New transform ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_tunnel.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,2645 @@ ++/* ++ * IPSEC Tunneling code. Heavily based on drivers/net/new_tunnel.c ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_tunnel_c_version[] = "RCSID $Id: ipsec_tunnel.c,v 1.220 2004/04/06 02:49:26 mcr Exp $"; ++ ++#define __NO_VERSION__ ++#include ++#include /* for CONFIG_IP_FORWARD */ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include /* struct tcphdr */ ++#include /* struct udphdr */ ++#include ++#include ++#ifdef NET_21 ++# include ++# include ++# define ip_chk_addr inet_addr_type ++# define IS_MYADDR RTN_LOCAL ++# include ++# undef dev_kfree_skb ++# define dev_kfree_skb(a,b) kfree_skb(a) ++# define PHYSDEV_TYPE ++#endif /* NET_21 */ ++#include ++#include /* icmp_send() */ ++#include ++#ifdef NETDEV_23 ++# include ++#endif /* NETDEV_23 */ ++ ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_eroute.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_sa.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_xmit.h" ++#include "openswan/ipsec_ipe4.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++#include ++#endif ++ ++static __u32 zeroes[64]; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_tunnel = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_open(struct device *dev) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ /* ++ * Can't open until attached. ++ */ ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_open: " ++ "dev = %s, prv->dev = %s\n", ++ dev->name, prv->dev?prv->dev->name:"NONE"); ++ ++ if (prv->dev == NULL) ++ return -ENODEV; ++ ++ MOD_INC_USE_COUNT; ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_close(struct device *dev) ++{ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++#ifdef NETDEV_23 ++static inline int ipsec_tunnel_xmit2(struct sk_buff *skb) ++{ ++#ifdef NETDEV_25 /* 2.6 kernels */ ++ return dst_output(skb); ++#else ++ return ip_send(skb); ++#endif ++} ++#endif /* NETDEV_23 */ ++ ++enum ipsec_xmit_value ++ipsec_tunnel_strip_hard_header(struct ipsec_xmit_state *ixs) ++{ ++ /* ixs->physdev->hard_header_len is unreliable and should not be used */ ++ ixs->hard_header_len = (unsigned char *)(ixs->iph) - ixs->skb->data; ++ ++ if(ixs->hard_header_len < 0) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_strip_hard_header: " ++ "Negative hard_header_len (%d)?!\n", ixs->hard_header_len); ++ ixs->stats->tx_dropped++; ++ return IPSEC_XMIT_BADHHLEN; ++ } ++ ++ /* while ixs->physdev->hard_header_len is unreliable and ++ * should not be trusted, it accurate and required for ATM, GRE and ++ * some other interfaces to work. Thanks to Willy Tarreau ++ * . ++ */ ++ if(ixs->hard_header_len == 0) { /* no hard header present */ ++ ixs->hard_header_stripped = 1; ++ ixs->hard_header_len = ixs->physdev->hard_header_len; ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_tunnel & DB_TN_XMIT) { ++ int i; ++ char c; ++ ++ printk(KERN_INFO "klips_debug:ipsec_xmit_strip_hard_header: " ++ ">>> skb->len=%ld hard_header_len:%d", ++ (unsigned long int)ixs->skb->len, ixs->hard_header_len); ++ c = ' '; ++ for (i=0; i < ixs->hard_header_len; i++) { ++ printk("%c%02x", c, ixs->skb->data[i]); ++ c = ':'; ++ } ++ printk(" \n"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph); ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_strip_hard_header: " ++ "Original head,tailroom: %d,%d\n", ++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); ++ ++ return IPSEC_XMIT_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_tunnel_SAlookup(struct ipsec_xmit_state *ixs) ++{ ++ /* ++ * First things first -- look us up in the erouting tables. ++ */ ++ ixs->matcher.sen_len = sizeof (struct sockaddr_encap); ++ ixs->matcher.sen_family = AF_ENCAP; ++ ixs->matcher.sen_type = SENT_IP4; ++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr; ++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr; ++ ixs->matcher.sen_proto = ixs->iph->protocol; ++ ipsec_extract_ports(ixs->iph, &ixs->matcher); ++ ++ /* ++ * The spinlock is to prevent any other process from accessing or deleting ++ * the eroute while we are using and updating it. ++ */ ++ spin_lock(&eroute_lock); ++ ++ ixs->eroute = ipsec_findroute(&ixs->matcher); ++ ++ if(ixs->iph->protocol == IPPROTO_UDP) { ++ if(ixs->skb->sk) { ++ ixs->sport=ntohs(ixs->skb->sk->sport); ++ ixs->dport=ntohs(ixs->skb->sk->dport); ++ } else if((ntohs(ixs->iph->frag_off) & IP_OFFSET) == 0 && ++ ((ixs->skb->len - ixs->hard_header_len) >= ++ ((ixs->iph->ihl << 2) + sizeof(struct udphdr)))) { ++ ixs->sport=ntohs(((struct udphdr*)((caddr_t)ixs->iph+(ixs->iph->ihl<<2)))->source); ++ ixs->dport=ntohs(((struct udphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl<<2)))->dest); ++ } else { ++ ixs->sport=0; ixs->dport=0; ++ } ++ } ++ ++ /* default to a %drop eroute */ ++ ixs->outgoing_said.proto = IPPROTO_INT; ++ ixs->outgoing_said.spi = htonl(SPI_DROP); ++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY; ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_SAlookup: " ++ "checking for local udp/500 IKE packet " ++ "saddr=%x, er=0p%p, daddr=%x, er_dst=%x, proto=%d sport=%d dport=%d\n", ++ ntohl((unsigned int)ixs->iph->saddr), ++ ixs->eroute, ++ ntohl((unsigned int)ixs->iph->daddr), ++ ixs->eroute ? ntohl((unsigned int)ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr) : 0, ++ ixs->iph->protocol, ++ ixs->sport, ++ ixs->dport); ++ ++ /* ++ * Quick cheat for now...are we udp/500? If so, let it through ++ * without interference since it is most likely an IKE packet. ++ */ ++ ++ if (ip_chk_addr((unsigned long)ixs->iph->saddr) == IS_MYADDR ++ && (!ixs->eroute ++ || ixs->iph->daddr == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr ++ || INADDR_ANY == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr) ++ ++ && ((ixs->sport == 500) || (ixs->sport == 4500))) { ++ /* Whatever the eroute, this is an IKE message ++ * from us (i.e. not being forwarded). ++ * Furthermore, if there is a tunnel eroute, ++ * the destination is the peer for this eroute. ++ * So %pass the packet: modify the default %drop. ++ */ ++ ixs->outgoing_said.spi = htonl(SPI_PASS); ++ if(!(ixs->skb->sk) && ((ntohs(ixs->iph->frag_off) & IP_MF) != 0)) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_SAlookup: " ++ "local UDP/500 (probably IKE) passthrough: base fragment, rest of fragments will probably get filtered.\n"); ++ } ++ } else if (ixs->eroute) { ++ ixs->eroute->er_count++; ++ ixs->eroute->er_lasttime = jiffies/HZ; ++ if(ixs->eroute->er_said.proto==IPPROTO_INT ++ && ixs->eroute->er_said.spi==htonl(SPI_HOLD)) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_SAlookup: " ++ "shunt SA of HOLD: skb stored in HOLD.\n"); ++ if(ixs->eroute->er_last != NULL) { ++ kfree_skb(ixs->eroute->er_last); ++ } ++ ixs->eroute->er_last = ixs->skb; ++ ixs->skb = NULL; ++ ixs->stats->tx_dropped++; ++ spin_unlock(&eroute_lock); ++ return IPSEC_XMIT_STOLEN; ++ } ++ ixs->outgoing_said = ixs->eroute->er_said; ++ ixs->eroute_pid = ixs->eroute->er_pid; ++ /* Copy of the ident for the TRAP/TRAPSUBNET eroutes */ ++ if(ixs->outgoing_said.proto==IPPROTO_INT ++ && (ixs->outgoing_said.spi==htonl(SPI_TRAP) ++ || (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)))) { ++ int len; ++ ++ ixs->ips.ips_ident_s.type = ixs->eroute->er_ident_s.type; ++ ixs->ips.ips_ident_s.id = ixs->eroute->er_ident_s.id; ++ ixs->ips.ips_ident_s.len = ixs->eroute->er_ident_s.len; ++ if (ixs->ips.ips_ident_s.len) { ++ len = ixs->ips.ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_SAlookup: " ++ "allocating %d bytes for ident_s shunt SA of HOLD: skb stored in HOLD.\n", ++ len); ++ if ((ixs->ips.ips_ident_s.data = kmalloc(len, GFP_ATOMIC)) == NULL) { ++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: " ++ "Failed, tried to allocate %d bytes for source ident.\n", ++ len); ++ ixs->stats->tx_dropped++; ++ spin_unlock(&eroute_lock); ++ return IPSEC_XMIT_ERRMEMALLOC; ++ } ++ memcpy(ixs->ips.ips_ident_s.data, ixs->eroute->er_ident_s.data, len); ++ } ++ ixs->ips.ips_ident_d.type = ixs->eroute->er_ident_d.type; ++ ixs->ips.ips_ident_d.id = ixs->eroute->er_ident_d.id; ++ ixs->ips.ips_ident_d.len = ixs->eroute->er_ident_d.len; ++ if (ixs->ips.ips_ident_d.len) { ++ len = ixs->ips.ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_SAlookup: " ++ "allocating %d bytes for ident_d shunt SA of HOLD: skb stored in HOLD.\n", ++ len); ++ if ((ixs->ips.ips_ident_d.data = kmalloc(len, GFP_ATOMIC)) == NULL) { ++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: " ++ "Failed, tried to allocate %d bytes for dest ident.\n", ++ len); ++ ixs->stats->tx_dropped++; ++ spin_unlock(&eroute_lock); ++ return IPSEC_XMIT_ERRMEMALLOC; ++ } ++ memcpy(ixs->ips.ips_ident_d.data, ixs->eroute->er_ident_d.data, len); ++ } ++ } ++ } ++ ++ spin_unlock(&eroute_lock); ++ return IPSEC_XMIT_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_tunnel_restore_hard_header(struct ipsec_xmit_state*ixs) ++{ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_restore_hard_header: " ++ "After recursive xforms -- head,tailroom: %d,%d\n", ++ skb_headroom(ixs->skb), ++ skb_tailroom(ixs->skb)); ++ ++ if(ixs->saved_header) { ++ if(skb_headroom(ixs->skb) < ixs->hard_header_len) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_xmit_restore_hard_header: " ++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n", ++ ixs->hard_header_len, ++ skb_headroom(ixs->skb)); ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_PUSHPULLERR; ++ ++ } ++ skb_push(ixs->skb, ixs->hard_header_len); ++ { ++ int i; ++ for (i = 0; i < ixs->hard_header_len; i++) { ++ ixs->skb->data[i] = ixs->saved_header[i]; ++ } ++ } ++ } ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (ixs->natt_type && ixs->natt_head) { ++ struct iphdr *ipp = ixs->skb->nh.iph; ++ struct udphdr *udp; ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "encapsuling packet into UDP (NAT-Traversal) (%d %d)\n", ++ ixs->natt_type, ixs->natt_head); ++ ++ ixs->iphlen = ipp->ihl << 2; ++ ipp->tot_len = ++ htons(ntohs(ipp->tot_len) + ixs->natt_head); ++ if(skb_tailroom(ixs->skb) < ixs->natt_head) { ++ printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: " ++ "tried to skb_put %d, %d available. " ++ "This should never happen, please report.\n", ++ ixs->natt_head, ++ skb_tailroom(ixs->skb)); ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESPUDP; ++ } ++ skb_put(ixs->skb, ixs->natt_head); ++ ++ udp = (struct udphdr *)((char *)ipp + ixs->iphlen); ++ ++ /* move ESP hdr after UDP hdr */ ++ memmove((void *)((char *)udp + ixs->natt_head), ++ (void *)(udp), ++ ntohs(ipp->tot_len) - ixs->iphlen - ixs->natt_head); ++ ++ /* clear UDP & Non-IKE Markers (if any) */ ++ memset(udp, 0, ixs->natt_head); ++ ++ /* fill UDP with usefull informations ;-) */ ++ udp->source = htons(ixs->natt_sport); ++ udp->dest = htons(ixs->natt_dport); ++ udp->len = htons(ntohs(ipp->tot_len) - ixs->iphlen); ++ ++ /* set protocol */ ++ ipp->protocol = IPPROTO_UDP; ++ ++ /* fix IP checksum */ ++ ipp->check = 0; ++ ipp->check = ip_fast_csum((unsigned char *)ipp, ipp->ihl); ++ } ++#endif ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_restore_hard_header: " ++ "With hard_header, final head,tailroom: %d,%d\n", ++ skb_headroom(ixs->skb), ++ skb_tailroom(ixs->skb)); ++ ++ return IPSEC_XMIT_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_tunnel_send(struct ipsec_xmit_state*ixs) ++{ ++#ifdef NETDEV_25 ++ struct flowi fl; ++#endif ++ ++#ifdef NET_21 /* 2.2 and 2.4 kernels */ ++ /* new route/dst cache code from James Morris */ ++ ixs->skb->dev = ixs->physdev; ++#ifdef NETDEV_25 ++ fl.oif = ixs->physdev->iflink; ++ fl.nl_u.ip4_u.daddr = ixs->skb->nh.iph->daddr; ++ fl.nl_u.ip4_u.saddr = ixs->pass ? 0 : ixs->skb->nh.iph->saddr; ++ fl.nl_u.ip4_u.tos = RT_TOS(ixs->skb->nh.iph->tos); ++ fl.proto = ixs->skb->nh.iph->protocol; ++ if ((ixs->error = ip_route_output_key(&ixs->route, &fl))) { ++#else ++ /*skb_orphan(ixs->skb);*/ ++ if((ixs->error = ip_route_output(&ixs->route, ++ ixs->skb->nh.iph->daddr, ++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr, ++ RT_TOS(ixs->skb->nh.iph->tos), ++ /* mcr->rgb: should this be 0 instead? */ ++ ixs->physdev->iflink))) { ++#endif ++ ixs->stats->tx_errors++; ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n", ++ ixs->error, ++ ixs->route->u.dst.dev->name); ++ return IPSEC_XMIT_ROUTEERR; ++ } ++ if(ixs->dev == ixs->route->u.dst.dev) { ++ ip_rt_put(ixs->route); ++ /* This is recursion, drop it. */ ++ ixs->stats->tx_errors++; ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n", ++ ixs->dev->name); ++ return IPSEC_XMIT_RECURSDETECT; ++ } ++ dst_release(ixs->skb->dst); ++ ixs->skb->dst = &ixs->route->u.dst; ++ ixs->stats->tx_bytes += ixs->skb->len; ++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) { ++ ixs->stats->tx_errors++; ++ printk(KERN_WARNING ++ "klips_error:ipsec_xmit_send: " ++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n", ++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data), ++ ixs->skb->len); ++ return IPSEC_XMIT_PUSHPULLERR; ++ } ++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data); ++#ifdef SKB_RESET_NFCT ++ if(!ixs->pass) { ++ nf_conntrack_put(ixs->skb->nfct); ++ ixs->skb->nfct = NULL; ++ } ++#ifdef CONFIG_NETFILTER_DEBUG ++ ixs->skb->nf_debug = 0; ++#endif /* CONFIG_NETFILTER_DEBUG */ ++#endif /* SKB_RESET_NFCT */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "...done, calling ip_send() on device:%s\n", ++ ixs->skb->dev ? ixs->skb->dev->name : "NULL"); ++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->skb->nh.iph); ++#ifdef NETDEV_23 /* 2.4 kernels */ ++ { ++ int err; ++ ++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev, ++ ipsec_tunnel_xmit2); ++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) { ++ if(net_ratelimit()) ++ printk(KERN_ERR ++ "klips_error:ipsec_xmit_send: " ++ "ip_send() failed, err=%d\n", ++ -err); ++ ixs->stats->tx_errors++; ++ ixs->stats->tx_aborted_errors++; ++ ixs->skb = NULL; ++ return IPSEC_XMIT_IPSENDFAILURE; ++ } ++ } ++#else /* NETDEV_23 */ /* 2.2 kernels */ ++ ip_send(ixs->skb); ++#endif /* NETDEV_23 */ ++#else /* NET_21 */ /* 2.0 kernels */ ++ ixs->skb->arp = 1; ++ /* ISDN/ASYNC PPP from Matjaz Godec. */ ++ /* skb->protocol = htons(ETH_P_IP); */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_send: " ++ "...done, calling dev_queue_xmit() or ip_fragment().\n"); ++ IP_SEND(ixs->skb, ixs->physdev); ++#endif /* NET_21 */ ++ ixs->stats->tx_packets++; ++ ++ ixs->skb = NULL; ++ ++ return IPSEC_XMIT_OK; ++} ++ ++void ++ipsec_tunnel_cleanup(struct ipsec_xmit_state*ixs) ++{ ++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) ++ netif_wake_queue(ixs->dev); ++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ ++ ixs->dev->tbusy = 0; ++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */ ++ if(ixs->saved_header) { ++ kfree(ixs->saved_header); ++ } ++ if(ixs->skb) { ++ dev_kfree_skb(ixs->skb, FREE_WRITE); ++ } ++ if(ixs->oskb) { ++ dev_kfree_skb(ixs->oskb, FREE_WRITE); ++ } ++ if (ixs->ips.ips_ident_s.data) { ++ kfree(ixs->ips.ips_ident_s.data); ++ } ++ if (ixs->ips.ips_ident_d.data) { ++ kfree(ixs->ips.ips_ident_d.data); ++ } ++} ++ ++/* ++ * This function assumes it is being called from dev_queue_xmit() ++ * and that skb is filled properly by that function. ++ */ ++int ++ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev) ++{ ++ struct ipsec_xmit_state ixs_mem; ++ struct ipsec_xmit_state *ixs = &ixs_mem; ++ enum ipsec_xmit_value stat; ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ ixs->natt_type = 0, ixs->natt_head = 0; ++ ixs->natt_sport = 0, ixs->natt_dport = 0; ++#endif ++ ++ memset((caddr_t)ixs, 0, sizeof(*ixs)); ++ ixs->oskb = NULL; ++ ixs->saved_header = NULL; /* saved copy of the hard header */ ++ ixs->route = NULL; ++ memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips)); ++ ixs->dev = dev; ++ ixs->skb = skb; ++ ++ stat = ipsec_xmit_sanity_check_dev(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ goto cleanup; ++ } ++ ++ stat = ipsec_xmit_sanity_check_skb(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ goto cleanup; ++ } ++ ++ stat = ipsec_tunnel_strip_hard_header(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ goto cleanup; ++ } ++ ++ stat = ipsec_tunnel_SAlookup(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n", ++ stat); ++ goto cleanup; ++ } ++ ++ ixs->innersrc = ixs->iph->saddr; ++ /* start encapsulation loop here XXX */ ++ do { ++ stat = ipsec_xmit_encap_bundle(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ if(stat == IPSEC_XMIT_PASS) { ++ goto bypass; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n", ++ stat); ++ goto cleanup; ++ } ++ ++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr; ++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr; ++ ixs->matcher.sen_proto = ixs->iph->protocol; ++ ipsec_extract_ports(ixs->iph, &ixs->matcher); ++ ++ spin_lock(&eroute_lock); ++ ixs->eroute = ipsec_findroute(&ixs->matcher); ++ if(ixs->eroute) { ++ ixs->outgoing_said = ixs->eroute->er_said; ++ ixs->eroute_pid = ixs->eroute->er_pid; ++ ixs->eroute->er_count++; ++ ixs->eroute->er_lasttime = jiffies/HZ; ++ } ++ spin_unlock(&eroute_lock); ++ ++ KLIPS_PRINT((debug_tunnel & DB_TN_XMIT) && ++ /* ((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc)) */ ++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) && ++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr && ++ ixs->eroute, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "We are recursing here.\n"); ++ ++ } while(/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/ ++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) && ++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr && ++ ixs->eroute); ++ ++ stat = ipsec_tunnel_restore_hard_header(ixs); ++ if(stat != IPSEC_XMIT_OK) { ++ goto cleanup; ++ } ++ ++ bypass: ++ stat = ipsec_tunnel_send(ixs); ++ ++ cleanup: ++ ipsec_tunnel_cleanup(ixs); ++ ++ return 0; ++} ++ ++DEBUG_NO_STATIC struct net_device_stats * ++ipsec_tunnel_get_stats(struct device *dev) ++{ ++ return &(((struct ipsecpriv *)(dev->priv))->mystats); ++} ++ ++/* ++ * Revectored calls. ++ * For each of these calls, a field exists in our private structure. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_hard_header(struct sk_buff *skb, struct device *dev, ++ unsigned short type, void *daddr, void *saddr, unsigned len) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ struct device *tmp; ++ int ret; ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(skb == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "no skb...\n"); ++ return -ENODATA; ++ } ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "no device...\n"); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "skb->dev=%s dev=%s.\n", ++ skb->dev ? skb->dev->name : "NULL", ++ dev->name); ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "no private space associated with dev=%s\n", ++ dev->name ? dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "no physical device associated with dev=%s\n", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ /* check if we have to send a IPv6 packet. It might be a Router ++ Solicitation, where the building of the packet happens in ++ reverse order: ++ 1. ll hdr, ++ 2. IPv6 hdr, ++ 3. ICMPv6 hdr ++ -> skb->nh.raw is still uninitialized when this function is ++ called!! If this is no IPv6 packet, we can print debugging ++ messages, otherwise we skip all debugging messages and just ++ build the ll header */ ++ if(type != ETH_P_IPV6) { ++ /* execute this only, if we don't have to build the ++ header for a IPv6 packet */ ++ if(!prv->hard_header) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ", ++ saddr, ++ daddr, ++ len, ++ type, ++ dev->name); ++#ifdef NET_21 ++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++#else /* NET_21 */ ++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->ip_hdr->saddr), ++ (__u32)ntohl(skb->ip_hdr->daddr) ); ++#endif /* NET_21 */ ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++#define da ((struct device *)(prv->dev))->dev_addr ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ", ++ saddr, ++ daddr, ++ len, ++ type, ++ dev->name, ++ prv->dev->name, ++ da[0], da[1], da[2], da[3], da[4], da[5]); ++#ifdef NET_21 ++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++#else /* NET_21 */ ++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->ip_hdr->saddr), ++ (__u32)ntohl(skb->ip_hdr->daddr) ); ++#endif /* NET_21 */ ++ } else { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_hard_header: " ++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n"); ++ } ++ tmp = skb->dev; ++ skb->dev = prv->dev; ++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len); ++ skb->dev = tmp; ++ return ret; ++} ++ ++DEBUG_NO_STATIC int ++#ifdef NET_21 ++ipsec_tunnel_rebuild_header(struct sk_buff *skb) ++#else /* NET_21 */ ++ipsec_tunnel_rebuild_header(void *buff, struct device *dev, ++ unsigned long raddr, struct sk_buff *skb) ++#endif /* NET_21 */ ++{ ++ struct ipsecpriv *prv = skb->dev->priv; ++ struct device *tmp; ++ int ret; ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(skb->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_rebuild_header: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_rebuild_header: " ++ "no private space associated with dev=%s", ++ skb->dev->name ? skb->dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_rebuild_header: " ++ "no physical device associated with dev=%s", ++ skb->dev->name ? skb->dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ if(!prv->rebuild_header) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_rebuild_header: " ++ "physical device has been detached, packet dropped skb->dev=%s->NULL ", ++ skb->dev->name); ++#ifdef NET_21 ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++#else /* NET_21 */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->ip_hdr->saddr), ++ (__u32)ntohl(skb->ip_hdr->daddr) ); ++#endif /* NET_21 */ ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel: " ++ "Revectored rebuild_header dev=%s->%s ", ++ skb->dev->name, prv->dev->name); ++#ifdef NET_21 ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->nh.iph->saddr), ++ (__u32)ntohl(skb->nh.iph->daddr) ); ++#else /* NET_21 */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "ip=%08x->%08x\n", ++ (__u32)ntohl(skb->ip_hdr->saddr), ++ (__u32)ntohl(skb->ip_hdr->daddr) ); ++#endif /* NET_21 */ ++ tmp = skb->dev; ++ skb->dev = prv->dev; ++ ++#ifdef NET_21 ++ ret = prv->rebuild_header(skb); ++#else /* NET_21 */ ++ ret = prv->rebuild_header(buff, prv->dev, raddr, skb); ++#endif /* NET_21 */ ++ skb->dev = tmp; ++ return ret; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_set_mac_address(struct device *dev, void *addr) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_set_mac_address: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_set_mac_address: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODEV; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_set_mac_address: " ++ "no physical device associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return -ENODEV; ++ } ++ ++ if(!prv->set_mac_address) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_set_mac_address: " ++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n", ++ dev->name); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_set_mac_address: " ++ "Revectored dev=%s->%s addr=0p%p\n", ++ dev->name, prv->dev->name, addr); ++ return prv->set_mac_address(prv->dev, addr); ++ ++} ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC void ++ipsec_tunnel_cache_bind(struct hh_cache **hhp, struct device *dev, ++ unsigned short htype, __u32 daddr) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_bind: " ++ "no device..."); ++ return; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_bind: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_bind: " ++ "no physical device associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return; ++ } ++ ++ if(!prv->header_cache_bind) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_bind: " ++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n", ++ dev->name); ++ stats->tx_dropped++; ++ return; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_bind: " ++ "Revectored \n"); ++ prv->header_cache_bind(hhp, prv->dev, htype, daddr); ++ return; ++} ++#endif /* !NET_21 */ ++ ++ ++DEBUG_NO_STATIC void ++ipsec_tunnel_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) ++{ ++ struct ipsecpriv *prv = dev->priv; ++ ++ struct net_device_stats *stats; /* This device's statistics */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_update: " ++ "no device..."); ++ return; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_update: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return; ++ } ++ ++ stats = (struct net_device_stats *) &(prv->mystats); ++ ++ if(prv->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_update: " ++ "no physical device associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ stats->tx_dropped++; ++ return; ++ } ++ ++ if(!prv->header_cache_update) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_cache_update: " ++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n", ++ dev->name); ++ return; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel: " ++ "Revectored cache_update\n"); ++ prv->header_cache_update(hh, prv->dev, haddr); ++ return; ++} ++ ++#ifdef NET_21 ++DEBUG_NO_STATIC int ++ipsec_tunnel_neigh_setup(struct neighbour *n) ++{ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_neigh_setup:\n"); ++ ++ if (n->nud_state == NUD_NONE) { ++ n->ops = &arp_broken_ops; ++ n->output = n->ops->output; ++ } ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_neigh_setup_dev(struct device *dev, struct neigh_parms *p) ++{ ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_neigh_setup_dev: " ++ "setting up %s\n", ++ dev ? dev->name : "NULL"); ++ ++ if (p->tbl->family == AF_INET) { ++ p->neigh_setup = ipsec_tunnel_neigh_setup; ++ p->ucast_probes = 0; ++ p->mcast_probes = 0; ++ } ++ return 0; ++} ++#endif /* NET_21 */ ++ ++/* ++ * We call the attach routine to attach another device. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_attach(struct device *dev, struct device *physdev) ++{ ++ int i; ++ struct ipsecpriv *prv = dev->priv; ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_attach: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_attach: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODATA; ++ } ++ ++ prv->dev = physdev; ++ prv->hard_start_xmit = physdev->hard_start_xmit; ++ prv->get_stats = physdev->get_stats; ++ ++ if (physdev->hard_header) { ++ prv->hard_header = physdev->hard_header; ++ dev->hard_header = ipsec_tunnel_hard_header; ++ } else ++ dev->hard_header = NULL; ++ ++ if (physdev->rebuild_header) { ++ prv->rebuild_header = physdev->rebuild_header; ++ dev->rebuild_header = ipsec_tunnel_rebuild_header; ++ } else ++ dev->rebuild_header = NULL; ++ ++ if (physdev->set_mac_address) { ++ prv->set_mac_address = physdev->set_mac_address; ++ dev->set_mac_address = ipsec_tunnel_set_mac_address; ++ } else ++ dev->set_mac_address = NULL; ++ ++#ifndef NET_21 ++ if (physdev->header_cache_bind) { ++ prv->header_cache_bind = physdev->header_cache_bind; ++ dev->header_cache_bind = ipsec_tunnel_cache_bind; ++ } else ++ dev->header_cache_bind = NULL; ++#endif /* !NET_21 */ ++ ++ if (physdev->header_cache_update) { ++ prv->header_cache_update = physdev->header_cache_update; ++ dev->header_cache_update = ipsec_tunnel_cache_update; ++ } else ++ dev->header_cache_update = NULL; ++ ++ dev->hard_header_len = physdev->hard_header_len; ++ ++#ifdef NET_21 ++/* prv->neigh_setup = physdev->neigh_setup; */ ++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev; ++#endif /* NET_21 */ ++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */ ++ prv->mtu = physdev->mtu; ++ ++#ifdef PHYSDEV_TYPE ++ dev->type = physdev->type; /* ARPHRD_TUNNEL; */ ++#endif /* PHYSDEV_TYPE */ ++ ++ dev->addr_len = physdev->addr_len; ++ for (i=0; iaddr_len; i++) { ++ dev->dev_addr[i] = physdev->dev_addr[i]; ++ } ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_tunnel & DB_TN_INIT) { ++ printk(KERN_INFO "klips_debug:ipsec_tunnel_attach: " ++ "physical device %s being attached has HW address: %2x", ++ physdev->name, physdev->dev_addr[0]); ++ for (i=1; i < physdev->addr_len; i++) { ++ printk(":%02x", physdev->dev_addr[i]); ++ } ++ printk("\n"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ return 0; ++} ++ ++/* ++ * We call the detach routine to detach the ipsec tunnel from another device. ++ */ ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_detach(struct device *dev) ++{ ++ int i; ++ struct ipsecpriv *prv = dev->priv; ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_detach: " ++ "no device..."); ++ return -ENODEV; ++ } ++ ++ if(prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC, ++ "klips_debug:ipsec_tunnel_detach: " ++ "no private space associated with dev=%s", ++ dev->name ? dev->name : "NULL"); ++ return -ENODATA; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_detach: " ++ "physical device %s being detached from virtual device %s\n", ++ prv->dev ? prv->dev->name : "NULL", ++ dev->name); ++ ++ ipsec_dev_put(prv->dev); ++ prv->dev = NULL; ++ prv->hard_start_xmit = NULL; ++ prv->get_stats = NULL; ++ ++ prv->hard_header = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->hard_header = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ prv->rebuild_header = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->rebuild_header = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++ prv->set_mac_address = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->set_mac_address = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++#ifndef NET_21 ++ prv->header_cache_bind = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->header_cache_bind = NULL; ++#endif /* DETACH_AND_DOWN */ ++#endif /* !NET_21 */ ++ ++ prv->header_cache_update = NULL; ++#ifdef DETACH_AND_DOWN ++ dev->header_cache_update = NULL; ++#endif /* DETACH_AND_DOWN */ ++ ++#ifdef NET_21 ++/* prv->neigh_setup = NULL; */ ++#ifdef DETACH_AND_DOWN ++ dev->neigh_setup = NULL; ++#endif /* DETACH_AND_DOWN */ ++#endif /* NET_21 */ ++ dev->hard_header_len = 0; ++#ifdef DETACH_AND_DOWN ++ dev->mtu = 0; ++#endif /* DETACH_AND_DOWN */ ++ prv->mtu = 0; ++ for (i=0; idev_addr[i] = 0; ++ } ++ dev->addr_len = 0; ++#ifdef PHYSDEV_TYPE ++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ ++#endif /* PHYSDEV_TYPE */ ++ ++ return 0; ++} ++ ++/* ++ * We call the clear routine to detach all ipsec tunnels from other devices. ++ */ ++DEBUG_NO_STATIC int ++ipsec_tunnel_clear(void) ++{ ++ int i; ++ struct device *ipsecdev = NULL, *prvdev; ++ struct ipsecpriv *prv; ++ char name[9]; ++ int ret; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_clear: .\n"); ++ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ ipsecdev = ipsecdevices[i]; ++ if(ipsecdev != NULL) { ++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) { ++ prvdev = (struct device *)(prv->dev); ++ if(prvdev) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_clear: " ++ "physical device for device %s is %s\n", ++ name, prvdev->name); ++ if((ret = ipsec_tunnel_detach(ipsecdev))) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_clear: " ++ "error %d detatching device %s from device %s.\n", ++ ret, name, prvdev->name); ++ return ret; ++ } ++ } ++ } ++ } ++ } ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++ipsec_tunnel_ioctl(struct device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct ipsectunnelconf *cf = (struct ipsectunnelconf *)&ifr->ifr_data; ++ struct ipsecpriv *prv = dev->priv; ++ struct device *them; /* physical device */ ++#ifdef CONFIG_IP_ALIAS ++ char *colon; ++ char realphysname[IFNAMSIZ]; ++#endif /* CONFIG_IP_ALIAS */ ++ ++ if(dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "device not supplied.\n"); ++ return -ENODEV; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "tncfg service call #%d for dev=%s\n", ++ cmd, ++ dev->name ? dev->name : "NULL"); ++ switch (cmd) { ++ /* attach a virtual ipsec? device to a physical device */ ++ case IPSEC_SET_DEV: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "calling ipsec_tunnel_attatch...\n"); ++#ifdef CONFIG_IP_ALIAS ++ /* If this is an IP alias interface, get its real physical name */ ++ strncpy(realphysname, cf->cf_name, IFNAMSIZ); ++ realphysname[IFNAMSIZ-1] = 0; ++ colon = strchr(realphysname, ':'); ++ if (colon) *colon = 0; ++ them = ipsec_dev_get(realphysname); ++#else /* CONFIG_IP_ALIAS */ ++ them = ipsec_dev_get(cf->cf_name); ++#endif /* CONFIG_IP_ALIAS */ ++ ++ if (them == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "physical device %s requested is null\n", ++ cf->cf_name); ++ ipsec_dev_put(them); ++ return -ENXIO; ++ } ++ ++#if 0 ++ if (them->flags & IFF_UP) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "physical device %s requested is not up.\n", ++ cf->cf_name); ++ ipsec_dev_put(them); ++ return -ENXIO; ++ } ++#endif ++ ++ if (prv && prv->dev) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "virtual device is already connected to %s.\n", ++ prv->dev->name ? prv->dev->name : "NULL"); ++ ipsec_dev_put(them); ++ return -EBUSY; ++ } ++ return ipsec_tunnel_attach(dev, them); ++ ++ case IPSEC_DEL_DEV: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "calling ipsec_tunnel_detatch.\n"); ++ if (! prv->dev) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "physical device not connected.\n"); ++ return -ENODEV; ++ } ++ return ipsec_tunnel_detach(dev); ++ ++ case IPSEC_CLR_DEV: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "calling ipsec_tunnel_clear.\n"); ++ return ipsec_tunnel_clear(); ++ ++ default: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_ioctl: " ++ "unknown command %d.\n", ++ cmd); ++ return -EOPNOTSUPP; ++ } ++} ++ ++int ++ipsec_device_event(struct notifier_block *unused, unsigned long event, void *ptr) ++{ ++ struct device *dev = ptr; ++ struct device *ipsec_dev; ++ struct ipsecpriv *priv; ++ int i; ++ ++ if (dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "dev=NULL for event type %ld.\n", ++ event); ++ return(NOTIFY_DONE); ++ } ++ ++ /* check for loopback devices */ ++ if (dev && (dev->flags & IFF_LOOPBACK)) { ++ return(NOTIFY_DONE); ++ } ++ ++ switch (event) { ++ case NETDEV_DOWN: ++ /* look very carefully at the scope of these compiler ++ directives before changing anything... -- RGB */ ++#ifdef NET_21 ++ case NETDEV_UNREGISTER: ++ switch (event) { ++ case NETDEV_DOWN: ++#endif /* NET_21 */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_DOWN dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) { ++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n", ++ dev->name); ++ } ++#ifdef NET_21 ++ break; ++ case NETDEV_UNREGISTER: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_UNREGISTER dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ break; ++ } ++#endif /* NET_21 */ ++ ++ /* find the attached physical device and detach it. */ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ ipsec_dev = ipsecdevices[i]; ++ ++ if(ipsec_dev) { ++ priv = (struct ipsecpriv *)(ipsec_dev->priv); ++ if(priv) { ++ ; ++ if(((struct device *)(priv->dev)) == dev) { ++ /* dev_close(ipsec_dev); */ ++ /* return */ ipsec_tunnel_detach(ipsec_dev); ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "device '%s' has been detached.\n", ++ ipsec_dev->name); ++ break; ++ } ++ } else { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "device '%s' has no private data space!\n", ++ ipsec_dev->name); ++ } ++ } ++ } ++ break; ++ case NETDEV_UP: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_UP dev=%s\n", ++ dev->name); ++ break; ++#ifdef NET_21 ++ case NETDEV_REBOOT: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_REBOOT dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGE: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_CHANGE dev=%s flags=%x\n", ++ dev->name, ++ dev->flags); ++ break; ++ case NETDEV_REGISTER: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_REGISTER dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGEMTU: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n", ++ dev->name, ++ dev->mtu); ++ break; ++ case NETDEV_CHANGEADDR: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_CHANGEADDR dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_GOING_DOWN: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_GOING_DOWN dev=%s\n", ++ dev->name); ++ break; ++ case NETDEV_CHANGENAME: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "NETDEV_CHANGENAME dev=%s\n", ++ dev->name); ++ break; ++#endif /* NET_21 */ ++ default: ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_device_event: " ++ "event type %ld unrecognised for dev=%s\n", ++ event, ++ dev->name); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++/* ++ * Called when an ipsec tunnel device is initialized. ++ * The ipsec tunnel device structure is passed to us. ++ */ ++ ++int ++ipsec_tunnel_init(struct device *dev) ++{ ++ int i; ++ ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_init: " ++ "allocating %lu bytes initialising device: %s\n", ++ (unsigned long) sizeof(struct ipsecpriv), ++ dev->name ? dev->name : "NULL"); ++ ++ /* Add our tunnel functions to the device */ ++ dev->open = ipsec_tunnel_open; ++ dev->stop = ipsec_tunnel_close; ++ dev->hard_start_xmit = ipsec_tunnel_start_xmit; ++ dev->get_stats = ipsec_tunnel_get_stats; ++ ++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL); ++ if (dev->priv == NULL) ++ return -ENOMEM; ++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv)); ++ ++ for(i = 0; i < sizeof(zeroes); i++) { ++ ((__u8*)(zeroes))[i] = 0; ++ } ++ ++#ifndef NET_21 ++ /* Initialize the tunnel device structure */ ++ for (i = 0; i < DEV_NUMBUFFS; i++) ++ skb_queue_head_init(&dev->buffs[i]); ++#endif /* !NET_21 */ ++ ++ dev->set_multicast_list = NULL; ++ dev->do_ioctl = ipsec_tunnel_ioctl; ++ dev->hard_header = NULL; ++ dev->rebuild_header = NULL; ++ dev->set_mac_address = NULL; ++#ifndef NET_21 ++ dev->header_cache_bind = NULL; ++#endif /* !NET_21 */ ++ dev->header_cache_update= NULL; ++ ++#ifdef NET_21 ++/* prv->neigh_setup = NULL; */ ++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev; ++#endif /* NET_21 */ ++ dev->hard_header_len = 0; ++ dev->mtu = 0; ++ dev->addr_len = 0; ++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */ ++ dev->tx_queue_len = 10; /* Small queue */ ++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */ ++ ++ /* New-style flags. */ ++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */; ++#ifdef NET_21 ++ dev_init_buffers(dev); ++#else /* NET_21 */ ++ dev->family = AF_INET; ++ dev->pa_addr = 0; ++ dev->pa_brdaddr = 0; ++ dev->pa_mask = 0; ++ dev->pa_alen = 4; ++#endif /* NET_21 */ ++ ++ /* We're done. Have I forgotten anything? */ ++ return 0; ++} ++ ++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ++/* Module specific interface (but it links with the rest of IPSEC) */ ++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ++ ++int ++ipsec_tunnel_probe(struct device *dev) ++{ ++ ipsec_tunnel_init(dev); ++ return 0; ++} ++ ++struct device *ipsecdevices[IPSEC_NUM_IF]; ++ ++int ++ipsec_tunnel_init_devices(void) ++{ ++ int i; ++ char name[IFNAMSIZ]; ++ struct device *dev_ipsec; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n", ++ IPSEC_NUM_IF, ++ (unsigned long) (sizeof(struct device) + IFNAMSIZ), ++ IFNAMSIZ); ++ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ sprintf(name, IPSEC_DEV_FORMAT, i); ++ dev_ipsec = (struct device*)kmalloc(sizeof(struct device), GFP_KERNEL); ++ if (dev_ipsec == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "failed to allocate memory for device %s, quitting device init.\n", ++ name); ++ return -ENOMEM; ++ } ++ memset((caddr_t)dev_ipsec, 0, sizeof(struct device)); ++#ifdef NETDEV_23 ++ strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name)); ++#else /* NETDEV_23 */ ++ dev_ipsec->name = (char*)kmalloc(IFNAMSIZ, GFP_KERNEL); ++ if (dev_ipsec->name == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "failed to allocate memory for device %s name, quitting device init.\n", ++ name); ++ return -ENOMEM; ++ } ++ memset((caddr_t)dev_ipsec->name, 0, IFNAMSIZ); ++ strncpy(dev_ipsec->name, name, IFNAMSIZ); ++#endif /* NETDEV_23 */ ++ dev_ipsec->next = NULL; ++ dev_ipsec->init = &ipsec_tunnel_probe; ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "registering device %s\n", ++ dev_ipsec->name); ++ ++ /* reference and hold the device reference */ ++ dev_hold(dev_ipsec); ++ ipsecdevices[i]=dev_ipsec; ++ ++ if (register_netdev(dev_ipsec) != 0) { ++ KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "registering device %s failed, quitting device init.\n", ++ dev_ipsec->name); ++ return -EIO; ++ } else { ++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT, ++ "klips_debug:ipsec_tunnel_init_devices: " ++ "registering device %s succeeded, continuing...\n", ++ dev_ipsec->name); ++ } ++ } ++ return 0; ++} ++ ++/* void */ ++int ++ipsec_tunnel_cleanup_devices(void) ++{ ++ int error = 0; ++ int i; ++ char name[32]; ++ struct device *dev_ipsec; ++ ++ for(i = 0; i < IPSEC_NUM_IF; i++) { ++ dev_ipsec = ipsecdevices[i]; ++ if(dev_ipsec == NULL) { ++ continue; ++ } ++ ++ /* release reference */ ++ ipsecdevices[i]=NULL; ++ ipsec_dev_put(dev_ipsec); ++ ++ KLIPS_PRINT(debug_tunnel, "Unregistering %s (refcnt=%d)\n", ++ name, ++ atomic_read(&dev_ipsec->refcnt)); ++ unregister_netdev(dev_ipsec); ++ KLIPS_PRINT(debug_tunnel, "Unregisted %s\n", name); ++#ifndef NETDEV_23 ++ kfree(dev_ipsec->name); ++ dev_ipsec->name=NULL; ++#endif /* !NETDEV_23 */ ++ kfree(dev_ipsec->priv); ++ dev_ipsec->priv=NULL; ++ } ++ return error; ++} ++ ++/* ++ * $Log: ipsec_tunnel.c,v $ ++ * Revision 1.220 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.219 2004/02/03 03:13:17 mcr ++ * minor edits for readability, and error reporting. ++ * ++ * Revision 1.218 2004/01/27 20:29:20 mcr ++ * fix for unregister_netdev() problem for underlying eth0. ++ * ++ * Revision 1.217 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.216 2003/12/04 23:01:17 mcr ++ * removed ipsec_netlink.h ++ * ++ * Revision 1.215 2003/12/04 16:35:16 ken ++ * Fix for ATM devices where physdev->hard_header_len *is* correct ++ * ++ * Revision 1.214 2003/11/25 23:52:37 mcr ++ * fix typo in patch - ixs-> needed. ++ * ++ * Revision 1.213 2003/11/24 18:25:49 mcr ++ * patch from willy@w.ods.org to fix problems with ATM interfaces. ++ * ++ * Revision 1.212 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.211.2.2 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.211.2.1 2003/09/21 13:59:56 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.211 2003/09/10 16:46:30 mcr ++ * patches for 2.4 backport/2.6 existence. ++ * ++ * Revision 1.210 2003/07/31 22:47:16 mcr ++ * preliminary (untested by FS-team) 2.5 patches. ++ * ++ * Revision 1.209 2003/06/22 21:28:43 mcr ++ * inability to unload module was caused by calls to dev_get ++ * (ipsec_dev_get), to gather a device from a name. There is ++ * simply no reason to look the devices up - they should be kept ++ * in a nice array, ready for use. ++ * ++ * Revision 1.208 2003/06/22 21:25:07 mcr ++ * all staticly counted ipsecXXX device support removed. ++ * ++ * Revision 1.207 2003/04/02 20:15:37 mcr ++ * fix for PR#204 - do not clear connection tracking info if we ++ * the packet is being sent in the clear. ++ * ++ * Revision 1.206 2003/02/12 19:32:51 rgb ++ * Refactored file to: ++ * ipsec_xmit.c ++ * ipsec_xmit.h ++ * ipsec_mast.c ++ * ++ * Revision 1.205 2003/02/06 17:47:00 rgb ++ * ++ * Remove unused ipsec_tunnel_lock() and ipsec_tunnel_unlock() code. ++ * Refactor ipsec_tunnel_start_xmit() further into: ++ * ipsec_xmit_sanity_check_dev() ++ * ipsec_xmit_sanity_check_skb() ++ * ipsec_xmit_strip_hard_header() ++ * ipsec_xmit_restore_hard_header() ++ * ipsec_xmit_send() ++ * ipsec_xmit_cleanup() ++ * and start a skeletal ipsec_mast_start_xmit() . ++ * ++ * Revision 1.204 2003/02/06 06:43:46 rgb ++ * ++ * Refactor ipsec_tunnel_start_xmit, bringing out: ++ * ipsec_xmit_SAlookup ++ * ipsec_xmit_encap_once ++ * ipsec_xmit_encap_bundle ++ * ++ * Revision 1.203 2003/02/06 02:21:34 rgb ++ * ++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . ++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". ++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. ++ * ++ * Revision 1.202 2003/01/03 07:38:01 rgb ++ * ++ * Start to refactor ipsec_tunnel_start_xmit() by putting local variables ++ * into struct ipsec_xmit_state and renaming a few variables to give more ++ * unique or searchable names. ++ * ++ * Revision 1.201 2003/01/03 00:31:28 rgb ++ * ++ * Clean up memset usage, including fixing 2 places where keys were not ++ * properly wiped. ++ * ++ * Revision 1.200 2002/12/06 02:24:02 mcr ++ * patches for compiling against SUSE 8.1 kernels. Requires ++ * an additional -DSUSE_LINUX_2_4_19_IS_STUPID. ++ * ++ * Revision 1.199 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.198 2002/10/05 05:02:58 dhr ++ * ++ * C labels go on statements ++ * ++ * Revision 1.197 2002/09/20 05:01:50 rgb ++ * Added compiler directive to switch on IP options and fix IP options bug. ++ * Make ip->ihl treatment consistent using shifts rather than multiplications. ++ * Check for large enough packet before accessing udp header for IKE bypass. ++ * Added memory allocation debugging. ++ * Fixed potential memory allocation failure-induced oops. ++ * ++ * Revision 1.196 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.195 2002/07/23 03:36:07 rgb ++ * Fixed 2.2 device initialisation hang. ++ * ++ * Revision 1.194 2002/05/27 21:40:34 rgb ++ * Set unused ipsec devices to ARPHRD_VOID to avoid confusing iproute2. ++ * Cleaned up intermediate step to dynamic device allocation. ++ * ++ * Revision 1.193 2002/05/27 19:31:36 rgb ++ * Convert to dynamic ipsec device allocation. ++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. ++ * ++ * Revision 1.192 2002/05/23 07:14:28 rgb ++ * Added refcount code. ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.191 2002/05/14 02:34:37 rgb ++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, ++ * ipsec_sa or ipsec_sa. ++ * ++ * Revision 1.190 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.189 2002/04/24 07:36:32 mcr ++ * Moved from ./klips/net/ipsec/ipsec_tunnel.c,v ++ * ++ * Revision 1.188 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.187 2002/03/23 19:55:17 rgb ++ * Fix for 2.2 local IKE fragmentation blackhole. Still won't work if ++ * iptraf or another pcap app is running. ++ * ++ * Revision 1.186 2002/03/19 03:26:22 rgb ++ * Applied DHR's tunnel patch to streamline IKE/specialSA processing. ++ * ++ * Revision 1.185 2002/02/20 04:13:05 rgb ++ * Send back ICMP_PKT_FILTERED upon %reject. ++ * ++ * Revision 1.184 2002/01/29 17:17:56 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.183 2002/01/29 04:00:53 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.182 2002/01/29 02:13:18 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.181 2002/01/07 20:00:33 rgb ++ * Added IKE destination port debugging. ++ * ++ * Revision 1.180 2001/12/21 21:49:54 rgb ++ * Fixed bug as a result of moving IKE bypass above %trap/%hold code. ++ * ++ * Revision 1.179 2001/12/19 21:08:14 rgb ++ * Added transport protocol ports to ipsec_print_ip(). ++ * Update eroute info for non-SA targets. ++ * Added obey DF code disabled. ++ * Fixed formatting bugs in ipsec_tunnel_hard_header(). ++ * ++ * Revision 1.178 2001/12/05 09:36:10 rgb ++ * Moved the UDP/500 IKE check just above the %hold/%trap checks to avoid ++ * IKE packets being stolen by the %hold (and returned to the sending KMd ++ * in an ACQUIRE, ironically ;-). ++ * ++ * Revision 1.177 2001/11/26 09:23:50 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.170.2.1 2001/09/25 02:28:27 mcr ++ * struct tdb -> struct ipsec_sa. ++ * lifetime checks moved to common routines. ++ * cleaned up includes. ++ * ++ * Revision 1.170.2.2 2001/10/22 21:08:01 mcr ++ * include des.h, removed phony prototypes and fixed calling ++ * conventions to match real prototypes. ++ * ++ * Revision 1.176 2001/11/09 18:32:31 rgb ++ * Added Hans Schultz' fragmented UDP/500 IKE socket port selector. ++ * ++ * Revision 1.175 2001/11/06 20:47:00 rgb ++ * Added Eric Espie's TRAPSUBNET fix, minus spin-lock-bh dabbling. ++ * ++ * Revision 1.174 2001/11/06 19:50:43 rgb ++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for ++ * use also by pfkey_v2_parser.c ++ * ++ * Revision 1.173 2001/10/29 21:53:44 henry ++ * tone down the device-down message slightly, until we can make it smarter ++ * ++ * Revision 1.172 2001/10/26 04:59:37 rgb ++ * Added a critical level syslog message if an ipsec device goes down. ++ * ++ * Revision 1.171 2001/10/18 04:45:21 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.170 2001/09/25 00:09:50 rgb ++ * Added NetCelo's TRAPSUBNET code to convert a new type TRAPSUBNET into a ++ * HOLD. ++ * ++ * Revision 1.169 2001/09/15 16:24:05 rgb ++ * Re-inject first and last HOLD packet when an eroute REPLACE is done. ++ * ++ * Revision 1.168 2001/09/14 16:58:37 rgb ++ * Added support for storing the first and last packets through a HOLD. ++ * ++ * Revision 1.167 2001/09/08 21:13:33 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.166 2001/08/27 19:47:59 rgb ++ * Clear tdb before usage. ++ * Added comment: clear IF before calling routing? ++ * ++ * Revision 1.165 2001/07/03 01:23:53 rgb ++ * Send back ICMP iff DF set, !ICMP, offset==0, sysctl_icmp, iph->tot_len > ++ * emtu, and don't drop. ++ * ++ * Revision 1.164 2001/06/14 19:35:10 rgb ++ * Update copyright date. ++ * ++ * Revision 1.163 2001/06/06 20:28:51 rgb ++ * Added sanity checks for NULL skbs and devices. ++ * Added more debugging output to various functions. ++ * Removed redundant dev->priv argument to ipsec_tunnel_{at,de}tach(). ++ * Renamed ipsec_tunnel_attach() virtual and physical device arguments. ++ * Corrected neigh_setup() device function assignment. ++ * Keep valid pointers to ipsec_tunnel_*() on detach. ++ * Set dev->type to the originally-initiallised value. ++ * ++ * Revision 1.162 2001/06/01 07:28:04 rgb ++ * Added sanity checks for detached devices. Don't down virtual devices ++ * to prevent packets going out in the clear if the detached device comes ++ * back up. ++ * ++ * Revision 1.161 2001/05/30 08:14:52 rgb ++ * Removed vestiges of esp-null transforms. ++ * NetDev Notifier instrumentation to track down disappearing devices. ++ * ++ * Revision 1.160 2001/05/29 05:15:12 rgb ++ * Added SS' PMTU patch which notifies sender if packet doesn't fit ++ * physical MTU (if it wasn't ICMP) and then drops it. ++ * ++ * Revision 1.159 2001/05/27 06:12:12 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.158 2001/05/24 05:39:33 rgb ++ * Applied source zeroing to 2.2 ip_route_output() call as well to enable ++ * PASS eroutes for opportunism. ++ * ++ * Revision 1.157 2001/05/23 22:35:28 rgb ++ * 2.4 source override simplification. ++ * ++ * Revision 1.156 2001/05/23 21:41:31 rgb ++ * Added error return code printing on ip_route_output(). ++ * ++ * Revision 1.155 2001/05/23 05:09:13 rgb ++ * Fixed incorrect ip_route_output() failure message. ++ * ++ * Revision 1.154 2001/05/21 14:53:31 rgb ++ * Added debug statement for case when ip_route_output() fails, causing ++ * packet to be dropped, but log looked ok. ++ * ++ * Revision 1.153 2001/05/19 02:37:54 rgb ++ * Fixed missing comment termination. ++ * ++ * Revision 1.152 2001/05/19 02:35:50 rgb ++ * Debug code optimisation for non-debug speed. ++ * Kernel version compiler define comments. ++ * 2.2 and 2.4 kernel ip_send device and ip debug output added. ++ * ++ * Revision 1.151 2001/05/18 16:17:35 rgb ++ * Changed reference from "magic" to "shunt" SAs. ++ * ++ * Revision 1.150 2001/05/18 16:12:19 rgb ++ * Changed UDP/500 bypass test from 3 nested ifs to one anded if. ++ * ++ * Revision 1.149 2001/05/16 04:39:33 rgb ++ * Add default == eroute.dest to IKE bypass conditions for magic eroutes. ++ * ++ * Revision 1.148 2001/05/05 03:31:41 rgb ++ * IP frag debugging updates and enhancements. ++ * ++ * Revision 1.147 2001/05/03 19:41:40 rgb ++ * Added SS' skb_cow fix for 2.4.4. ++ * ++ * Revision 1.146 2001/04/30 19:28:16 rgb ++ * Update for 2.4.4. ip_select_ident() now has 3 args. ++ * ++ * Revision 1.145 2001/04/23 14:56:10 rgb ++ * Added spin_lock() check to prevent double-locking for multiple ++ * transforms and hence kernel lock-ups with SMP kernels. ++ * ++ * Revision 1.144 2001/04/21 23:04:45 rgb ++ * Define out skb->used for 2.4 kernels. ++ * Check if soft expire has already been sent before sending another to ++ * prevent ACQUIRE flooding. ++ * ++ * Revision 1.143 2001/03/16 07:37:21 rgb ++ * Added comments to all #endifs. ++ * ++ * Revision 1.142 2001/02/28 05:03:27 rgb ++ * Clean up and rationalise startup messages. ++ * ++ * Revision 1.141 2001/02/27 22:24:54 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.140 2001/02/27 06:40:12 rgb ++ * Fixed TRAP->HOLD eroute byte order. ++ * ++ * Revision 1.139 2001/02/26 20:38:59 rgb ++ * Added compiler defines for 2.4.x-specific code. ++ * ++ * Revision 1.138 2001/02/26 19:57:27 rgb ++ * Implement magic SAs %drop, %reject, %trap, %hold, %pass as part ++ * of the new SPD and to support opportunistic. ++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs. ++ * ++ * Revision 1.137 2001/02/19 22:29:49 rgb ++ * Fixes for presence of active ipv6 segments which share ipsec physical ++ * device (gg). ++ * ++ * Revision 1.136 2001/01/29 22:30:38 rgb ++ * Fixed minor acquire debug printing bug. ++ * ++ * Revision 1.135 2001/01/29 22:19:45 rgb ++ * Zero source address for 2.4 bypass route lookup. ++ * ++ * Revision 1.134 2001/01/23 20:19:49 rgb ++ * 2.4 fix to remove removed is_clone member. ++ * ++ * Revision 1.133 2000/12/09 22:08:35 rgb ++ * Fix NET_23 bug, should be NETDEV_23. ++ * ++ * Revision 1.132 2000/12/01 06:54:50 rgb ++ * Fix for new 2.4 IP TTL default variable name. ++ * ++ * Revision 1.131 2000/11/09 20:52:15 rgb ++ * More spinlock shuffling, locking earlier and unlocking later in rcv to ++ * include ipcomp and prevent races, renaming some tdb variables that got ++ * forgotten, moving some unlocks to include tdbs and adding a missing ++ * unlock. Thanks to Svenning for some of these. ++ * ++ * Revision 1.130 2000/11/09 20:11:22 rgb ++ * Minor shuffles to fix non-standard kernel config option selection. ++ * ++ * Revision 1.129 2000/11/06 04:32:49 rgb ++ * Clean up debug printing. ++ * Copy skb->protocol for all kernel versions. ++ * Ditched spin_lock_irqsave in favour of spin_lock. ++ * Disabled TTL decrement, done in ip_forward. ++ * Added debug printing before pfkey_acquire(). ++ * Fixed printk-deltdbchain-spin_lock races (Svenning). ++ * Use defaultTTL for 2.1+ kernels. ++ * Add Svenning's adaptive content compression. ++ * Fix up debug display arguments. ++ * ++ * Revision 1.128 2000/09/28 00:58:57 rgb ++ * Moved the IKE passthrough check after the eroute lookup so we can pass ++ * IKE through intermediate tunnels. ++ * ++ * Revision 1.127 2000/09/22 17:52:11 rgb ++ * Fixed misleading ipcomp debug output. ++ * ++ * Revision 1.126 2000/09/22 04:22:56 rgb ++ * Fixed dumb spi->cpi conversion error. ++ * ++ * Revision 1.125 2000/09/21 04:34:48 rgb ++ * A few debug-specific things should be hidden under ++ * CONFIG_IPSEC_DEBUG.(MB) ++ * Improved ip_send() error handling.(MB) ++ * ++ * Revision 1.124 2000/09/21 03:40:58 rgb ++ * Added more debugging to try and track down the cpi outward copy problem. ++ * ++ * Revision 1.123 2000/09/19 07:08:49 rgb ++ * Added debugging to outgoing compression report. ++ * ++ * Revision 1.122 2000/09/18 19:21:26 henry ++ * RGB-supplied fix for RH5.2 problem ++ * ++ * Revision 1.121 2000/09/17 21:05:09 rgb ++ * Added tdb to skb_compress call to write in cpi. ++ * ++ * Revision 1.120 2000/09/17 16:57:16 rgb ++ * Added Svenning's patch to remove restriction of ipcomp to innermost ++ * transform. ++ * ++ * Revision 1.119 2000/09/15 11:37:01 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.118 2000/09/15 04:57:16 rgb ++ * Moved debug output after sanity check. ++ * Added tos copy sysctl. ++ * ++ * Revision 1.117 2000/09/12 03:22:51 rgb ++ * Converted ipsec_icmp, no_eroute_pass, opportunistic and #if0 debugs to ++ * sysctl. ++ * ++ * Revision 1.116 2000/09/08 19:18:19 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Added outgoing opportunistic hook, ifdef'ed out. ++ * ++ * Revision 1.115 2000/08/30 05:27:29 rgb ++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst. ++ * Kill remainder of tdb_xform, tdb_xdata, xformsw. ++ * ++ * Revision 1.114 2000/08/28 18:15:46 rgb ++ * Added MB's nf-debug reset patch. ++ * ++ * Revision 1.113 2000/08/27 02:26:40 rgb ++ * Send all no-eroute-bypass, pluto-bypass and passthrough packets through ++ * fragmentation machinery for 2.0, 2.2 and 2.4 kernels. ++ * ++ * Revision 1.112 2000/08/20 21:37:33 rgb ++ * Activated pfkey_expire() calls. ++ * Added a hard/soft expiry parameter to pfkey_expire(). (Momchil) ++ * Re-arranged the order of soft and hard expiry to conform to RFC2367. ++ * Clean up references to CONFIG_IPSEC_PFKEYv2. ++ * ++ * Revision 1.111 2000/08/01 14:51:51 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.110 2000/07/28 14:58:31 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.109 2000/07/28 13:50:54 rgb ++ * Changed enet_statistics to net_device_stats and added back compatibility ++ * for pre-2.1.19. ++ * ++ * Revision 1.108 2000/05/16 03:03:11 rgb ++ * Updates for 2.3.99pre8 from MB. ++ * ++ * Revision 1.107 2000/05/10 23:08:21 rgb ++ * Print a debug warning about bogus packets received by the outgoing ++ * processing machinery only when klipsdebug is not set to none. ++ * Comment out the device initialisation informational messages. ++ * ++ * Revision 1.106 2000/05/10 19:17:14 rgb ++ * Define an IP_SEND macro, intending to have all packet passthroughs ++ * use fragmentation. This didn't quite work, but is a step in the ++ * right direction. ++ * Added buffer allocation debugging statements. ++ * Added configure option to shut off no eroute passthrough. ++ * Only check usetime against soft and hard limits if the tdb has been ++ * used. ++ * Cast output of ntohl so that the broken prototype doesn't make our ++ * compile noisy. ++ * ++ * Revision 1.105 2000/03/22 16:15:37 rgb ++ * Fixed renaming of dev_get (MB). ++ * ++ * Revision 1.104 2000/03/16 14:04:15 rgb ++ * Indented headers for readability. ++ * Fixed debug scope to enable compilation with debug off. ++ * Added macros for ip_chk_addr and IS_MYADDR for identifying self. ++ * ++ * Revision 1.103 2000/03/16 07:11:07 rgb ++ * Hardcode PF_KEYv2 support. ++ * Fixed bug which allowed UDP/500 packet from another machine ++ * through in the clear. ++ * Added disabled skb->protocol fix for ISDN/ASYNC PPP from Matjaz Godec. ++ * ++ * Revision 1.102 2000/03/14 12:26:59 rgb ++ * Added skb->nfct support for clearing netfilter conntrack bits (MB). ++ * ++ * Revision 1.101 2000/02/14 21:05:22 rgb ++ * Added MB's netif_queue fix for kernels 2.3.43+. ++ * ++ * Revision 1.100 2000/01/26 10:04:57 rgb ++ * Fixed noisy 2.0 printk arguments. ++ * ++ * Revision 1.99 2000/01/21 06:16:25 rgb ++ * Added sanity checks on skb_push(), skb_pull() to prevent panics. ++ * Switched to AF_ENCAP macro. ++ * Shortened debug output per packet and re-arranging debug_tunnel ++ * bitmap flags, while retaining necessary information to avoid ++ * trampling the kernel print ring buffer. ++ * Reformatted recursion switch code. ++ * Changed all references to tdb_proto to tdb_said.proto for clarity. ++ * ++ * Revision 1.98 2000/01/13 08:09:31 rgb ++ * Shuffled debug_tunnel switches to focus output. ++ * Fixed outgoing recursion bug, limiting to recursing only if the remote ++ * SG changes and if it is valid, ie. not passthrough. ++ * Clarified a number of debug messages. ++ * ++ * Revision 1.97 2000/01/10 16:37:16 rgb ++ * MB support for new ip_select_ident() upon disappearance of ++ * ip_id_count in 2.3.36+. ++ * ++ * Revision 1.96 1999/12/31 14:59:08 rgb ++ * MB fix to use new skb_copy_expand in kernel 2.3.35. ++ * ++ * Revision 1.95 1999/12/29 21:15:44 rgb ++ * Fix tncfg to aliased device bug. ++ * ++ * Revision 1.94 1999/12/22 04:26:06 rgb ++ * Converted all 'static' functions to 'DEBUG_NO_STATIC' to enable ++ * debugging by providing external labels to all functions with debugging ++ * turned on. ++ * ++ * Revision 1.93 1999/12/13 13:30:14 rgb ++ * Changed MTU reports and HW address reporting back to debug only. ++ * ++ * Revision 1.92 1999/12/07 18:57:56 rgb ++ * Fix PFKEY symbol compile error (SADB_*) without pfkey enabled. ++ * ++ * Revision 1.91 1999/12/01 22:15:36 rgb ++ * Add checks for LARVAL and DEAD SAs. ++ * Change state of SA from MATURE to DYING when a soft lifetime is ++ * reached and print debug warning. ++ * ++ * Revision 1.90 1999/11/23 23:04:04 rgb ++ * Use provided macro ADDRTOA_BUF instead of hardcoded value. ++ * Sort out pfkey and freeswan headers, putting them in a library path. ++ * ++ * Revision 1.89 1999/11/18 18:50:59 rgb ++ * Changed all device registrations for static linking to ++ * dynamic to reduce the number and size of patches. ++ * ++ * Revision 1.88 1999/11/18 04:09:19 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.87 1999/11/17 15:53:40 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.86 1999/10/16 18:25:37 rgb ++ * Moved SA lifetime expiry checks before packet processing. ++ * Expire SA on replay counter rollover. ++ * ++ * Revision 1.85 1999/10/16 04:24:31 rgb ++ * Add stats for time since last packet. ++ * ++ * Revision 1.84 1999/10/16 00:30:47 rgb ++ * Added SA lifetime counting. ++ * ++ * Revision 1.83 1999/10/15 22:15:57 rgb ++ * Clean out cruft. ++ * Add debugging. ++ * ++ * Revision 1.82 1999/10/08 18:26:19 rgb ++ * Fix 2.0.3x outgoing fragmented packet memory leak. ++ * ++ * Revision 1.81 1999/10/05 02:38:54 rgb ++ * Lower the default mtu of virtual devices to 16260. ++ * ++ * Revision 1.80 1999/10/03 18:56:41 rgb ++ * Spinlock support for 2.3.xx. ++ * Don't forget to undo spinlocks on error! ++ * Check for valid eroute before copying the structure. ++ * ++ * Revision 1.79 1999/10/01 15:44:53 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.78 1999/10/01 00:02:43 rgb ++ * Added tdb structure locking. ++ * Added eroute structure locking. ++ * ++ * Revision 1.77 1999/09/30 02:52:29 rgb ++ * Add Marc Boucher's Copy-On-Write code (same as ipsec_rcv.c). ++ * ++ * Revision 1.76 1999/09/25 19:31:27 rgb ++ * Refine MSS hack to affect SYN, but not SYN+ACK packets. ++ * ++ * Revision 1.75 1999/09/24 22:52:38 rgb ++ * Fix two things broken in 2.0.38 by trying to fix network notifiers. ++ * ++ * Revision 1.74 1999/09/24 00:30:37 rgb ++ * Add test for changed source as well as destination to check for ++ * recursion. ++ * ++ * Revision 1.73 1999/09/23 20:52:24 rgb ++ * Add James Morris' MSS hack patch, disabled. ++ * ++ * Revision 1.72 1999/09/23 20:22:40 rgb ++ * Enable, tidy and fix network notifier code. ++ * ++ * Revision 1.71 1999/09/23 18:09:05 rgb ++ * Clean up 2.2.x fragmenting traces. ++ * Disable dev->type switching, forcing ARPHRD_TUNNEL. ++ * ++ * Revision 1.70 1999/09/22 14:14:24 rgb ++ * Add sanity checks for revectored calls to prevent calling a downed I/F. ++ * ++ * Revision 1.69 1999/09/21 15:00:57 rgb ++ * Add Marc Boucher's packet size check. ++ * Flesh out network device notifier code. ++ * ++ * Revision 1.68 1999/09/18 11:39:57 rgb ++ * Start to add (disabled) netdevice notifier code. ++ * ++ * Revision 1.67 1999/09/17 23:44:40 rgb ++ * Add a comment warning potential code hackers to stay away from mac.raw. ++ * ++ * Revision 1.66 1999/09/17 18:04:02 rgb ++ * Add fix for unpredictable hard_header_len for ISDN folks (thanks MB). ++ * Ditch TTL decrement in 2.2 (MB). ++ * ++ * Revision 1.65 1999/09/15 23:15:35 henry ++ * Marc Boucher's PPP fixes ++ * ++ * Revision 1.64 1999/09/07 13:40:53 rgb ++ * Ditch unreliable references to skb->mac.raw. ++ * ++ * Revision 1.63 1999/08/28 11:33:09 rgb ++ * Check for null skb->mac pointer. ++ * ++ * Revision 1.62 1999/08/28 02:02:30 rgb ++ * Add Marc Boucher's fix for properly dealing with skb->sk. ++ * ++ * Revision 1.61 1999/08/27 05:23:05 rgb ++ * Clean up skb->data/raw/nh/h manipulation. ++ * Add Marc Boucher's mods to aid tcpdump. ++ * Add sanity checks to skb->raw/nh/h pointer copies in skb_copy_expand. ++ * Re-order hard_header stripping -- might be able to remove it... ++ * ++ * Revision 1.60 1999/08/26 20:01:02 rgb ++ * Tidy up compiler directives and macros. ++ * Re-enable ICMP for tunnels where inner_dst != outer_dst. ++ * Remove unnecessary skb->dev = physdev assignment affecting 2.2.x. ++ * ++ * Revision 1.59 1999/08/25 15:44:41 rgb ++ * Clean up from 2.2.x instrumenting for compilation under 2.0.36. ++ * ++ * Revision 1.58 1999/08/25 15:00:54 rgb ++ * Add dst cache code for 2.2.xx. ++ * Add sanity check for skb packet header pointers. ++ * Add/modify debugging instrumentation to *_start_xmit, *_hard_header and ++ * *_rebuild_header. ++ * Add neigh_* cache code. ++ * Change dev->type back to ARPHRD_TUNNEL. ++ * ++ * Revision 1.57 1999/08/17 21:50:23 rgb ++ * Fixed minor debug output bugs. ++ * Regrouped error recovery exit code. ++ * Added compiler directives to remove unwanted code and symbols. ++ * Shut off ICMP messages: to be refined to only send ICMP to remote systems. ++ * Add debugging code for output function addresses. ++ * Fix minor bug in (possibly unused) header_cache_bind function. ++ * Add device neighbour caching code. ++ * Change dev->type from ARPHRD_TUNNEL to physdev->type. ++ * ++ * Revision 1.56 1999/08/03 17:22:56 rgb ++ * Debug output clarification using KERN_* macros. Other inactive changes ++ * added. ++ * ++ * Revision 1.55 1999/08/03 16:58:46 rgb ++ * Fix skb_copy_expand size bug. Was getting incorrect size. ++ * ++ * Revision 1.54 1999/07/14 19:32:38 rgb ++ * Fix oversize packet crash and ssh stalling in 2.2.x kernels. ++ * ++ * Revision 1.53 1999/06/10 15:44:02 rgb ++ * Minor reformatting and clean-up. ++ * ++ * Revision 1.52 1999/05/09 03:25:36 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.51 1999/05/08 21:24:59 rgb ++ * Add casting to silence the 2.2.x compile. ++ * ++ * Revision 1.50 1999/05/05 22:02:32 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.49 1999/04/29 15:18:52 rgb ++ * Change gettdb parameter to a pointer to reduce stack loading and ++ * facilitate parameter sanity checking. ++ * Fix undetected bug that might have tried to access a null pointer. ++ * Eliminate unnessessary usage of tdb_xform member to further switch ++ * away from the transform switch to the algorithm switch. ++ * Add return values to init and cleanup functions. ++ * ++ * Revision 1.48 1999/04/16 15:38:00 rgb ++ * Minor rearrangement of freeing code to avoid memory leaks with impossible or ++ * rare situations. ++ * ++ * Revision 1.47 1999/04/15 15:37:25 rgb ++ * Forward check changes from POST1_00 branch. ++ * ++ * Revision 1.32.2.4 1999/04/13 21:00:18 rgb ++ * Ditch 'things I wish I had known before...'. ++ * ++ * Revision 1.32.2.3 1999/04/13 20:34:38 rgb ++ * Free skb after fragmentation. ++ * Use stats more effectively. ++ * Add I/F to mtu notch-down reporting. ++ * ++ * Revision 1.32.2.2 1999/04/02 04:26:14 rgb ++ * Backcheck from HEAD, pre1.0. ++ * ++ * Revision 1.46 1999/04/11 00:29:00 henry ++ * GPL boilerplate ++ * ++ * Revision 1.45 1999/04/07 15:42:01 rgb ++ * Fix mtu/ping bug AGAIN! ++ * ++ * Revision 1.44 1999/04/06 04:54:27 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.43 1999/04/04 03:57:07 rgb ++ * ip_fragment() doesn't free the supplied skb. Freed. ++ * ++ * Revision 1.42 1999/04/01 23:27:15 rgb ++ * Preload size of virtual mtu. ++ * ++ * Revision 1.41 1999/04/01 09:31:23 rgb ++ * Invert meaning of ICMP PMTUD config option and clarify. ++ * Code clean-up. ++ * ++ * Revision 1.40 1999/04/01 04:37:17 rgb ++ * SSH stalling bug fix. ++ * ++ * Revision 1.39 1999/03/31 23:44:28 rgb ++ * Don't send ICMP on DF and frag_off. ++ * ++ * Revision 1.38 1999/03/31 15:20:10 rgb ++ * Quiet down debugging. ++ * ++ * Revision 1.37 1999/03/31 08:30:31 rgb ++ * Add switch to shut off ICMP PMTUD packets. ++ * ++ * Revision 1.36 1999/03/31 05:44:47 rgb ++ * Keep PMTU reduction private. ++ * ++ * Revision 1.35 1999/03/27 15:13:02 rgb ++ * PMTU/fragmentation bug fix. ++ * ++ * Revision 1.34 1999/03/17 21:19:26 rgb ++ * Fix kmalloc nonatomic bug. ++ * ++ * Revision 1.33 1999/03/17 15:38:42 rgb ++ * Code clean-up. ++ * ESP_NULL IV bug fix. ++ * ++ * Revision 1.32 1999/03/01 20:44:25 rgb ++ * Code clean-up. ++ * Memory leak bug fix. ++ * ++ * Revision 1.31 1999/02/27 00:02:09 rgb ++ * Tune to report the MTU reduction once, rather than after every recursion ++ * through the encapsulating code, preventing tcp stream stalling. ++ * ++ * Revision 1.30 1999/02/24 20:21:01 rgb ++ * Reformat debug printk's. ++ * Fix recursive encapsulation, dynamic MTU bugs and add debugging code. ++ * Clean-up. ++ * ++ * Revision 1.29 1999/02/22 17:08:14 rgb ++ * Fix recursive encapsulation code. ++ * ++ * Revision 1.28 1999/02/19 18:27:02 rgb ++ * Improve DF, fragmentation and PMTU behaviour and add dynamic MTU discovery. ++ * ++ * Revision 1.27 1999/02/17 16:51:37 rgb ++ * Clean out unused cruft. ++ * Temporarily tone down volume of debug output. ++ * Temporarily shut off fragment rejection. ++ * Disabled temporary failed recursive encapsulation loop. ++ * ++ * Revision 1.26 1999/02/12 21:21:26 rgb ++ * Move KLIPS_PRINT to ipsec_netlink.h for accessibility. ++ * ++ * Revision 1.25 1999/02/11 19:38:27 rgb ++ * More clean-up. ++ * Add sanity checking for skb_copy_expand() to prevent kernel panics on ++ * skb_put() values out of range. ++ * Fix head/tailroom calculation causing skb_put() out-of-range values. ++ * Fix return values to prevent 'nonatomic alloc_skb' warnings. ++ * Allocate new skb iff needed. ++ * Added more debug statements. ++ * Make headroom depend on structure, not hard-coded values. ++ * ++ * Revision 1.24 1999/02/10 23:20:33 rgb ++ * Shut up annoying 'statement has no effect' compiler warnings with ++ * debugging compiled out. ++ * ++ * Revision 1.23 1999/02/10 22:36:30 rgb ++ * Clean-up obsolete, unused and messy code. ++ * Converted most IPSEC_DEBUG statements to KLIPS_PRINT macros. ++ * Rename ipsec_tunnel_do_xmit to ipsec_tunnel_start_xmit and eliminated ++ * original ipsec_tunnel_start_xmit. ++ * Send all packet with different inner and outer destinations directly to ++ * the attached physical device, rather than back through ip_forward, ++ * preventing disappearing routes problems. ++ * Do sanity checking before investing too much CPU in allocating new ++ * structures. ++ * Fail on IP header options: We cannot process them yet. ++ * Add some helpful comments. ++ * Use virtual device for parameters instead of physical device. ++ * ++ * Revision 1.22 1999/02/10 03:03:02 rgb ++ * Duh. Fixed the TTL bug: forgot to update the checksum. ++ * ++ * Revision 1.21 1999/02/09 23:17:53 rgb ++ * Add structure members to ipsec_print_ip debug function. ++ * Temporarily fix TTL bug preventing tunnel mode from functioning. ++ * ++ * Revision 1.20 1999/02/09 00:14:25 rgb ++ * Add KLIPSPRINT macro. (Not used yet, though.) ++ * Delete old ip_tunnel code (BADCODE). ++ * Decrement TTL in outgoing packet. ++ * Set TTL on new IPIP_TUNNEL to default, not existing packet TTL. ++ * Delete ethernet only feature and fix hard-coded hard_header_len. ++ * ++ * Revision 1.19 1999/01/29 17:56:22 rgb ++ * 64-bit re-fix submitted by Peter Onion. ++ * ++ * Revision 1.18 1999/01/28 22:43:24 rgb ++ * Fixed bug in ipsec_print_ip that caused an OOPS, found by P.Onion. ++ * ++ * Revision 1.17 1999/01/26 02:08:16 rgb ++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro. ++ * Removed dead code. ++ * ++ * Revision 1.16 1999/01/22 06:25:26 rgb ++ * Cruft clean-out. ++ * Added algorithm switch code. ++ * 64-bit clean-up. ++ * Passthrough on IPIP protocol, spi 0x0 fix. ++ * Enhanced debugging. ++ * ++ * Revision 1.15 1998/12/01 13:22:04 rgb ++ * Added support for debug printing of version info. ++ * ++ * Revision 1.14 1998/11/30 13:22:55 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.13 1998/11/17 21:13:52 rgb ++ * Put IKE port bypass debug output in user-switched debug statements. ++ * ++ * Revision 1.12 1998/11/13 13:20:25 rgb ++ * Fixed ntohs bug in udp/500 hole for IKE. ++ * ++ * Revision 1.11 1998/11/10 08:01:19 rgb ++ * Kill tcp/500 hole, keep udp/500 hole. ++ * ++ * Revision 1.10 1998/11/09 21:29:26 rgb ++ * If no eroute is found, discard packet and incr. tx_error. ++ * ++ * Revision 1.9 1998/10/31 06:50:00 rgb ++ * Add tcp/udp/500 bypass. ++ * Fixed up comments in #endif directives. ++ * ++ * Revision 1.8 1998/10/27 00:34:31 rgb ++ * Reformat debug output of IP headers. ++ * Newlines added before calls to ipsec_print_ip. ++ * ++ * Revision 1.7 1998/10/19 14:44:28 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.6 1998/10/09 04:31:35 rgb ++ * Added 'klips_debug' prefix to all klips printk debug statements. ++ * ++ * Revision 1.5 1998/08/28 03:09:51 rgb ++ * Prevent kernel log spam with default route through ipsec. ++ * ++ * Revision 1.4 1998/08/05 22:23:09 rgb ++ * Change setdev return code to ENXIO for a non-existant physical device. ++ * ++ * Revision 1.3 1998/07/29 20:41:11 rgb ++ * Add ipsec_tunnel_clear to clear all tunnel attachments. ++ * ++ * Revision 1.2 1998/06/25 20:00:33 rgb ++ * Clean up #endif comments. ++ * Rename dev_ipsec to dev_ipsec0 for consistency. ++ * Document ipsec device fields. ++ * Make ipsec_tunnel_probe visible from rest of kernel for static linking. ++ * Get debugging report for *every* ipsec device initialisation. ++ * Comment out redundant code. ++ * ++ * Revision 1.1 1998/06/18 21:27:50 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.8 1998/06/14 23:49:40 rgb ++ * Clarify version reporting on module loading. ++ * ++ * Revision 1.7 1998/05/27 23:19:20 rgb ++ * Added version reporting. ++ * ++ * Revision 1.6 1998/05/18 21:56:23 rgb ++ * Clean up for numerical consistency of output and cleaning up debug code. ++ * ++ * Revision 1.5 1998/05/12 02:44:23 rgb ++ * Clarifying 'no e-route to host' message. ++ * ++ * Revision 1.4 1998/04/30 15:34:35 rgb ++ * Enclosed most remaining debugging statements in #ifdef's to make it quieter. ++ * ++ * Revision 1.3 1998/04/21 21:28:54 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.2 1998/04/12 22:03:24 rgb ++ * Updated ESP-3DES-HMAC-MD5-96, ++ * ESP-DES-HMAC-MD5-96, ++ * AH-HMAC-MD5-96, ++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository ++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts. ++ * ++ * Fixed eroute references in /proc/net/ipsec*. ++ * ++ * Started to patch module unloading memory leaks in ipsec_netlink and ++ * radij tree unloading. ++ * ++ * Revision 1.1 1998/04/09 03:06:12 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.5 1997/06/03 04:24:48 ji ++ * Added transport mode. ++ * Changed the way routing is done. ++ * Lots of bug fixes. ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * Local Variables: ++ * c-style: linux ++ * End: ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_xform.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,351 @@ ++/* ++ * Common routines for IPSEC transformations. ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: ipsec_xform.c,v 1.63 2003/10/31 02:27:55 mcr Exp $ ++ */ ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "freeswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#include /* get_random_bytes() */ ++#include ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++#endif ++#include ++#include ++ ++#include "freeswan/radij.h" ++#include "freeswan/ipsec_encap.h" ++#include "freeswan/ipsec_radij.h" ++#include "freeswan/ipsec_xform.h" ++#include "freeswan/ipsec_ipe4.h" ++#include "freeswan/ipsec_ah.h" ++#include "freeswan/ipsec_esp.h" ++ ++#include ++#include ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_xform = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#ifdef SPINLOCK ++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED; ++#else /* SPINLOCK */ ++spinlock_t tdb_lock; ++#endif /* SPINLOCK */ ++ ++/* ++ * $Log: ipsec_xform.c,v $ ++ * Revision 1.63 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.62.30.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.62 2002/05/14 02:34:21 rgb ++ * Delete stale code. ++ * ++ * Revision 1.61 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.60 2002/04/24 07:36:33 mcr ++ * Moved from ./klips/net/ipsec/ipsec_xform.c,v ++ * ++ * Revision 1.59 2002/03/29 15:01:36 rgb ++ * Delete decommissioned code. ++ * ++ * Revision 1.58 2002/01/29 17:17:57 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.57 2002/01/29 04:00:53 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.56 2001/11/27 05:17:22 mcr ++ * turn off the worst of the per-packet debugging. ++ * ++ * Revision 1.55 2001/11/26 09:23:50 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.54 2001/10/18 04:45:21 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.53 2001/09/08 21:13:34 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * ++ * Revision 1.52 2001/06/14 19:35:11 rgb ++ * Update copyright date. ++ * ++ * Revision 1.51 2001/05/30 08:14:03 rgb ++ * Removed vestiges of esp-null transforms. ++ * ++ * Revision 1.50 2001/05/03 19:43:18 rgb ++ * Initialise error return variable. ++ * Update SENDERR macro. ++ * Fix sign of error return code for ipsec_tdbcleanup(). ++ * Use more appropriate return code for ipsec_tdbwipe(). ++ * ++ * Revision 1.49 2001/04/19 18:56:17 rgb ++ * Fixed tdb table locking comments. ++ * ++ * Revision 1.48 2001/02/27 22:24:55 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.47 2000/11/06 04:32:08 rgb ++ * Ditched spin_lock_irqsave in favour of spin_lock_bh. ++ * ++ * Revision 1.46 2000/09/20 16:21:57 rgb ++ * Cleaned up ident string alloc/free. ++ * ++ * Revision 1.45 2000/09/08 19:16:51 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Removed all references to CONFIG_IPSEC_PFKEYv2. ++ * ++ * Revision 1.44 2000/08/30 05:29:04 rgb ++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c. ++ * ++ * Revision 1.43 2000/08/18 21:30:41 rgb ++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear. ++ * ++ * Revision 1.42 2000/08/01 14:51:51 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.41 2000/07/28 14:58:31 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.40 2000/06/28 05:50:11 rgb ++ * Actually set iv_bits. ++ * ++ * Revision 1.39 2000/05/10 23:11:09 rgb ++ * Added netlink debugging output. ++ * Added a cast to quiet down the ntohl bug. ++ * ++ * Revision 1.38 2000/05/10 19:18:42 rgb ++ * Cast output of ntohl so that the broken prototype doesn't make our ++ * compile noisy. ++ * ++ * Revision 1.37 2000/03/16 14:04:59 rgb ++ * Hardwired CONFIG_IPSEC_PFKEYv2 on. ++ * ++ * Revision 1.36 2000/01/26 10:11:28 rgb ++ * Fixed spacing in error text causing run-in words. ++ * ++ * Revision 1.35 2000/01/21 06:17:16 rgb ++ * Tidied up compiler directive indentation for readability. ++ * Added ictx,octx vars for simplification.(kravietz) ++ * Added macros for HMAC padding magic numbers.(kravietz) ++ * Fixed missing key length reporting bug. ++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in. ++ * ++ * Revision 1.34 1999/12/08 00:04:19 rgb ++ * Fixed SA direction overwriting bug for netlink users. ++ * ++ * Revision 1.33 1999/12/01 22:16:44 rgb ++ * Minor formatting changes in ESP MD5 initialisation. ++ * ++ * Revision 1.32 1999/11/25 09:06:36 rgb ++ * Fixed error return messages, should be returning negative numbers. ++ * Implemented SENDERR macro for propagating error codes. ++ * Added debug message and separate error code for algorithms not compiled ++ * in. ++ * ++ * Revision 1.31 1999/11/23 23:06:26 rgb ++ * Sort out pfkey and freeswan headers, putting them in a library path. ++ * ++ * Revision 1.30 1999/11/18 04:09:20 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.29 1999/11/17 15:53:40 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.28 1999/10/18 20:04:01 rgb ++ * Clean-out unused cruft. ++ * ++ * Revision 1.27 1999/10/03 19:01:03 rgb ++ * Spinlock support for 2.3.xx and 2.0.xx kernels. ++ * ++ * Revision 1.26 1999/10/01 16:22:24 rgb ++ * Switch from assignment init. to functional init. of spinlocks. ++ * ++ * Revision 1.25 1999/10/01 15:44:54 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.24 1999/10/01 00:03:46 rgb ++ * Added tdb structure locking. ++ * Minor formatting changes. ++ * Add function to initialize tdb hash table. ++ * ++ * Revision 1.23 1999/05/25 22:42:12 rgb ++ * Add deltdbchain() debugging. ++ * ++ * Revision 1.22 1999/05/25 21:24:31 rgb ++ * Add debugging statements to deltdbchain(). ++ * ++ * Revision 1.21 1999/05/25 03:51:48 rgb ++ * Refix error return code. ++ * ++ * Revision 1.20 1999/05/25 03:34:07 rgb ++ * Fix error return for flush. ++ * ++ * Revision 1.19 1999/05/09 03:25:37 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.18 1999/05/05 22:02:32 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.17 1999/04/29 15:20:16 rgb ++ * Change gettdb parameter to a pointer to reduce stack loading and ++ * facilitate parameter sanity checking. ++ * Add sanity checking for null pointer arguments. ++ * Add debugging instrumentation. ++ * Add function deltdbchain() which will take care of unlinking, ++ * zeroing and deleting a chain of tdbs. ++ * Add a parameter to tdbcleanup to be able to delete a class of SAs. ++ * tdbwipe now actually zeroes the tdb as well as any of its pointed ++ * structures. ++ * ++ * Revision 1.16 1999/04/16 15:36:29 rgb ++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing. ++ * ++ * Revision 1.15 1999/04/11 00:29:01 henry ++ * GPL boilerplate ++ * ++ * Revision 1.14 1999/04/06 04:54:28 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.13 1999/02/19 18:23:01 rgb ++ * Nix debug off compile warning. ++ * ++ * Revision 1.12 1999/02/17 16:52:16 rgb ++ * Consolidate satoa()s for space and speed efficiency. ++ * Convert DEBUG_IPSEC to KLIPS_PRINT ++ * Clean out unused cruft. ++ * Ditch NET_IPIP dependancy. ++ * Loop for 3des key setting. ++ * ++ * Revision 1.11 1999/01/26 02:09:05 rgb ++ * Remove ah/esp/IPIP switching on include files. ++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro. ++ * Removed dead code. ++ * Clean up debug code when switched off. ++ * Remove references to INET_GET_PROTOCOL. ++ * Added code exclusion macros to reduce code from unused algorithms. ++ * ++ * Revision 1.10 1999/01/22 06:28:55 rgb ++ * Cruft clean-out. ++ * Put random IV generation in kernel. ++ * Added algorithm switch code. ++ * Enhanced debugging. ++ * 64-bit clean-up. ++ * ++ * Revision 1.9 1998/11/30 13:22:55 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.8 1998/11/25 04:59:06 rgb ++ * Add conditionals for no IPIP tunnel code. ++ * Delete commented out code. ++ * ++ * Revision 1.7 1998/10/31 06:50:41 rgb ++ * Convert xform ASCII names to no spaces. ++ * Fixed up comments in #endif directives. ++ * ++ * Revision 1.6 1998/10/19 14:44:28 rgb ++ * Added inclusion of freeswan.h. ++ * sa_id structure implemented and used: now includes protocol. ++ * ++ * Revision 1.5 1998/10/09 04:32:19 rgb ++ * Added 'klips_debug' prefix to all klips printk debug statements. ++ * ++ * Revision 1.4 1998/08/12 00:11:31 rgb ++ * Added new xform functions to the xform table. ++ * Fixed minor debug output spelling error. ++ * ++ * Revision 1.3 1998/07/09 17:45:31 rgb ++ * Clarify algorithm not available message. ++ * ++ * Revision 1.2 1998/06/23 03:00:51 rgb ++ * Check for presence of IPIP protocol if it is setup one way (we don't ++ * know what has been set up the other way and can only assume it will be ++ * symmetrical with the exception of keys). ++ * ++ * Revision 1.1 1998/06/18 21:27:51 henry ++ * move sources from klips/src to klips/net/ipsec, to keep stupid ++ * kernel-build scripts happier in the presence of symlinks ++ * ++ * Revision 1.3 1998/06/11 05:54:59 rgb ++ * Added transform version string pointer to xformsw initialisations. ++ * ++ * Revision 1.2 1998/04/21 21:28:57 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.1 1998/04/09 03:06:13 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.5 1997/06/03 04:24:48 ji ++ * Added ESP-3DES-MD5-96 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * Added new transforms. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/ipsec_xmit.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1869 @@ ++/* ++ * IPSEC Transmit code. ++ * Copyright (C) 1996, 1997 John Ioannidis. ++ * Copyright (C) 1998-2003 Richard Guy Briggs. ++ * Copyright (C) 2004 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ */ ++ ++char ipsec_xmit_c_version[] = "RCSID $Id: ipsec_xmit.c,v 1.8 2004/04/06 02:49:26 mcr Exp $"; ++ ++#define __NO_VERSION__ ++#include ++#include /* for CONFIG_IP_FORWARD */ ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include /* struct tcphdr */ ++#include /* struct udphdr */ ++#include ++#include ++#ifdef NET_21 ++# define MSS_HACK_ /* experimental */ ++# include ++# include ++# include ++# define proto_priv cb ++#endif /* NET_21 */ ++#include ++#include /* icmp_send() */ ++#include ++#ifdef NETDEV_23 ++# include ++#endif /* NETDEV_23 */ ++ ++#include ++#ifdef MSS_HACK ++# include /* TCP options */ ++#endif /* MSS_HACK */ ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_life.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_eroute.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xmit.h" ++#include "openswan/ipsec_sa.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_ipe4.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++ ++#ifdef CONFIG_IPSEC_IPCOMP ++#include "openswan/ipcomp.h" ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++ ++/* ++ * Stupid kernel API differences in APIs. Not only do some ++ * kernels not have ip_select_ident, but some have differing APIs, ++ * and SuSE has one with one parameter, but no way of checking to ++ * see what is really what. ++ */ ++ ++#ifdef SUSE_LINUX_2_4_19_IS_STUPID ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph) ++#else ++ ++/* simplest case, nothing */ ++#if !defined(IP_SELECT_IDENT) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0) ++#endif ++ ++/* kernels > 2.3.37-ish */ ++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst) ++#endif ++ ++/* kernels > 2.4.2 */ ++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW) ++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL) ++#endif ++ ++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */ ++ ++ ++static __u32 zeroes[64]; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int sysctl_ipsec_debug_verbose = 0; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++int ipsec_xmit_trap_count = 0; ++int ipsec_xmit_trap_sendcount = 0; ++ ++int sysctl_ipsec_icmp = 0; ++int sysctl_ipsec_tos = 0; ++ ++#ifdef CONFIG_IPSEC_DEBUG_ ++DEBUG_NO_STATIC void ++dmp(char *s, caddr_t bb, int len) ++{ ++ int i; ++ unsigned char *b = bb; ++ ++ if (debug_tunnel) { ++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: " ++ "at %s, len=%d:", ++ s, ++ len); ++ for (i=0; i < len; i++) { ++ if(!(i%16)){ ++ printk("\nklips_debug: "); ++ } ++ printk(" %02x", *b++); ++ } ++ printk("\n"); ++ } ++} ++#else /* CONFIG_IPSEC_DEBUG */ ++#define dmp(_x, _y, _z) ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#ifndef SKB_COPY_EXPAND ++/* ++ * This is mostly skbuff.c:skb_copy(). ++ */ ++struct sk_buff * ++skb_copy_expand(struct sk_buff *skb, int headroom, int tailroom, int priority) ++{ ++ struct sk_buff *n; ++ unsigned long offset; ++ ++ /* ++ * Do sanity checking ++ */ ++ if((headroom < 0) || (tailroom < 0) || ((headroom+tailroom) < 0)) { ++ printk(KERN_WARNING ++ "klips_error:skb_copy_expand: " ++ "Illegal negative head,tailroom %d,%d\n", ++ headroom, ++ tailroom); ++ return NULL; ++ } ++ /* ++ * Allocate the copy buffer ++ */ ++ ++#ifndef NET_21 ++ IS_SKB(skb); ++#endif /* !NET_21 */ ++ ++ ++ n=alloc_skb(skb->end - skb->head + headroom + tailroom, priority); ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:skb_copy_expand: " ++ "allocating %d bytes, head=0p%p data=0p%p tail=0p%p end=0p%p end-head=%d tail-data=%d\n", ++ skb->end - skb->head + headroom + tailroom, ++ skb->head, ++ skb->data, ++ skb->tail, ++ skb->end, ++ skb->end - skb->head, ++ skb->tail - skb->data); ++ ++ if(n==NULL) ++ return NULL; ++ ++ /* ++ * Shift between the two data areas in bytes ++ */ ++ ++ /* Set the data pointer */ ++ skb_reserve(n,skb->data-skb->head+headroom); ++ /* Set the tail pointer and length */ ++ if(skb_tailroom(n) < skb->len) { ++ printk(KERN_WARNING "klips_error:skb_copy_expand: " ++ "tried to skb_put %ld, %d available. This should never happen, please report.\n", ++ (unsigned long int)skb->len, ++ skb_tailroom(n)); ++ ipsec_kfree_skb(n); ++ return NULL; ++ } ++ skb_put(n,skb->len); ++ ++ offset=n->head + headroom - skb->head; ++ ++ /* Copy the bytes */ ++ memcpy(n->head + headroom, skb->head,skb->end-skb->head); ++#ifdef NET_21 ++ n->csum=skb->csum; ++ n->priority=skb->priority; ++ n->dst=dst_clone(skb->dst); ++ if(skb->nh.raw) ++ n->nh.raw=skb->nh.raw+offset; ++#ifndef NETDEV_23 ++ n->is_clone=0; ++#endif /* NETDEV_23 */ ++ atomic_set(&n->users, 1); ++ n->destructor = NULL; ++ n->security=skb->security; ++#else /* NET_21 */ ++ n->link3=NULL; ++ n->when=skb->when; ++ if(skb->ip_hdr) ++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); ++ n->saddr=skb->saddr; ++ n->daddr=skb->daddr; ++ n->raddr=skb->raddr; ++ n->seq=skb->seq; ++ n->end_seq=skb->end_seq; ++ n->ack_seq=skb->ack_seq; ++ n->acked=skb->acked; ++ n->free=1; ++ n->arp=skb->arp; ++ n->tries=0; ++ n->lock=0; ++ n->users=0; ++#endif /* NET_21 */ ++ n->protocol=skb->protocol; ++ n->list=NULL; ++ n->sk=NULL; ++ n->dev=skb->dev; ++ if(skb->h.raw) ++ n->h.raw=skb->h.raw+offset; ++ if(skb->mac.raw) ++ n->mac.raw=skb->mac.raw+offset; ++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); ++#ifndef NETDEV_23 ++ n->used=skb->used; ++#endif /* !NETDEV_23 */ ++ n->pkt_type=skb->pkt_type; ++ n->stamp=skb->stamp; ++ ++#ifndef NET_21 ++ IS_SKB(n); ++#endif /* !NET_21 */ ++ return n; ++} ++#endif /* !SKB_COPY_EXPAND */ ++ ++#ifdef CONFIG_IPSEC_DEBUG ++void ++ipsec_print_ip(struct iphdr *ip) ++{ ++ char buf[ADDRTOA_BUF]; ++ ++ printk(KERN_INFO "klips_debug: IP:"); ++ printk(" ihl:%d", ip->ihl << 2); ++ printk(" ver:%d", ip->version); ++ printk(" tos:%d", ip->tos); ++ printk(" tlen:%d", ntohs(ip->tot_len)); ++ printk(" id:%d", ntohs(ip->id)); ++ printk(" %s%s%sfrag_off:%d", ++ ip->frag_off & __constant_htons(IP_CE) ? "CE " : "", ++ ip->frag_off & __constant_htons(IP_DF) ? "DF " : "", ++ ip->frag_off & __constant_htons(IP_MF) ? "MF " : "", ++ (ntohs(ip->frag_off) & IP_OFFSET) << 3); ++ printk(" ttl:%d", ip->ttl); ++ printk(" proto:%d", ip->protocol); ++ if(ip->protocol == IPPROTO_UDP) ++ printk(" (UDP)"); ++ if(ip->protocol == IPPROTO_TCP) ++ printk(" (TCP)"); ++ if(ip->protocol == IPPROTO_ICMP) ++ printk(" (ICMP)"); ++ printk(" chk:%d", ntohs(ip->check)); ++ addrtoa(*((struct in_addr*)(&ip->saddr)), 0, buf, sizeof(buf)); ++ printk(" saddr:%s", buf); ++ if(ip->protocol == IPPROTO_UDP) ++ printk(":%d", ++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); ++ if(ip->protocol == IPPROTO_TCP) ++ printk(":%d", ++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); ++ addrtoa(*((struct in_addr*)(&ip->daddr)), 0, buf, sizeof(buf)); ++ printk(" daddr:%s", buf); ++ if(ip->protocol == IPPROTO_UDP) ++ printk(":%d", ++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); ++ if(ip->protocol == IPPROTO_TCP) ++ printk(":%d", ++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); ++ if(ip->protocol == IPPROTO_ICMP) ++ printk(" type:code=%d:%d", ++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->type, ++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->code); ++ printk("\n"); ++ ++ if(sysctl_ipsec_debug_verbose) { ++ __u8 *c; ++ int i; ++ ++ c = ((__u8*)ip) + ip->ihl*4; ++ for(i = 0; i < ntohs(ip->tot_len) - ip->ihl*4; i++ /*, c++*/) { ++ if(!(i % 16)) { ++ printk(KERN_INFO ++ "klips_debug: @%03x:", ++ i); ++ } ++ printk(" %02x", /***/c[i]); ++ if(!((i + 1) % 16)) { ++ printk("\n"); ++ } ++ } ++ if(i % 16) { ++ printk("\n"); ++ } ++ } ++} ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#ifdef MSS_HACK ++/* ++ * Issues: ++ * 1) Fragments arriving in the tunnel should probably be rejected. ++ * 2) How does this affect syncookies, mss_cache, dst cache ? ++ * 3) Path MTU discovery handling needs to be reviewed. For example, ++ * if we receive an ICMP 'packet too big' message from an intermediate ++ * router specifying it's next hop MTU, our stack may process this and ++ * adjust the MSS without taking our AH/ESP overheads into account. ++ */ ++ ++ ++/* ++ * Recaclulate checksum using differences between changed datum, ++ * borrowed from netfilter. ++ */ ++DEBUG_NO_STATIC u_int16_t ++ipsec_fast_csum(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) ++{ ++ u_int32_t diffs[] = { oldvalinv, newval }; ++ return csum_fold(csum_partial((char *)diffs, sizeof(diffs), ++ oldcheck^0xFFFF)); ++} ++ ++/* ++ * Determine effective MSS. ++ * ++ * Note that we assume that there is always an MSS option for our own ++ * SYN segments, which is mentioned in tcp_syn_build_options(), kernel 2.2.x. ++ * This could change, and we should probably parse TCP options instead. ++ * ++ */ ++DEBUG_NO_STATIC u_int8_t ++ipsec_adjust_mss(struct sk_buff *skb, struct tcphdr *tcph, u_int16_t mtu) ++{ ++ u_int16_t oldmss, newmss; ++ u_int32_t *mssp; ++ struct sock *sk = skb->sk; ++ ++ newmss = tcp_sync_mss(sk, mtu); ++ printk(KERN_INFO "klips: setting mss to %u\n", newmss); ++ mssp = (u_int32_t *)tcph + sizeof(struct tcphdr) / sizeof(u_int32_t); ++ oldmss = ntohl(*mssp) & 0x0000FFFF; ++ *mssp = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | newmss); ++ tcph->check = ipsec_fast_csum(htons(~oldmss), ++ htons(newmss), tcph->check); ++ return 1; ++} ++#endif /* MSS_HACK */ ++ ++/* ++ * Sanity checks ++ */ ++enum ipsec_xmit_value ++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs) ++{ ++ ++ if (ixs->dev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_sanity_check_dev: " ++ "No device associated with skb!\n" ); ++ return IPSEC_XMIT_NODEV; ++ } ++ ++ ixs->prv = ixs->dev->priv; ++ if (ixs->prv == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_sanity_check_dev: " ++ "Device has no private structure!\n" ); ++ return IPSEC_XMIT_NOPRIVDEV; ++ } ++ ++ ixs->physdev = ixs->prv->dev; ++ if (ixs->physdev == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_sanity_check_dev: " ++ "Device is not attached to physical device!\n" ); ++ return IPSEC_XMIT_NOPHYSDEV; ++ } ++ ++ ixs->physmtu = ixs->physdev->mtu; ++ ++ ixs->stats = (struct net_device_stats *) &(ixs->prv->mystats); ++ ++ return IPSEC_XMIT_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs) ++{ ++ /* ++ * Return if there is nothing to do. (Does this ever happen?) XXX ++ */ ++ if (ixs->skb == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_sanity_check_skb: " ++ "Nothing to do!\n" ); ++ return IPSEC_XMIT_NOSKB; ++ } ++#ifdef NET_21 ++ /* if skb was cloned (most likely due to a packet sniffer such as ++ tcpdump being momentarily attached to the interface), make ++ a copy of our own to modify */ ++ if(skb_cloned(ixs->skb)) { ++ if ++#ifdef SKB_COW_NEW ++ (skb_cow(ixs->skb, skb_headroom(ixs->skb)) != 0) ++#else /* SKB_COW_NEW */ ++ ((ixs->skb = skb_cow(ixs->skb, skb_headroom(ixs->skb))) == NULL) ++#endif /* SKB_COW_NEW */ ++ { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_error:ipsec_xmit_sanity_check_skb: " ++ "skb_cow failed to allocate buffer, dropping.\n" ); ++ ixs->stats->tx_dropped++; ++ return IPSEC_XMIT_ERRSKBALLOC; ++ } ++ } ++#endif /* NET_21 */ ++ ++#ifdef NET_21 ++ ixs->iph = ixs->skb->nh.iph; ++#else /* NET_21 */ ++ ixs->iph = ixs->skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ /* sanity check for IP version as we can't handle IPv6 right now */ ++ if (ixs->iph->version != 4) { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_xmit_sanity_check_skb: " ++ "found IP Version %d but cannot process other IP versions than v4.\n", ++ ixs->iph->version); /* XXX */ ++ ixs->stats->tx_dropped++; ++ return IPSEC_XMIT_NOIPV6; ++ } ++ ++#if IPSEC_DISALLOW_IPOPTIONS ++ if ((ixs->iph->ihl << 2) != sizeof (struct iphdr)) { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_xmit_sanity_check_skb: " ++ "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */ ++ ixs->stats->tx_dropped++; ++ return IPSEC_XMIT_NOIPOPTIONS; ++ } ++#endif /* IPSEC_DISALLOW_IPOPTIONS */ ++ ++#ifndef NET_21 ++ if (ixs->iph->ttl <= 0) { ++ /* Tell the sender its packet died... */ ++ ICMP_SEND(ixs->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, ixs->physdev); ++ ++ KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: " ++ "TTL=0, too many hops!\n"); ++ ixs->stats->tx_dropped++; ++ return IPSEC_XMIT_TTLEXPIRED; ++ } ++#endif /* !NET_21 */ ++ ++ return IPSEC_XMIT_OK; ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_encap_once(struct ipsec_xmit_state *ixs) ++{ ++#ifdef CONFIG_IPSEC_ESP ++ struct esphdr *espp; ++#ifdef CONFIG_IPSEC_ENC_3DES ++ __u32 iv[ESP_IV_MAXSZ_INT]; ++#endif /* !CONFIG_IPSEC_ENC_3DES */ ++ unsigned char *idat, *pad; ++ int authlen = 0, padlen = 0, i; ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_AH ++ struct iphdr ipo; ++ struct ahhdr *ahp; ++#endif /* CONFIG_IPSEC_AH */ ++#if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) ++ union { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ MD5_CTX md5; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ SHA1_CTX sha1; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ } tctx; ++ __u8 hash[AH_AMAX]; ++#endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */ ++ int headroom = 0, tailroom = 0, ilen = 0, len = 0; ++ unsigned char *dat; ++ int blocksize = 8; /* XXX: should be inside ixs --jjo */ ++#ifdef CONFIG_IPSEC_ALG ++ struct ipsec_alg_enc *ixt_e = NULL; ++ struct ipsec_alg_auth *ixt_a = NULL; ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ ixs->iphlen = ixs->iph->ihl << 2; ++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; ++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF); ++ KLIPS_PRINT(debug_tunnel & DB_TN_OXFS, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "calling output for <%s%s%s>, SA:%s\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ++ switch(ixs->ipsp->ips_said.proto) { ++#ifdef CONFIG_IPSEC_AH ++ case IPPROTO_AH: ++ headroom += sizeof(struct ahhdr); ++ break; ++#endif /* CONFIG_IPSEC_AH */ ++#ifdef CONFIG_IPSEC_ESP ++ case IPPROTO_ESP: ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) { ++ blocksize = ixt_e->ixt_blocksize; ++ headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_encalg) { ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++ headroom += sizeof(struct esphdr); ++ break; ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) { ++ tailroom += AHHMAC_HASHLEN; ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ authlen = AHHMAC_HASHLEN; ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ authlen = AHHMAC_HASHLEN; ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ break; ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++#ifdef CONFIG_IPSEC_ALG ++ tailroom += blocksize != 1 ? ++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : ++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; ++#else ++ tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; ++#endif /* CONFIG_IPSEC_ALG */ ++ tailroom += authlen; ++ break; ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_IPIP ++ case IPPROTO_IPIP: ++ headroom += sizeof(struct iphdr); ++ ixs->iphlen = sizeof(struct iphdr); ++ break; ++#endif /* !CONFIG_IPSEC_IPIP */ ++#ifdef CONFIG_IPSEC_IPCOMP ++ case IPPROTO_COMP: ++ break; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_BADPROTO; ++ } ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "pushing %d bytes, putting %d, proto %d.\n", ++ headroom, tailroom, ixs->ipsp->ips_said.proto); ++ if(skb_headroom(ixs->skb) < headroom) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_xmit_encap_once: " ++ "tried to skb_push headroom=%d, %d available. This should never happen, please report.\n", ++ headroom, skb_headroom(ixs->skb)); ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_PUSHPULLERR; ++ } ++ dat = skb_push(ixs->skb, headroom); ++ ilen = ixs->skb->len - tailroom; ++ if(skb_tailroom(ixs->skb) < tailroom) { ++ printk(KERN_WARNING ++ "klips_error:ipsec_xmit_encap_once: " ++ "tried to skb_put %d, %d available. This should never happen, please report.\n", ++ tailroom, skb_tailroom(ixs->skb)); ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_PUSHPULLERR; ++ } ++ skb_put(ixs->skb, tailroom); ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "head,tailroom: %d,%d before xform.\n", ++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); ++ len = ixs->skb->len; ++ if(len > 0xfff0) { ++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: " ++ "tot_len (%d) > 65520. This should never happen, please report.\n", ++ len); ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_BADLEN; ++ } ++ memmove((void *)dat, (void *)(dat + headroom), ixs->iphlen); ++ ixs->iph = (struct iphdr *)dat; ++ ixs->iph->tot_len = htons(ixs->skb->len); ++ ++ switch(ixs->ipsp->ips_said.proto) { ++#ifdef CONFIG_IPSEC_ESP ++ case IPPROTO_ESP: ++ espp = (struct esphdr *)(dat + ixs->iphlen); ++ espp->esp_spi = ixs->ipsp->ips_said.spi; ++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (!ixt_e) ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_encalg) { ++#if defined(CONFIG_IPSEC_ENC_3DES) ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ iv[0] = *((__u32*)&(espp->esp_iv) ) = ++ ((__u32*)(ixs->ipsp->ips_iv))[0]; ++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) = ++ ((__u32*)(ixs->ipsp->ips_iv))[1]; ++ break; ++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++ idat = dat + ixs->iphlen + headroom; ++ ilen = len - (ixs->iphlen + headroom + authlen); ++ ++ /* Self-describing padding */ ++ pad = &dat[len - tailroom]; ++ padlen = tailroom - 2 - authlen; ++ for (i = 0; i < padlen; i++) { ++ pad[i] = i + 1; ++ } ++ dat[len - authlen - 2] = padlen; ++ ++ dat[len - authlen - 1] = ixs->iph->protocol; ++ ixs->iph->protocol = IPPROTO_ESP; ++ ++#ifdef CONFIG_IPSEC_ALG ++ /* Do all operations here: ++ * copy IV->ESP, encrypt, update ips IV ++ */ ++ if (ixt_e) { ++ int ret; ++ memcpy(espp->esp_iv, ++ ixs->ipsp->ips_iv, ++ ixt_e->ixt_ivlen/8); ++ ret=ipsec_alg_esp_encrypt(ixs->ipsp, ++ idat, ilen, espp->esp_iv, ++ IPSEC_ALG_ENCRYPT); ++ memcpy(ixs->ipsp->ips_iv, ++ idat + ilen - ixt_e->ixt_ivlen/8, ++ ixt_e->ixt_ivlen/8); ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_encalg) { ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++ des_ede3_cbc_encrypt((des_cblock *)idat, ++ (des_cblock *)idat, ++ ilen, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks, ++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks, ++ (des_cblock *)iv, 1); ++ break; ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (!ixt_e) ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_encalg) { ++#if defined(CONFIG_IPSEC_ENC_3DES) ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ /* XXX update IV with the last 8 octets of the encryption */ ++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK ++ ((__u32*)(ixs->ipsp->ips_iv))[0] = ++ ((__u32 *)(idat))[(ilen >> 2) - 2]; ++ ((__u32*)(ixs->ipsp->ips_iv))[1] = ++ ((__u32 *)(idat))[(ilen >> 2) - 1]; ++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ ++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ); ++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ ++ break; ++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_ESP_BADALG; ++ } ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (ixt_a) { ++ ipsec_alg_sa_esp_hash(ixs->ipsp, ++ (caddr_t)espp, len - ixs->iphlen - authlen, ++ &(dat[len - authlen]), authlen); ++ ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ dmp("espp", (char*)espp, len - ixs->iphlen - authlen); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (caddr_t)espp, len - ixs->iphlen - authlen); ++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ dmp("ictx hash", (char*)&hash, sizeof(hash)); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, hash, AHMD596_ALEN); ++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ dmp("octx hash", (char*)&hash, sizeof(hash)); ++ memcpy(&(dat[len - authlen]), hash, authlen); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ SHA1Update(&tctx.sha1, (caddr_t)espp, len - ixs->iphlen - authlen); ++ SHA1Final(hash, &tctx.sha1); ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); ++ SHA1Final(hash, &tctx.sha1); ++ memcpy(&(dat[len - authlen]), hash, authlen); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ break; ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_AH_BADALG; ++ } ++#ifdef NET_21 ++ ixs->skb->h.raw = (unsigned char*)espp; ++#endif /* NET_21 */ ++ break; ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_AH ++ case IPPROTO_AH: ++ ahp = (struct ahhdr *)(dat + ixs->iphlen); ++ ahp->ah_spi = ixs->ipsp->ips_said.spi; ++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); ++ ahp->ah_rv = 0; ++ ahp->ah_nh = ixs->iph->protocol; ++ ahp->ah_hl = (headroom >> 2) - sizeof(__u64)/sizeof(__u32); ++ ixs->iph->protocol = IPPROTO_AH; ++ dmp("ahp", (char*)ahp, sizeof(*ahp)); ++ ++ ipo = *ixs->iph; ++ ipo.tos = 0; ++ ipo.frag_off = 0; ++ ipo.ttl = 0; ++ ipo.check = 0; ++ dmp("ipo", (char*)&ipo, sizeof(ipo)); ++ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr)); ++ dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); ++ dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN); ++ dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); ++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ dmp("ictx hash", (char*)&hash, sizeof(hash)); ++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Update(&tctx.md5, hash, AHMD596_ALEN); ++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); ++ MD5Final(hash, &tctx.md5); ++ dmp("octx hash", (char*)&hash, sizeof(hash)); ++ ++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; ++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr)); ++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); ++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN); ++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); ++ SHA1Final(hash, &tctx.sha1); ++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; ++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); ++ SHA1Final(hash, &tctx.sha1); ++ ++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); ++ ++ /* paranoid */ ++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); ++ memset((caddr_t)hash, 0, sizeof(*hash)); ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_AH_BADALG; ++ } ++#ifdef NET_21 ++ ixs->skb->h.raw = (unsigned char*)ahp; ++#endif /* NET_21 */ ++ break; ++#endif /* CONFIG_IPSEC_AH */ ++#ifdef CONFIG_IPSEC_IPIP ++ case IPPROTO_IPIP: ++ ixs->iph->version = 4; ++ switch(sysctl_ipsec_tos) { ++ case 0: ++#ifdef NET_21 ++ ixs->iph->tos = ixs->skb->nh.iph->tos; ++#else /* NET_21 */ ++ ixs->iph->tos = ixs->skb->ip_hdr->tos; ++#endif /* NET_21 */ ++ break; ++ case 1: ++ ixs->iph->tos = 0; ++ break; ++ default: ++ break; ++ } ++#ifdef NET_21 ++#ifdef NETDEV_23 ++ ixs->iph->ttl = sysctl_ip_default_ttl; ++#else /* NETDEV_23 */ ++ ixs->iph->ttl = ip_statistics.IpDefaultTTL; ++#endif /* NETDEV_23 */ ++#else /* NET_21 */ ++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */ ++#endif /* NET_21 */ ++ ixs->iph->frag_off = 0; ++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr; ++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr; ++ ixs->iph->protocol = IPPROTO_IPIP; ++ ixs->iph->ihl = sizeof(struct iphdr) >> 2; ++ ++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb); ++ ++ ixs->newdst = (__u32)ixs->iph->daddr; ++ ixs->newsrc = (__u32)ixs->iph->saddr; ++ ++#ifdef NET_21 ++ ixs->skb->h.ipiph = ixs->skb->nh.iph; ++#endif /* NET_21 */ ++ break; ++#endif /* !CONFIG_IPSEC_IPIP */ ++#ifdef CONFIG_IPSEC_IPCOMP ++ case IPPROTO_COMP: ++ { ++ unsigned int flags = 0; ++#ifdef CONFIG_IPSEC_DEBUG ++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len); ++ ++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags); ++ ++#ifdef NET_21 ++ ixs->iph = ixs->skb->nh.iph; ++#else /* NET_21 */ ++ ixs->iph = ixs->skb->ip_hdr; ++#endif /* NET_21 */ ++ ++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len); ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_tunnel & DB_TN_CROUT) ++ { ++ if (old_tot_len > ntohs(ixs->iph->tot_len)) ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n", ++ old_tot_len, ntohs(ixs->iph->tot_len), ++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi), ++ ntohl(ixs->ipsp->ips_said.spi), ++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff)); ++ else ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "packet did not compress (flags = %d).\n", ++ flags); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ } ++ break; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ default: ++ ixs->stats->tx_errors++; ++ return IPSEC_XMIT_BADPROTO; ++ } ++ ++#ifdef NET_21 ++ ixs->skb->nh.raw = ixs->skb->data; ++#else /* NET_21 */ ++ ixs->skb->ip_hdr = ixs->skb->h.iph = (struct iphdr *) ixs->skb->data; ++#endif /* NET_21 */ ++ ixs->iph->check = 0; ++ ixs->iph->check = ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl); ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_once: " ++ "after <%s%s%s>, SA:%s:\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph); ++ ++ ixs->ipsp->ips_life.ipl_bytes.ipl_count += len; ++ ixs->ipsp->ips_life.ipl_bytes.ipl_last = len; ++ ++ if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count) { ++ ixs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; ++ } ++ ixs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; ++ ixs->ipsp->ips_life.ipl_packets.ipl_count++; ++ ++ ixs->ipsp = ixs->ipsp->ips_onext; ++ ++ return IPSEC_XMIT_OK; ++} ++ ++/* ++ * If the IP packet (iph) is a carrying TCP/UDP, then set the encaps ++ * source and destination ports to those from the TCP/UDP header. ++ */ ++void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er) ++{ ++ struct udphdr *udp; ++ ++ switch (iph->protocol) { ++ case IPPROTO_UDP: ++ case IPPROTO_TCP: ++ /* ++ * The ports are at the same offsets in a TCP and UDP ++ * header so hack it ... ++ */ ++ udp = (struct udphdr*)(((char*)iph)+(iph->ihl<<2)); ++ er->sen_sport = udp->source; ++ er->sen_dport = udp->dest; ++ break; ++ default: ++ er->sen_sport = 0; ++ er->sen_dport = 0; ++ break; ++ } ++} ++ ++/* ++ * A TRAP eroute is installed and we want to replace it with a HOLD ++ * eroute. ++ */ ++static int create_hold_eroute(struct eroute *origtrap, ++ struct sk_buff * skb, struct iphdr * iph, ++ uint32_t eroute_pid) ++{ ++ struct eroute hold_eroute; ++ ip_said hold_said; ++ struct sk_buff *first, *last; ++ int error; ++ ++ first = last = NULL; ++ memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute)); ++ memset((caddr_t)&hold_said, 0, sizeof(hold_said)); ++ ++ hold_said.proto = IPPROTO_INT; ++ hold_said.spi = htonl(SPI_HOLD); ++ hold_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY; ++ ++ hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap); ++ hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap); ++ hold_eroute.er_eaddr.sen_family = AF_ENCAP; ++ hold_eroute.er_emask.sen_family = AF_ENCAP; ++ hold_eroute.er_eaddr.sen_type = SENT_IP4; ++ hold_eroute.er_emask.sen_type = 255; ++ ++ hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr; ++ hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr; ++ hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST; ++ hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST; ++ hold_eroute.er_emask.sen_sport = 0; ++ hold_eroute.er_emask.sen_dport = 0; ++ hold_eroute.er_pid = eroute_pid; ++ hold_eroute.er_count = 0; ++ hold_eroute.er_lasttime = jiffies/HZ; ++ ++ /* ++ * if it wasn't captured by a wildcard, then don't record it as ++ * a wildcard. ++ */ ++ if(origtrap->er_eaddr.sen_proto != 0) { ++ hold_eroute.er_eaddr.sen_proto = iph->protocol; ++ ++ if((iph->protocol == IPPROTO_TCP || ++ iph->protocol == IPPROTO_UDP) && ++ (origtrap->er_eaddr.sen_sport != 0 || ++ origtrap->er_eaddr.sen_dport != 0)) { ++ ++ if(origtrap->er_eaddr.sen_sport != 0) ++ hold_eroute.er_emask.sen_sport = ~0; ++ ++ if(origtrap->er_eaddr.sen_dport != 0) ++ hold_eroute.er_emask.sen_dport = ~0; ++ ++ ipsec_extract_ports(iph, &hold_eroute.er_eaddr); ++ } ++ } ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_pfkey) { ++ char buf1[64], buf2[64]; ++ subnettoa(hold_eroute.er_eaddr.sen_ip_src, ++ hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(hold_eroute.er_eaddr.sen_ip_dst, ++ hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "calling breakeroute and makeroute for %s:%d->%s:%d %d HOLD eroute.\n", ++ buf1, ntohs(hold_eroute.er_eaddr.sen_sport), ++ buf2, ntohs(hold_eroute.er_eaddr.sen_dport), ++ hold_eroute.er_eaddr.sen_proto); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ if (ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask), ++ &first, &last)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "HOLD breakeroute found nothing.\n"); ++ } else { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "HOLD breakroute deleted %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u %u\n", ++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_src), ++ ntohs(hold_eroute.er_eaddr.sen_sport), ++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_dst), ++ ntohs(hold_eroute.er_eaddr.sen_dport), ++ hold_eroute.er_eaddr.sen_proto); ++ } ++ if (first != NULL) ++ kfree_skb(first); ++ if (last != NULL) ++ kfree_skb(last); ++ ++ error = ipsec_makeroute(&(hold_eroute.er_eaddr), ++ &(hold_eroute.er_emask), ++ hold_said, eroute_pid, skb, NULL, NULL); ++ if (error) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "HOLD makeroute returned %d, failed.\n", error); ++ } else { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "HOLD makeroute call successful.\n"); ++ } ++ return (error == 0); ++} ++ ++enum ipsec_xmit_value ++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs) ++{ ++#ifdef CONFIG_IPSEC_ALG ++ struct ipsec_alg_enc *ixt_e = NULL; ++ struct ipsec_alg_auth *ixt_a = NULL; ++ int blocksize = 8; ++#endif /* CONFIG_IPSEC_ALG */ ++ enum ipsec_xmit_value bundle_stat = IPSEC_XMIT_OK; ++ ++ ixs->newdst = ixs->orgdst = ixs->iph->daddr; ++ ixs->newsrc = ixs->orgsrc = ixs->iph->saddr; ++ ixs->orgedst = ixs->outgoing_said.dst.u.v4.sin_addr.s_addr; ++ ixs->iphlen = ixs->iph->ihl << 2; ++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; ++ ixs->max_headroom = ixs->max_tailroom = 0; ++ ++ if (ixs->outgoing_said.proto == IPPROTO_INT) { ++ switch (ntohl(ixs->outgoing_said.spi)) { ++ case SPI_DROP: ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "shunt SA of DROP or no eroute: dropping.\n"); ++ ixs->stats->tx_dropped++; ++ break; ++ ++ case SPI_REJECT: ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "shunt SA of REJECT: notifying and dropping.\n"); ++ ICMP_SEND(ixs->skb, ++ ICMP_DEST_UNREACH, ++ ICMP_PKT_FILTERED, ++ 0, ++ ixs->physdev); ++ ixs->stats->tx_dropped++; ++ break; ++ ++ case SPI_PASS: ++#ifdef NET_21 ++ ixs->pass = 1; ++#endif /* NET_21 */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "PASS: calling dev_queue_xmit\n"); ++ return IPSEC_XMIT_PASS; ++ goto cleanup; ++ ++ case SPI_HOLD: ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "shunt SA of HOLD: this does not make sense here, dropping.\n"); ++ ixs->stats->tx_dropped++; ++ break; ++ ++ case SPI_TRAP: ++ case SPI_TRAPSUBNET: ++ { ++ struct sockaddr_in src, dst; ++#ifdef CONFIG_IPSEC_DEBUG ++ char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF]; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ /* Signal all listening KMds with a PF_KEY ACQUIRE */ ++ ++ src.sin_family = AF_INET; ++ dst.sin_family = AF_INET; ++ src.sin_addr.s_addr = ixs->iph->saddr; ++ dst.sin_addr.s_addr = ixs->iph->daddr; ++ ++ ixs->ips.ips_transport_protocol = 0; ++ src.sin_port = 0; ++ dst.sin_port = 0; ++ { ++ int i; ++ for(i = 0; ++ i < sizeof(struct sockaddr_in) ++ - offsetof(struct sockaddr_in, sin_zero); ++ i++) { ++ src.sin_zero[i] = 0; ++ dst.sin_zero[i] = 0; ++ } ++ } ++ ++ if(ixs->eroute->er_eaddr.sen_proto != 0) { ++ ixs->ips.ips_transport_protocol = ixs->iph->protocol; ++ ++ if(ixs->eroute->er_eaddr.sen_sport != 0) { ++ src.sin_port = ++ (ixs->iph->protocol == IPPROTO_UDP ++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->source ++ : (ixs->iph->protocol == IPPROTO_TCP ++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->source ++ : 0)); ++ } ++ if(ixs->eroute->er_eaddr.sen_dport != 0) { ++ dst.sin_port = ++ (ixs->iph->protocol == IPPROTO_UDP ++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->dest ++ : (ixs->iph->protocol == IPPROTO_TCP ++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->dest ++ : 0)); ++ } ++ } ++ ++ ixs->ips.ips_addr_s = (struct sockaddr*)(&src); ++ ixs->ips.ips_addr_d = (struct sockaddr*)(&dst); ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n", ++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR", ++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_port), ++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR", ++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_port), ++ ixs->ips.ips_said.proto); ++ ++ /* increment count of total traps needed */ ++ ipsec_xmit_trap_count++; ++ ++ if (pfkey_acquire(&ixs->ips) == 0) { ++ ++ /* note that we succeeded */ ++ ipsec_xmit_trap_sendcount++; ++ ++ if (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)) { ++ /* ++ * The spinlock is to prevent any other ++ * process from accessing or deleting ++ * the eroute while we are using and ++ * updating it. ++ */ ++ spin_lock(&eroute_lock); ++ ixs->eroute = ipsec_findroute(&ixs->matcher); ++ if(ixs->eroute) { ++ ixs->eroute->er_said.spi = htonl(SPI_HOLD); ++ ixs->eroute->er_first = ixs->skb; ++ ixs->skb = NULL; ++ } ++ spin_unlock(&eroute_lock); ++ } else if (create_hold_eroute(ixs->eroute, ++ ixs->skb, ++ ixs->iph, ++ ixs->eroute_pid)) { ++ ixs->skb = NULL; ++ } ++ /* whether or not the above succeeded, we continue */ ++ ++ } ++ ixs->stats->tx_dropped++; ++ } ++ default: ++ /* XXX what do we do with an unknown shunt spi? */ ++ break; ++ } /* switch (ntohl(ixs->outgoing_said.spi)) */ ++ return IPSEC_XMIT_STOLEN; ++ } /* if (ixs->outgoing_said.proto == IPPROTO_INT) */ ++ ++ /* ++ The spinlock is to prevent any other process from ++ accessing or deleting the ipsec_sa hash table or any of the ++ ipsec_sa s while we are using and updating them. ++ ++ This is not optimal, but was relatively straightforward ++ at the time. A better way to do it has been planned for ++ more than a year, to lock the hash table and put reference ++ counts on each ipsec_sa instead. This is not likely to happen ++ in KLIPS1 unless a volunteer contributes it, but will be ++ designed into KLIPS2. ++ */ ++ spin_lock(&tdb_lock); ++ ++ ixs->ipsp = ipsec_sa_getbyid(&ixs->outgoing_said); ++ ixs->sa_len = satot(&ixs->outgoing_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt)); ++ ++ if (ixs->ipsp == NULL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "no ipsec_sa for SA%s: outgoing packet with no SA, dropped.\n", ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ixs->stats->tx_dropped++; ++ bundle_stat = IPSEC_XMIT_SAIDNOTFOUND; ++ goto cleanup; ++ } ++ ++ ipsec_sa_put(ixs->ipsp); /* incomplete */ ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "found ipsec_sa -- SA:<%s%s%s> %s\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ++ /* ++ * How much headroom do we need to be able to apply ++ * all the grouped transforms? ++ */ ++ ixs->ipsq = ixs->ipsp; /* save the head of the ipsec_sa chain */ ++ while (ixs->ipsp) { ++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt)); ++ if(ixs->sa_len == 0) { ++ strcpy(ixs->sa_txt, "(error)"); ++ } ++ ++ /* If it is in larval state, drop the packet, we cannot process yet. */ ++ if(ixs->ipsp->ips_state == SADB_SASTATE_LARVAL) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "ipsec_sa in larval state for SA:<%s%s%s> %s, cannot be used yet, dropping packet.\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; ++ goto cleanup; ++ } ++ ++ if(ixs->ipsp->ips_state == SADB_SASTATE_DEAD) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "ipsec_sa in dead state for SA:<%s%s%s> %s, can no longer be used, dropping packet.\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; ++ goto cleanup; ++ } ++ ++ /* If the replay window counter == -1, expire SA, it will roll */ ++ if(ixs->ipsp->ips_replaywin && ixs->ipsp->ips_replaywin_lastseq == -1) { ++ pfkey_expire(ixs->ipsp, 1); ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "replay window counter rolled for SA:<%s%s%s> %s, packet dropped, expiring SA.\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ ipsec_sa_delchain(ixs->ipsp); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_REPLAYROLLED; ++ goto cleanup; ++ } ++ ++ /* ++ * if this is the first time we are using this SA, mark start time, ++ * and offset hard/soft counters by "now" for later checking. ++ */ ++#if 0 ++ if(ixs->ipsp->ips_life.ipl_usetime.count == 0) { ++ ixs->ipsp->ips_life.ipl_usetime.count = jiffies; ++ ixs->ipsp->ips_life.ipl_usetime.hard += jiffies; ++ ixs->ipsp->ips_life.ipl_usetime.soft += jiffies; ++ } ++#endif ++ ++ ++ if(ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_bytes, "bytes", ixs->sa_txt, ++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_addtime, "addtime",ixs->sa_txt, ++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_usetime, "usetime",ixs->sa_txt, ++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_packets, "packets",ixs->sa_txt, ++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied) { ++ ++ ipsec_sa_delchain(ixs->ipsp); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_LIFETIMEFAILED; ++ goto cleanup; ++ } ++ ++ ++ ixs->headroom = ixs->tailroom = 0; ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "calling room for <%s%s%s>, SA:%s\n", ++ IPS_XFORM_NAME(ixs->ipsp), ++ ixs->sa_len ? ixs->sa_txt : " (error)"); ++ switch(ixs->ipsp->ips_said.proto) { ++#ifdef CONFIG_IPSEC_AH ++ case IPPROTO_AH: ++ ixs->headroom += sizeof(struct ahhdr); ++ break; ++#endif /* CONFIG_IPSEC_AH */ ++#ifdef CONFIG_IPSEC_ESP ++ case IPPROTO_ESP: ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) { ++ blocksize = ixt_e->ixt_blocksize; ++ ixs->headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_encalg) { ++#ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++ ixs->headroom += sizeof(struct esphdr); ++ break; ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ default: ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_ESP_BADALG; ++ goto cleanup; ++ } ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) { ++ ixs->tailroom += AHHMAC_HASHLEN; ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ixs->ipsp->ips_authalg) { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: ++ ixs->tailroom += AHHMAC_HASHLEN; ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: ++ ixs->tailroom += AHHMAC_HASHLEN; ++ break; ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ break; ++ default: ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_AH_BADALG; ++ goto cleanup; ++ } ++#ifdef CONFIG_IPSEC_ALG ++ ixs->tailroom += blocksize != 1 ? ++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : ++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; ++#else ++ ixs->tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; ++#endif /* CONFIG_IPSEC_ALG */ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if ((ixs->ipsp->ips_natt_type) && (!ixs->natt_type)) { ++ ixs->natt_type = ixs->ipsp->ips_natt_type; ++ ixs->natt_sport = ixs->ipsp->ips_natt_sport; ++ ixs->natt_dport = ixs->ipsp->ips_natt_dport; ++ switch (ixs->natt_type) { ++ case ESPINUDP_WITH_NON_IKE: ++ ixs->natt_head = sizeof(struct udphdr)+(2*sizeof(__u32)); ++ break; ++ ++ case ESPINUDP_WITH_NON_ESP: ++ ixs->natt_head = sizeof(struct udphdr); ++ break; ++ ++ default: ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT ++ , "klips_xmit: invalid nat-t type %d" ++ , ixs->natt_type); ++ bundle_stat = IPSEC_XMIT_ESPUDP_BADTYPE; ++ goto cleanup; ++ ++ break; ++ } ++ ixs->tailroom += ixs->natt_head; ++ } ++#endif ++ break; ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_IPIP ++ case IPPROTO_IPIP: ++ ixs->headroom += sizeof(struct iphdr); ++ break; ++#endif /* !CONFIG_IPSEC_IPIP */ ++ case IPPROTO_COMP: ++#ifdef CONFIG_IPSEC_IPCOMP ++ /* ++ We can't predict how much the packet will ++ shrink without doing the actual compression. ++ We could do it here, if we were the first ++ encapsulation in the chain. That might save ++ us a skb_copy_expand, since we might fit ++ into the existing skb then. However, this ++ would be a bit unclean (and this hack has ++ bit us once), so we better not do it. After ++ all, the skb_copy_expand is cheap in ++ comparison to the actual compression. ++ At least we know the packet will not grow. ++ */ ++ break; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ default: ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_BADPROTO; ++ goto cleanup; ++ } ++ ixs->ipsp = ixs->ipsp->ips_onext; ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "Required head,tailroom: %d,%d\n", ++ ixs->headroom, ixs->tailroom); ++ ixs->max_headroom += ixs->headroom; ++ ixs->max_tailroom += ixs->tailroom; ++ ixs->pyldsz += (ixs->headroom + ixs->tailroom); ++ } ++ ixs->ipsp = ixs->ipsq; /* restore the head of the ipsec_sa chain */ ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "existing head,tailroom: %d,%d before applying xforms with head,tailroom: %d,%d .\n", ++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb), ++ ixs->max_headroom, ixs->max_tailroom); ++ ++ ixs->tot_headroom += ixs->max_headroom; ++ ixs->tot_tailroom += ixs->max_tailroom; ++ ++ ixs->mtudiff = ixs->prv->mtu + ixs->tot_headroom + ixs->tot_tailroom - ixs->physmtu; ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "mtu:%d physmtu:%d tothr:%d tottr:%d mtudiff:%d ippkttotlen:%d\n", ++ ixs->prv->mtu, ixs->physmtu, ++ ixs->tot_headroom, ixs->tot_tailroom, ixs->mtudiff, ntohs(ixs->iph->tot_len)); ++ if(ixs->mtudiff > 0) { ++ int newmtu = ixs->physmtu - (ixs->tot_headroom + ((ixs->tot_tailroom + 2) & ~7) + 5); ++ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_info:ipsec_xmit_encap_bundle: " ++ "dev %s mtu of %d decreased by %d to %d\n", ++ ixs->dev->name, ++ ixs->prv->mtu, ++ ixs->prv->mtu - newmtu, ++ newmtu); ++ ixs->prv->mtu = newmtu; ++#ifdef NET_21 ++#if 0 ++ ixs->skb->dst->pmtu = ixs->prv->mtu; /* RGB */ ++#endif /* 0 */ ++#else /* NET_21 */ ++#if 0 ++ ixs->dev->mtu = ixs->prv->mtu; /* RGB */ ++#endif /* 0 */ ++#endif /* NET_21 */ ++ } ++ ++ /* ++ If the sender is doing PMTU discovery, and the ++ packet doesn't fit within ixs->prv->mtu, notify him ++ (unless it was an ICMP packet, or it was not the ++ zero-offset packet) and send it anyways. ++ ++ Note: buggy firewall configuration may prevent the ++ ICMP packet from getting back. ++ */ ++ if(sysctl_ipsec_icmp ++ && ixs->prv->mtu < ntohs(ixs->iph->tot_len) ++ && (ixs->iph->frag_off & __constant_htons(IP_DF)) ) { ++ int notify = ixs->iph->protocol != IPPROTO_ICMP ++ && (ixs->iph->frag_off & __constant_htons(IP_OFFSET)) == 0; ++ ++#ifdef IPSEC_obey_DF ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "fragmentation needed and DF set; %sdropping packet\n", ++ notify ? "sending ICMP and " : ""); ++ if (notify) ++ ICMP_SEND(ixs->skb, ++ ICMP_DEST_UNREACH, ++ ICMP_FRAG_NEEDED, ++ ixs->prv->mtu, ++ ixs->physdev); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_CANNOTFRAG; ++ goto cleanup; ++#else /* IPSEC_obey_DF */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "fragmentation needed and DF set; %spassing packet\n", ++ notify ? "sending ICMP and " : ""); ++ if (notify) ++ ICMP_SEND(ixs->skb, ++ ICMP_DEST_UNREACH, ++ ICMP_FRAG_NEEDED, ++ ixs->prv->mtu, ++ ixs->physdev); ++#endif /* IPSEC_obey_DF */ ++ } ++ ++#ifdef MSS_HACK ++ /* ++ * If this is a transport mode TCP packet with ++ * SYN set, determine an effective MSS based on ++ * AH/ESP overheads determined above. ++ */ ++ if (ixs->iph->protocol == IPPROTO_TCP ++ && ixs->outgoing_said.proto != IPPROTO_IPIP) { ++ struct tcphdr *tcph = ixs->skb->h.th; ++ if (tcph->syn && !tcph->ack) { ++ if(!ipsec_adjust_mss(ixs->skb, tcph, ixs->prv->mtu)) { ++ printk(KERN_WARNING ++ "klips_warning:ipsec_xmit_encap_bundle: " ++ "ipsec_adjust_mss() failed\n"); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_MSSERR; ++ goto cleanup; ++ } ++ } ++ } ++#endif /* MSS_HACK */ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if ((ixs->natt_type) && (ixs->outgoing_said.proto != IPPROTO_IPIP)) { ++ /** ++ * NAT-Traversal and Transport Mode: ++ * we need to correct TCP/UDP checksum ++ * ++ * If we've got NAT-OA, we can fix checksum without recalculation. ++ * If we don't we can zero udp checksum. ++ */ ++ __u32 natt_oa = ixs->ipsp->ips_natt_oa ? ++ ((struct sockaddr_in*)(ixs->ipsp->ips_natt_oa))->sin_addr.s_addr : 0; ++ __u16 pkt_len = ixs->skb->tail - (unsigned char *)ixs->iph; ++ __u16 data_len = pkt_len - (ixs->iph->ihl << 2); ++ switch (ixs->iph->protocol) { ++ case IPPROTO_TCP: ++ if (data_len >= sizeof(struct tcphdr)) { ++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); ++ if (natt_oa) { ++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: " ++ "fix TCP checksum using NAT-OA\n"); ++ tcp->check = csum_fold( ++ csum_partial((unsigned char *)buff, sizeof(buff), ++ tcp->check^0xffff)); ++ } ++ else { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: do not recalc TCP checksum\n"); ++ } ++ } ++ else { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: can't fix TCP checksum\n"); ++ } ++ break; ++ case IPPROTO_UDP: ++ if (data_len >= sizeof(struct udphdr)) { ++ struct udphdr *udp = (struct udphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); ++ if (udp->check == 0) { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: UDP checksum already 0\n"); ++ } ++ else if (natt_oa) { ++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: " ++ "fix UDP checksum using NAT-OA\n"); ++ udp->check = csum_fold( ++ csum_partial((unsigned char *)buff, sizeof(buff), ++ udp->check^0xffff)); ++ } ++ else { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: zero UDP checksum\n"); ++ udp->check = 0; ++ } ++ } ++ else { ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: can't fix UDP checksum\n"); ++ } ++ break; ++ default: ++ KLIPS_PRINT(debug_tunnel, ++ "klips_debug:ipsec_tunnel_start_xmit: " ++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n"); ++ break; ++ } ++ } ++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ ++ ++ if(!ixs->hard_header_stripped) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "allocating %d bytes for hardheader.\n", ++ ixs->hard_header_len); ++ if((ixs->saved_header = kmalloc(ixs->hard_header_len, GFP_ATOMIC)) == NULL) { ++ printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: " ++ "Failed, tried to allocate %d bytes for temp hard_header.\n", ++ ixs->hard_header_len); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_ERRMEMALLOC; ++ goto cleanup; ++ } ++ { ++ int i; ++ for (i = 0; i < ixs->hard_header_len; i++) { ++ ixs->saved_header[i] = ixs->skb->data[i]; ++ } ++ } ++ if(ixs->skb->len < ixs->hard_header_len) { ++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_bundle: " ++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", ++ ixs->hard_header_len, (int)(ixs->skb->len)); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_ESP_PUSHPULLERR; ++ goto cleanup; ++ } ++ skb_pull(ixs->skb, ixs->hard_header_len); ++ ixs->hard_header_stripped = 1; ++ ++/* ixs->iph = (struct iphdr *) (ixs->skb->data); */ ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "head,tailroom: %d,%d after hard_header stripped.\n", ++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); ++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, ixs->iph); ++ } else { ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "hard header already stripped.\n"); ++ } ++ ++ ixs->ll_headroom = (ixs->hard_header_len + 15) & ~15; ++ ++ if ((skb_headroom(ixs->skb) >= ixs->max_headroom + 2 * ixs->ll_headroom) && ++ (skb_tailroom(ixs->skb) >= ixs->max_tailroom) ++#ifndef NET_21 ++ && ixs->skb->free ++#endif /* !NET_21 */ ++ ) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "data fits in existing skb\n"); ++ } else { ++ struct sk_buff* tskb; ++ ++ if(!ixs->oskb) { ++ ixs->oskb = ixs->skb; ++ } ++ ++ tskb = skb_copy_expand(ixs->skb, ++ /* The need for 2 * link layer length here remains unexplained...RGB */ ++ ixs->max_headroom + 2 * ixs->ll_headroom, ++ ixs->max_tailroom, ++ GFP_ATOMIC); ++#ifdef NET_21 ++ if(tskb && ixs->skb->sk) { ++ skb_set_owner_w(tskb, ixs->skb->sk); ++ } ++#endif /* NET_21 */ ++ if(ixs->skb != ixs->oskb) { ++ ipsec_kfree_skb(ixs->skb); ++ } ++ ixs->skb = tskb; ++ if (!ixs->skb) { ++ printk(KERN_WARNING ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "Failed, tried to allocate %d head and %d tailroom\n", ++ ixs->max_headroom, ixs->max_tailroom); ++ ixs->stats->tx_errors++; ++ bundle_stat = IPSEC_XMIT_ERRSKBALLOC; ++ goto cleanup; ++ } ++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, ++ "klips_debug:ipsec_xmit_encap_bundle: " ++ "head,tailroom: %d,%d after allocation\n", ++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); ++ } ++ ++ /* ++ * Apply grouped transforms to packet ++ */ ++ while (ixs->ipsp) { ++ enum ipsec_xmit_value encap_stat = IPSEC_XMIT_OK; ++ ++ encap_stat = ipsec_xmit_encap_once(ixs); ++ if(encap_stat != IPSEC_XMIT_OK) { ++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, ++ "klips_debug:ipsec_xmit_encap_bundle: encap_once failed: %d\n", ++ encap_stat); ++ ++ bundle_stat = IPSEC_XMIT_ENCAPFAIL; ++ goto cleanup; ++ } ++ } ++ /* end encapsulation loop here XXX */ ++ cleanup: ++ spin_unlock(&tdb_lock); ++ return bundle_stat; ++} ++ ++/* ++ * $Log: ipsec_xmit.c,v $ ++ * Revision 1.8 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.7 2004/02/03 03:13:41 mcr ++ * mark invalid encapsulation states. ++ * ++ * Revision 1.6.2.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.6 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.5 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.4.4.2 2003/10/29 01:37:39 mcr ++ * when creating %hold from %trap, only make the %hold as ++ * specific as the %trap was - so if the protocol and ports ++ * were wildcards, then the %hold will be too. ++ * ++ * Revision 1.4.4.1 2003/09/21 13:59:56 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.4 2003/06/20 02:28:10 mcr ++ * misstype of variable name, not detected by module build. ++ * ++ * Revision 1.3 2003/06/20 01:42:21 mcr ++ * added counters to measure how many ACQUIREs we send to pluto, ++ * and how many are successfully sent. ++ * ++ * Revision 1.2 2003/04/03 17:38:35 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * Normalised coding style. ++ * Simplified logic and reduced duplication of code. ++ * ++ * Revision 1.1 2003/02/12 19:31:23 rgb ++ * Refactored from ipsec_tunnel.c ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/pfkey_v2.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,2126 @@ ++/* ++ * @(#) RFC2367 PF_KEYv2 Key management API domain socket I/F ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2.c,v 1.81 2004/04/25 21:23:11 ken Exp $ ++ */ ++ ++/* ++ * Template from /usr/src/linux-2.0.36/net/unix/af_unix.c. ++ * Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c. ++ */ ++ ++#define __NO_VERSION__ ++#include ++#include ++#include ++#include ++ ++#include "openswan/ipsec_param.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* struct socket */ ++#include ++#include ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include ++#include ++#include ++#include /* struct sock */ ++/* #include */ ++#include ++#ifdef CONFIG_PROC_FS ++# include ++#endif /* CONFIG_PROC_FS */ ++ ++#include ++ ++#include ++#ifdef NET_21 ++# include ++# include ++#endif /* NET_21 */ ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++ ++#ifdef CONFIG_IPSEC_DEBUG ++int debug_pfkey = 0; ++extern int sysctl_ipsec_debug_verbose; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++#ifndef SOCKOPS_WRAPPED ++#define SOCKOPS_WRAPPED(name) name ++#endif /* SOCKOPS_WRAPPED */ ++ ++extern struct proto_ops pfkey_ops; ++struct sock *pfkey_sock_list = NULL; ++struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1]; ++ ++struct socket_list *pfkey_open_sockets = NULL; ++struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1]; ++ ++int pfkey_msg_interp(struct sock *, struct sadb_msg *, struct sadb_msg **); ++ ++int ++pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets) ++{ ++ struct socket_list *socket_listp,*prev; ++ ++ if(!socketp) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_socket: " ++ "NULL socketp handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ if(!sockets) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_socket: " ++ "NULL sockets list handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ socket_listp = *sockets; ++ prev = NULL; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_socket: " ++ "removing sock=0p%p\n", ++ socketp); ++ ++ while(socket_listp != NULL) { ++ if(socket_listp->socketp == socketp) { ++ if(prev != NULL) { ++ prev->next = socket_listp->next; ++ } else { ++ *sockets = socket_listp->next; ++ } ++ ++ kfree((void*)socket_listp); ++ ++ break; ++ } ++ prev = socket_listp; ++ socket_listp = socket_listp->next; ++ } ++ ++ return 0; ++} ++ ++int ++pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets) ++{ ++ struct socket_list *socket_listp; ++ ++ if(!socketp) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_socket: " ++ "NULL socketp handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ if(!sockets) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_socket: " ++ "NULL sockets list handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_socket: " ++ "allocating %lu bytes for socketp=0p%p\n", ++ (unsigned long) sizeof(struct socket_list), ++ socketp); ++ ++ if((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_socket: " ++ "memory allocation error.\n"); ++ return -ENOMEM; ++ } ++ ++ socket_listp->socketp = socketp; ++ socket_listp->next = *sockets; ++ *sockets = socket_listp; ++ ++ return 0; ++} ++ ++int ++pfkey_list_remove_supported(struct supported *supported, struct supported_list **supported_list) ++{ ++ struct supported_list *supported_listp = *supported_list, *prev = NULL; ++ ++ if(!supported) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_supported: " ++ "NULL supported handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ if(!supported_list) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_supported: " ++ "NULL supported_list handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_remove_supported: " ++ "removing supported=0p%p\n", ++ supported); ++ ++ while(supported_listp != NULL) { ++ if(supported_listp->supportedp == supported) { ++ if(prev != NULL) { ++ prev->next = supported_listp->next; ++ } else { ++ *supported_list = supported_listp->next; ++ } ++ ++ kfree((void*)supported_listp); ++ ++ break; ++ } ++ prev = supported_listp; ++ supported_listp = supported_listp->next; ++ } ++ ++ return 0; ++} ++ ++int ++pfkey_list_insert_supported(struct supported *supported, struct supported_list **supported_list) ++{ ++ struct supported_list *supported_listp; ++ ++ if(!supported) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_supported: " ++ "NULL supported handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ if(!supported_list) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_supported: " ++ "NULL supported_list handed in, failed.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_supported: " ++ "allocating %lu bytes for incoming, supported=0p%p, supported_list=0p%p\n", ++ (unsigned long) sizeof(struct supported_list), ++ supported, ++ supported_list); ++ ++ supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL); ++ if(supported_listp == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_supported: " ++ "memory allocation error.\n"); ++ return -ENOMEM; ++ } ++ ++ supported_listp->supportedp = supported; ++ supported_listp->next = *supported_list; ++ *supported_list = supported_listp; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_list_insert_supported: " ++ "outgoing, supported=0p%p, supported_list=0p%p\n", ++ supported, ++ supported_list); ++ ++ return 0; ++} ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC void ++pfkey_state_change(struct sock *sk) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_state_change: .\n"); ++ if(!sk->dead) { ++ wake_up_interruptible(sk->sleep); ++ } ++} ++#endif /* !NET_21 */ ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC void ++pfkey_data_ready(struct sock *sk, int len) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_data_ready: " ++ "sk=0p%p len=%d\n", ++ sk, ++ len); ++ if(!sk->dead) { ++ wake_up_interruptible(sk->sleep); ++ sock_wake_async(sk->socket, 1); ++ } ++} ++ ++DEBUG_NO_STATIC void ++pfkey_write_space(struct sock *sk) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_write_space: .\n"); ++ if(!sk->dead) { ++ wake_up_interruptible(sk->sleep); ++ sock_wake_async(sk->socket, 2); ++ } ++} ++#endif /* !NET_21 */ ++ ++DEBUG_NO_STATIC void ++pfkey_insert_socket(struct sock *sk) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_insert_socket: " ++ "sk=0p%p\n", ++ sk); ++ cli(); ++ sk->next=pfkey_sock_list; ++ pfkey_sock_list=sk; ++ sti(); ++} ++ ++DEBUG_NO_STATIC void ++pfkey_remove_socket(struct sock *sk) ++{ ++ struct sock **s; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_remove_socket: .\n"); ++ cli(); ++ s=&pfkey_sock_list; ++ ++ while(*s!=NULL) { ++ if(*s==sk) { ++ *s=sk->next; ++ sk->next=NULL; ++ sti(); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_remove_socket: " ++ "succeeded.\n"); ++ return; ++ } ++ s=&((*s)->next); ++ } ++ sti(); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_remove_socket: " ++ "not found.\n"); ++ return; ++} ++ ++DEBUG_NO_STATIC void ++pfkey_destroy_socket(struct sock *sk) ++{ ++ struct sk_buff *skb; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: .\n"); ++ pfkey_remove_socket(sk); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: " ++ "pfkey_remove_socket called.\n"); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: " ++ "sk(0p%p)->(&0p%p)receive_queue.{next=0p%p,prev=0p%p}.\n", ++ sk, ++ &(sk->receive_queue), ++ sk->receive_queue.next, ++ sk->receive_queue.prev); ++ while(sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) { ++#ifdef NET_21 ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_pfkey && sysctl_ipsec_debug_verbose) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: " ++ "skb=0p%p dequeued.\n", skb); ++ printk(KERN_INFO "klips_debug:pfkey_destroy_socket: " ++ "pfkey_skb contents:"); ++ printk(" next:0p%p", skb->next); ++ printk(" prev:0p%p", skb->prev); ++ printk(" list:0p%p", skb->list); ++ printk(" sk:0p%p", skb->sk); ++ printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec); ++ printk(" dev:0p%p", skb->dev); ++ if(skb->dev) { ++ if(skb->dev->name) { ++ printk(" dev->name:%s", skb->dev->name); ++ } else { ++ printk(" dev->name:NULL?"); ++ } ++ } else { ++ printk(" dev:NULL"); ++ } ++ printk(" h:0p%p", skb->h.raw); ++ printk(" nh:0p%p", skb->nh.raw); ++ printk(" mac:0p%p", skb->mac.raw); ++ printk(" dst:0p%p", skb->dst); ++ if(sysctl_ipsec_debug_verbose) { ++ int i; ++ ++ printk(" cb"); ++ for(i=0; i<48; i++) { ++ printk(":%2x", skb->cb[i]); ++ } ++ } ++ printk(" len:%d", skb->len); ++ printk(" csum:%d", skb->csum); ++#ifndef NETDEV_23 ++ printk(" used:%d", skb->used); ++ printk(" is_clone:%d", skb->is_clone); ++#endif /* NETDEV_23 */ ++ printk(" cloned:%d", skb->cloned); ++ printk(" pkt_type:%d", skb->pkt_type); ++ printk(" ip_summed:%d", skb->ip_summed); ++ printk(" priority:%d", skb->priority); ++ printk(" protocol:%d", skb->protocol); ++ printk(" security:%d", skb->security); ++ printk(" truesize:%d", skb->truesize); ++ printk(" head:0p%p", skb->head); ++ printk(" data:0p%p", skb->data); ++ printk(" tail:0p%p", skb->tail); ++ printk(" end:0p%p", skb->end); ++ if(sysctl_ipsec_debug_verbose) { ++ unsigned char* i; ++ printk(" data"); ++ for(i = skb->head; i < skb->end; i++) { ++ printk(":%2x", (unsigned char)(*(i))); ++ } ++ } ++ printk(" destructor:0p%p", skb->destructor); ++ printk("\n"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++#endif /* NET_21 */ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: " ++ "skb=0p%p freed.\n", ++ skb); ++ ipsec_kfree_skb(skb); ++ } ++ ++ sk->dead = 1; ++ sk_free(sk); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_destroy_socket: destroyed.\n"); ++} ++ ++int ++pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg) ++{ ++ int error = 0; ++ struct sk_buff * skb = NULL; ++ struct sock *sk; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "NULL socket passed in.\n"); ++ return -EINVAL; ++ } ++ ++ if(pfkey_msg == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "NULL pfkey_msg passed in.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef NET_21 ++ sk = sock->sk; ++#else /* NET_21 */ ++ sk = sock->data; ++#endif /* NET_21 */ ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "NULL sock passed in.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "allocating %d bytes...\n", ++ (int)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)); ++ if(!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "no buffers left to send up a message.\n"); ++ return -ENOBUFS; ++ } ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "...allocated at 0p%p.\n", ++ skb); ++ ++ skb->dev = NULL; ++ ++ if(skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) { ++ printk(KERN_WARNING "klips_error:pfkey_upmsg: " ++ "tried to skb_put %ld, %d available. This should never happen, please report.\n", ++ (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, ++ skb_tailroom(skb)); ++ ipsec_kfree_skb(skb); ++ return -ENOBUFS; ++ } ++ skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); ++ memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); ++ ++#ifndef NET_21 ++ skb->free = 1; ++#endif /* !NET_21 */ ++ ++ if((error = sock_queue_rcv_skb(sk, skb)) < 0) { ++ skb->sk=NULL; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_upmsg: " ++ "error=%d calling sock_queue_rcv_skb with skb=0p%p.\n", ++ error, ++ skb); ++ ipsec_kfree_skb(skb); ++ return error; ++ } ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_create(struct socket *sock, int protocol) ++{ ++ struct sock *sk; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "socket NULL.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "sock=0p%p type:%d state:%d flags:%ld protocol:%d\n", ++ sock, ++ sock->type, ++ (unsigned int)(sock->state), ++ sock->flags, protocol); ++ ++ if(sock->type != SOCK_RAW) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "only SOCK_RAW supported.\n"); ++ return -ESOCKTNOSUPPORT; ++ } ++ ++ if(protocol != PF_KEY_V2) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "protocol not PF_KEY_V2.\n"); ++ return -EPROTONOSUPPORT; ++ } ++ ++ if((current->uid != 0)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "must be root to open pfkey sockets.\n"); ++ return -EACCES; ++ } ++ ++#ifdef NET_21 ++ sock->state = SS_UNCONNECTED; ++#endif /* NET_21 */ ++ MOD_INC_USE_COUNT; ++#ifdef NET_21 ++ if((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL) ++#else /* NET_21 */ ++ if((sk=(struct sock *)sk_alloc(GFP_KERNEL)) == NULL) ++#endif /* NET_21 */ ++ { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "Out of memory trying to allocate.\n"); ++ MOD_DEC_USE_COUNT; ++ return -ENOMEM; ++ } ++ ++#ifndef NET_21 ++ memset(sk, 0, sizeof(*sk)); ++#endif /* !NET_21 */ ++ ++#ifdef NET_21 ++ sock_init_data(sock, sk); ++ ++ sk->destruct = NULL; ++ sk->reuse = 1; ++ sock->ops = &pfkey_ops; ++ ++ sk->zapped=0; ++ sk->family = PF_KEY; ++/* sk->num = protocol; */ ++ sk->protocol = protocol; ++ key_pid(sk) = current->pid; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "sock->fasync_list=0p%p sk->sleep=0p%p.\n", ++ sock->fasync_list, ++ sk->sleep); ++#else /* NET_21 */ ++ sk->type=sock->type; ++ init_timer(&sk->timer); ++ skb_queue_head_init(&sk->write_queue); ++ skb_queue_head_init(&sk->receive_queue); ++ skb_queue_head_init(&sk->back_log); ++ sk->rcvbuf=SK_RMEM_MAX; ++ sk->sndbuf=SK_WMEM_MAX; ++ sk->allocation=GFP_KERNEL; ++ sk->state=TCP_CLOSE; ++ sk->priority=SOPRI_NORMAL; ++ sk->state_change=pfkey_state_change; ++ sk->data_ready=pfkey_data_ready; ++ sk->write_space=pfkey_write_space; ++ sk->error_report=pfkey_state_change; ++ sk->mtu=4096; ++ sk->socket=sock; ++ sock->data=(void *)sk; ++ sk->sleep=sock->wait; ++#endif /* NET_21 */ ++ ++ pfkey_insert_socket(sk); ++ pfkey_list_insert_socket(sock, &pfkey_open_sockets); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_create: " ++ "Socket sock=0p%p sk=0p%p initialised.\n", sock, sk); ++ return 0; ++} ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC int ++pfkey_dup(struct socket *newsock, struct socket *oldsock) ++{ ++ struct sock *sk; ++ ++ if(newsock==NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_dup: " ++ "No new socket attached.\n"); ++ return -EINVAL; ++ } ++ ++ if(oldsock==NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_dup: " ++ "No old socket attached.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef NET_21 ++ sk=oldsock->sk; ++#else /* NET_21 */ ++ sk=oldsock->data; ++#endif /* NET_21 */ ++ ++ /* May not have data attached */ ++ if(sk==NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_dup: " ++ "No sock attached to old socket.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_dup: .\n"); ++ ++ return pfkey_create(newsock, sk->protocol); ++} ++#endif /* !NET_21 */ ++ ++DEBUG_NO_STATIC int ++#ifdef NETDEV_23 ++pfkey_release(struct socket *sock) ++#else /* NETDEV_23 */ ++pfkey_release(struct socket *sock, struct socket *peersock) ++#endif /* NETDEV_23 */ ++{ ++ struct sock *sk; ++ int i; ++ ++ if(sock==NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_release: " ++ "No socket attached.\n"); ++ return 0; /* -EINVAL; */ ++ } ++ ++#ifdef NET_21 ++ sk=sock->sk; ++#else /* NET_21 */ ++ sk=sock->data; ++#endif /* NET_21 */ ++ ++ /* May not have data attached */ ++ if(sk==NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_release: " ++ "No sk attached to sock=0p%p.\n", sock); ++ return 0; /* -EINVAL; */ ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_release: " ++ "sock=0p%p sk=0p%p\n", sock, sk); ++ ++#ifdef NET_21 ++ if(!sk->dead) ++#endif /* NET_21 */ ++ if(sk->state_change) { ++ sk->state_change(sk); ++ } ++ ++#ifdef NET_21 ++ sock->sk = NULL; ++#else /* NET_21 */ ++ sock->data = NULL; ++#endif /* NET_21 */ ++ ++ /* Try to flush out this socket. Throw out buffers at least */ ++ pfkey_destroy_socket(sk); ++ pfkey_list_remove_socket(sock, &pfkey_open_sockets); ++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) { ++ pfkey_list_remove_socket(sock, &(pfkey_registered_sockets[i])); ++ } ++ ++ MOD_DEC_USE_COUNT; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_release: " ++ "succeeded.\n"); ++ ++ return 0; ++} ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC int ++pfkey_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_bind: " ++ "operation not supported.\n"); ++ return -EINVAL; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_connect: " ++ "operation not supported.\n"); ++ return -EINVAL; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_socketpair(struct socket *a, struct socket *b) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_socketpair: " ++ "operation not supported.\n"); ++ return -EINVAL; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_accept(struct socket *sock, struct socket *newsock, int flags) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_aaccept: " ++ "operation not supported.\n"); ++ return -EINVAL; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, ++ int peer) ++{ ++ struct sockaddr *ska = (struct sockaddr*)uaddr; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getname: .\n"); ++ ska->sa_family = PF_KEY; ++ *uaddr_len = sizeof(*ska); ++ return 0; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_select(struct socket *sock, int sel_type, select_table *wait) ++{ ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_select: " ++ ".sock=0p%p sk=0p%p sel_type=%d\n", ++ sock, ++ sock->data, ++ sel_type); ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_select: " ++ "Null socket passed in.\n"); ++ return -EINVAL; ++ } ++ return datagram_select(sock->data, sel_type, wait); ++} ++ ++DEBUG_NO_STATIC int ++pfkey_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ioctl: " ++ "not supported.\n"); ++ return -EINVAL; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_listen(struct socket *sock, int backlog) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_listen: " ++ "not supported.\n"); ++ return -EINVAL; ++} ++#endif /* !NET_21 */ ++ ++DEBUG_NO_STATIC int ++pfkey_shutdown(struct socket *sock, int mode) ++{ ++ struct sock *sk; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_shutdown: " ++ "NULL socket passed in.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef NET_21 ++ sk=sock->sk; ++#else /* NET_21 */ ++ sk=sock->data; ++#endif /* NET_21 */ ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_shutdown: " ++ "No sock attached to socket.\n"); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_shutdown: " ++ "mode=%x.\n", mode); ++ mode++; ++ ++ if(mode&SEND_SHUTDOWN) { ++ sk->shutdown|=SEND_SHUTDOWN; ++ sk->state_change(sk); ++ } ++ ++ if(mode&RCV_SHUTDOWN) { ++ sk->shutdown|=RCV_SHUTDOWN; ++ sk->state_change(sk); ++ } ++ return 0; ++} ++ ++#ifndef NET_21 ++DEBUG_NO_STATIC int ++pfkey_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) ++{ ++#ifndef NET_21 ++ struct sock *sk; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_setsockopt: " ++ "Null socket passed in.\n"); ++ return -EINVAL; ++ } ++ ++ sk=sock->data; ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_setsockopt: " ++ "Null sock passed in.\n"); ++ return -EINVAL; ++ } ++#endif /* !NET_21 */ ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_setsockopt: .\n"); ++ if(level!=SOL_SOCKET) { ++ return -EOPNOTSUPP; ++ } ++#ifdef NET_21 ++ return sock_setsockopt(sock, level, optname, optval, optlen); ++#else /* NET_21 */ ++ return sock_setsockopt(sk, level, optname, optval, optlen); ++#endif /* NET_21 */ ++} ++ ++DEBUG_NO_STATIC int ++pfkey_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) ++{ ++#ifndef NET_21 ++ struct sock *sk; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_setsockopt: " ++ "Null socket passed in.\n"); ++ return -EINVAL; ++ } ++ ++ sk=sock->data; ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_setsockopt: " ++ "Null sock passed in.\n"); ++ return -EINVAL; ++ } ++#endif /* !NET_21 */ ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getsockopt: .\n"); ++ if(level!=SOL_SOCKET) { ++ return -EOPNOTSUPP; ++ } ++#ifdef NET_21 ++ return sock_getsockopt(sock, level, optname, optval, optlen); ++#else /* NET_21 */ ++ return sock_getsockopt(sk, level, optname, optval, optlen); ++#endif /* NET_21 */ ++} ++ ++DEBUG_NO_STATIC int ++pfkey_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_fcntl: " ++ "not supported.\n"); ++ return -EINVAL; ++} ++#endif /* !NET_21 */ ++ ++/* ++ * Send PF_KEY data down. ++ */ ++ ++DEBUG_NO_STATIC int ++#ifdef NET_21 ++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) ++#else /* NET_21 */ ++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) ++#endif /* NET_21 */ ++{ ++ struct sock *sk; ++ int error = 0; ++ struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "Null socket passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++#ifdef NET_21 ++ sk = sock->sk; ++#else /* NET_21 */ ++ sk = sock->data; ++#endif /* NET_21 */ ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "Null sock passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(msg == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "Null msghdr passed in.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: .\n"); ++ if(sk->err) { ++ error = sock_error(sk); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "sk->err is non-zero, returns %d.\n", ++ error); ++ SENDERR(-error); ++ } ++ ++ if((current->uid != 0)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "must be root to send messages to pfkey sockets.\n"); ++ SENDERR(EACCES); ++ } ++ ++#ifdef NET_21 ++ if(msg->msg_control) ++#else /* NET_21 */ ++ if(flags || msg->msg_control) ++#endif /* NET_21 */ ++ { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "can't set flags or set msg_control.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(sk->shutdown & SEND_SHUTDOWN) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "shutdown.\n"); ++ send_sig(SIGPIPE, current, 0); ++ SENDERR(EPIPE); ++ } ++ ++ if(len < sizeof(struct sadb_msg)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "bogus msg len of %d, too small.\n", len); ++ SENDERR(EMSGSIZE); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "allocating %d bytes for downward message.\n", ++ len); ++ if((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "memory allocation error.\n"); ++ SENDERR(ENOBUFS); ++ } ++ ++ memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len); ++ ++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { ++ KLIPS_PRINT(1 || debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "not PF_KEY_V2 msg, found %d, should be %d.\n", ++ pfkey_msg->sadb_msg_version, ++ PF_KEY_V2); ++ kfree((void*)pfkey_msg); ++ return -EINVAL; ++ } ++ ++ if(len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "bogus msg len of %d, not %d byte aligned.\n", ++ len, (int)IPSEC_PFKEYv2_ALIGN); ++ SENDERR(EMSGSIZE); ++ } ++ ++#if 0 ++ /* This check is questionable, since a downward message could be ++ the result of an ACQUIRE either from kernel (PID==0) or ++ userspace (some other PID). */ ++ /* check PID */ ++ if(pfkey_msg->sadb_msg_pid != current->pid) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "pid (%d) does not equal sending process pid (%d).\n", ++ pfkey_msg->sadb_msg_pid, current->pid); ++ SENDERR(EINVAL); ++ } ++#endif ++ ++ if(pfkey_msg->sadb_msg_reserved) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "reserved field must be zero, set to %d.\n", ++ pfkey_msg->sadb_msg_reserved); ++ SENDERR(EINVAL); ++ } ++ ++ if((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)){ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "msg type too large or small:%d.\n", ++ pfkey_msg->sadb_msg_type); ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "msg sent for parsing.\n"); ++ ++ if((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) { ++ struct socket_list *pfkey_socketsp; ++ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " ++ "pfkey_msg_parse returns %d.\n", ++ error); ++ ++ if((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "memory allocation error.\n"); ++ SENDERR(ENOBUFS); ++ } ++ memcpy((void*)pfkey_reply, (void*)pfkey_msg, sizeof(struct sadb_msg)); ++ pfkey_reply->sadb_msg_errno = -error; ++ pfkey_reply->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; ++ ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ int error_upmsg = 0; ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " ++ "sending up error=%d message=0p%p to socket=0p%p.\n", ++ error, ++ pfkey_reply, ++ pfkey_socketsp->socketp); ++ if((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " ++ "sending up error message to socket=0p%p failed with error=%d.\n", ++ pfkey_socketsp->socketp, ++ error_upmsg); ++ /* pfkey_msg_free(&pfkey_reply); */ ++ /* SENDERR(-error); */ ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: " ++ "sending up error message to socket=0p%p succeeded.\n", ++ pfkey_socketsp->socketp); ++ } ++ ++ pfkey_msg_free(&pfkey_reply); ++ ++ SENDERR(-error); ++ } ++ ++ errlab: ++ if (pfkey_msg) { ++ kfree((void*)pfkey_msg); ++ } ++ ++ if(error) { ++ return error; ++ } else { ++ return len; ++ } ++} ++ ++/* ++ * Receive PF_KEY data up. ++ */ ++ ++DEBUG_NO_STATIC int ++#ifdef NET_21 ++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) ++#else /* NET_21 */ ++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) ++#endif /* NET_21 */ ++{ ++ struct sock *sk; ++#ifdef NET_21 ++ int noblock = flags & MSG_DONTWAIT; ++#endif /* NET_21 */ ++ struct sk_buff *skb; ++ int error; ++ ++ if(sock == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_recvmsg: " ++ "Null socket passed in.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef NET_21 ++ sk = sock->sk; ++#else /* NET_21 */ ++ sk = sock->data; ++#endif /* NET_21 */ ++ ++ if(sk == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_recvmsg: " ++ "Null sock passed in for sock=0p%p.\n", sock); ++ return -EINVAL; ++ } ++ ++ if(msg == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_recvmsg: " ++ "Null msghdr passed in for sock=0p%p, sk=0p%p.\n", ++ sock, sk); ++ return -EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_recvmsg: sock=0p%p sk=0p%p msg=0p%p size=%d.\n", ++ sock, sk, msg, size); ++ if(flags & ~MSG_PEEK) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "flags (%d) other than MSG_PEEK not supported.\n", ++ flags); ++ return -EOPNOTSUPP; ++ } ++ ++#ifdef NET_21 ++ msg->msg_namelen = 0; /* sizeof(*ska); */ ++#else /* NET_21 */ ++ if(addr_len) { ++ *addr_len = 0; /* sizeof(*ska); */ ++ } ++#endif /* NET_21 */ ++ ++ if(sk->err) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sendmsg: " ++ "sk->err=%d.\n", sk->err); ++ return sock_error(sk); ++ } ++ ++ if((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL) { ++ return error; ++ } ++ ++ if(size > skb->len) { ++ size = skb->len; ++ } ++#ifdef NET_21 ++ else if(size len) { ++ msg->msg_flags |= MSG_TRUNC; ++ } ++#endif /* NET_21 */ ++ ++ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); ++ sk->stamp=skb->stamp; ++ ++ skb_free_datagram(sk, skb); ++ return size; ++} ++ ++#ifdef NET_21 ++struct net_proto_family pfkey_family_ops = { ++ PF_KEY, ++ pfkey_create ++}; ++ ++struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = { ++#ifdef NETDEV_23 ++ family: PF_KEY, ++ release: pfkey_release, ++ bind: sock_no_bind, ++ connect: sock_no_connect, ++ socketpair: sock_no_socketpair, ++ accept: sock_no_accept, ++ getname: sock_no_getname, ++ poll: datagram_poll, ++ ioctl: sock_no_ioctl, ++ listen: sock_no_listen, ++ shutdown: pfkey_shutdown, ++ setsockopt: sock_no_setsockopt, ++ getsockopt: sock_no_getsockopt, ++ sendmsg: pfkey_sendmsg, ++ recvmsg: pfkey_recvmsg, ++ mmap: sock_no_mmap, ++#else /* NETDEV_23 */ ++ PF_KEY, ++ sock_no_dup, ++ pfkey_release, ++ sock_no_bind, ++ sock_no_connect, ++ sock_no_socketpair, ++ sock_no_accept, ++ sock_no_getname, ++ datagram_poll, ++ sock_no_ioctl, ++ sock_no_listen, ++ pfkey_shutdown, ++ sock_no_setsockopt, ++ sock_no_getsockopt, ++ sock_no_fcntl, ++ pfkey_sendmsg, ++ pfkey_recvmsg ++#endif /* NETDEV_23 */ ++}; ++ ++#ifdef NETDEV_23 ++#include ++SOCKOPS_WRAP(pfkey, PF_KEY); ++#endif /* NETDEV_23 */ ++ ++#else /* NET_21 */ ++struct proto_ops pfkey_proto_ops = { ++ PF_KEY, ++ pfkey_create, ++ pfkey_dup, ++ pfkey_release, ++ pfkey_bind, ++ pfkey_connect, ++ pfkey_socketpair, ++ pfkey_accept, ++ pfkey_getname, ++ pfkey_select, ++ pfkey_ioctl, ++ pfkey_listen, ++ pfkey_shutdown, ++ pfkey_setsockopt, ++ pfkey_getsockopt, ++ pfkey_fcntl, ++ pfkey_sendmsg, ++ pfkey_recvmsg ++}; ++#endif /* NET_21 */ ++ ++#ifdef CONFIG_PROC_FS ++#ifndef PROC_FS_2325 ++DEBUG_NO_STATIC ++#endif /* PROC_FS_2325 */ ++int ++pfkey_get_info(char *buffer, char **start, off_t offset, int length ++#ifndef PROC_NO_DUMMY ++, int dummy ++#endif /* !PROC_NO_DUMMY */ ++) ++{ ++ const int max_content = length > 0? length-1 : 0; ++ off_t begin=0; ++ int len=0; ++ struct sock *sk=pfkey_sock_list; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(!sysctl_ipsec_debug_verbose) { ++#endif /* CONFIG_IPSEC_DEBUG */ ++ len+= snprintf(buffer,length, ++ " sock pid socket next prev e n p sndbf Flags Type St\n"); ++#ifdef CONFIG_IPSEC_DEBUG ++ } else { ++ len+= snprintf(buffer,length, ++ " sock pid d sleep socket next prev e r z n p sndbf stamp Flags Type St\n"); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ while(sk!=NULL) { ++#ifdef CONFIG_IPSEC_DEBUG ++ if(!sysctl_ipsec_debug_verbose) { ++#endif /* CONFIG_IPSEC_DEBUG */ ++ len += ipsec_snprintf(buffer+len, length-len, ++ "%8p %5d %8p %8p %8p %d %d %d %5d %08lX %8X %2X\n", ++ sk, ++ key_pid(sk), ++ sk->socket, ++ sk->next, ++ sk->prev, ++ sk->err, ++ sk->num, ++ sk->protocol, ++ sk->sndbuf, ++ sk->socket->flags, ++ sk->socket->type, ++ sk->socket->state); ++#ifdef CONFIG_IPSEC_DEBUG ++ } else { ++ len += ipsec_snprintf(buffer+len, length-len, ++ "%8p %5d %d %8p %8p %8p %8p %d %d %d %d %d %5d %d.%06d %08lX %8X %2X\n", ++ sk, ++ key_pid(sk), ++ sk->dead, ++ sk->sleep, ++ sk->socket, ++ sk->next, ++ sk->prev, ++ sk->err, ++ sk->reuse, ++ sk->zapped, ++ sk->num, ++ sk->protocol, ++ sk->sndbuf, ++ (unsigned int)sk->stamp.tv_sec, ++ (unsigned int)sk->stamp.tv_usec, ++ sk->socket->flags, ++ sk->socket->type, ++ sk->socket->state); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loop */ ++ len = max_content; /* truncate crap */ ++ break; ++ } else { ++ ++ const off_t pos = begin + len; /* file position of end of what we've generated */ ++ ++ if (pos <= offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ len = 0; ++ begin = pos; ++ } ++ } ++ sk=sk->next; ++ ++ } ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ return len - (offset - begin); ++ ++} ++ ++#ifndef PROC_FS_2325 ++DEBUG_NO_STATIC ++#endif /* PROC_FS_2325 */ ++int ++pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length ++#ifndef PROC_NO_DUMMY ++, int dummy ++#endif /* !PROC_NO_DUMMY */ ++) ++{ ++ const int max_content = length > 0? length-1 : 0; ++ off_t begin=0; ++ int len=0; ++ int satype; ++ struct supported_list *pfkey_supported_p; ++ ++ len += ipsec_snprintf(buffer, length, ++ "satype exttype alg_id ivlen minbits maxbits\n"); ++ ++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) { ++ pfkey_supported_p = pfkey_supported_list[satype]; ++ while(pfkey_supported_p) { ++ len += ipsec_snprintf(buffer+len, length-len, ++ " %2d %2d %2d %3d %3d %3d\n", ++ satype, ++ pfkey_supported_p->supportedp->supported_alg_exttype, ++ pfkey_supported_p->supportedp->supported_alg_id, ++ pfkey_supported_p->supportedp->supported_alg_ivlen, ++ pfkey_supported_p->supportedp->supported_alg_minbits, ++ pfkey_supported_p->supportedp->supported_alg_maxbits); ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loop */ ++ len = max_content; /* truncate crap */ ++ break; ++ } else { ++ const off_t pos = begin + len; /* file position of end of what we've generated */ ++ ++ if (pos <= offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ len = 0; ++ begin = pos; ++ } ++ } ++ ++ pfkey_supported_p = pfkey_supported_p->next; ++ } ++ } ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ return len - (offset - begin); ++ ++} ++ ++#ifndef PROC_FS_2325 ++DEBUG_NO_STATIC ++#endif /* PROC_FS_2325 */ ++int ++pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length ++#ifndef PROC_NO_DUMMY ++, int dummy ++#endif /* !PROC_NO_DUMMY */ ++) ++{ ++ const int max_content = length > 0? length-1 : 0; ++ off_t begin=0; ++ int len=0; ++ int satype; ++ struct socket_list *pfkey_sockets; ++ ++ len += ipsec_snprintf(buffer, length, ++ "satype socket pid sk\n"); ++ ++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) { ++ pfkey_sockets = pfkey_registered_sockets[satype]; ++ while(pfkey_sockets) { ++#ifdef NET_21 ++ len += ipsec_snprintf(buffer+len, length-len, ++ " %2d %8p %5d %8p\n", ++ satype, ++ pfkey_sockets->socketp, ++ key_pid(pfkey_sockets->socketp->sk), ++ pfkey_sockets->socketp->sk); ++#else /* NET_21 */ ++ len += ipsec_snprintf(buffer+len, length-len, ++ " %2d %8p N/A %8p\n", ++ satype, ++ pfkey_sockets->socketp, ++#if 0 ++ key_pid((pfkey_sockets->socketp)->data), ++#endif ++ (pfkey_sockets->socketp)->data); ++#endif /* NET_21 */ ++ ++ if (len >= max_content) { ++ /* we've done all that can fit -- stop loop (could stop two) */ ++ len = max_content; /* truncate crap */ ++ break; ++ } else { ++ const off_t pos = begin + len; /* file position of end of what we've generated */ ++ ++ if (pos <= offset) { ++ /* all is before first interesting character: ++ * discard, but note where we are. ++ */ ++ len = 0; ++ begin = pos; ++ } ++ } ++ ++ ++ pfkey_sockets = pfkey_sockets->next; ++ } ++ } ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ return len - (offset - begin); ++} ++ ++#ifndef PROC_FS_2325 ++struct proc_dir_entry proc_net_pfkey = ++{ ++ 0, ++ 6, "pf_key", ++ S_IFREG | S_IRUGO, 1, 0, 0, ++ 0, &proc_net_inode_operations, ++ pfkey_get_info ++}; ++struct proc_dir_entry proc_net_pfkey_supported = ++{ ++ 0, ++ 16, "pf_key_supported", ++ S_IFREG | S_IRUGO, 1, 0, 0, ++ 0, &proc_net_inode_operations, ++ pfkey_supported_get_info ++}; ++struct proc_dir_entry proc_net_pfkey_registered = ++{ ++ 0, ++ 17, "pf_key_registered", ++ S_IFREG | S_IRUGO, 1, 0, 0, ++ 0, &proc_net_inode_operations, ++ pfkey_registered_get_info ++}; ++#endif /* !PROC_FS_2325 */ ++#endif /* CONFIG_PROC_FS */ ++ ++DEBUG_NO_STATIC int ++supported_add_all(int satype, struct supported supported[], int size) ++{ ++ int i; ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:init_pfkey: " ++ "sizeof(supported_init_)[%d]/sizeof(struct supported)[%d]=%d.\n", ++ satype, ++ size, ++ (int)sizeof(struct supported), ++ (int)(size/sizeof(struct supported))); ++ ++ for(i = 0; i < size / sizeof(struct supported); i++) { ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:init_pfkey: " ++ "i=%d inserting satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", ++ i, ++ satype, ++ supported[i].supported_alg_exttype, ++ supported[i].supported_alg_id, ++ supported[i].supported_alg_ivlen, ++ supported[i].supported_alg_minbits, ++ supported[i].supported_alg_maxbits); ++ ++ error |= pfkey_list_insert_supported(&(supported[i]), ++ &(pfkey_supported_list[satype])); ++ } ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++supported_remove_all(int satype) ++{ ++ int error = 0; ++ struct supported*supportedp; ++ ++ while(pfkey_supported_list[satype]) { ++ supportedp = pfkey_supported_list[satype]->supportedp; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:init_pfkey: " ++ "removing satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", ++ satype, ++ supportedp->supported_alg_exttype, ++ supportedp->supported_alg_id, ++ supportedp->supported_alg_ivlen, ++ supportedp->supported_alg_minbits, ++ supportedp->supported_alg_maxbits); ++ ++ error |= pfkey_list_remove_supported(supportedp, ++ &(pfkey_supported_list[satype])); ++ } ++ return error; ++} ++ ++int ++pfkey_init(void) ++{ ++ int error = 0; ++ int i; ++ ++ static struct supported supported_init_ah[] = { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128}, ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160} ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ }; ++ static struct supported supported_init_esp[] = { ++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128}, ++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160}, ++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++#ifdef CONFIG_IPSEC_ENC_3DES ++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_3DESCBC, 64, 168, 168}, ++#endif /* CONFIG_IPSEC_ENC_3DES */ ++ }; ++ static struct supported supported_init_ipip[] = { ++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv4, 0, 32, 32} ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv4, 0, 128, 32} ++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv6, 0, 32, 128} ++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv6, 0, 128, 128} ++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ ++ }; ++#ifdef CONFIG_IPSEC_IPCOMP ++ static struct supported supported_init_ipcomp[] = { ++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_CALG_DEFLATE, 0, 1, 1} ++ }; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#if 0 ++ printk(KERN_INFO ++ "klips_info:pfkey_init: " ++ "FreeS/WAN: initialising PF_KEYv2 domain sockets.\n"); ++#endif ++ ++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) { ++ pfkey_registered_sockets[i] = NULL; ++ pfkey_supported_list[i] = NULL; ++ } ++ ++ error |= supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah)); ++ error |= supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp)); ++#ifdef CONFIG_IPSEC_IPCOMP ++ error |= supported_add_all(SADB_X_SATYPE_COMP, supported_init_ipcomp, sizeof(supported_init_ipcomp)); ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ error |= supported_add_all(SADB_X_SATYPE_IPIP, supported_init_ipip, sizeof(supported_init_ipip)); ++ ++#ifdef NET_21 ++ error |= sock_register(&pfkey_family_ops); ++#else /* NET_21 */ ++ error |= sock_register(pfkey_proto_ops.family, &pfkey_proto_ops); ++#endif /* NET_21 */ ++ ++#ifdef CONFIG_PROC_FS ++# ifndef PROC_FS_2325 ++# ifdef PROC_FS_21 ++ error |= proc_register(proc_net, &proc_net_pfkey); ++ error |= proc_register(proc_net, &proc_net_pfkey_supported); ++ error |= proc_register(proc_net, &proc_net_pfkey_registered); ++# else /* PROC_FS_21 */ ++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey); ++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_supported); ++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_registered); ++# endif /* PROC_FS_21 */ ++# else /* !PROC_FS_2325 */ ++ proc_net_create ("pf_key", 0, pfkey_get_info); ++ proc_net_create ("pf_key_supported", 0, pfkey_supported_get_info); ++ proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info); ++# endif /* !PROC_FS_2325 */ ++#endif /* CONFIG_PROC_FS */ ++ ++ return error; ++} ++ ++int ++pfkey_cleanup(void) ++{ ++ int error = 0; ++ ++ printk(KERN_INFO "klips_info:pfkey_cleanup: " ++ "shutting down PF_KEY domain sockets.\n"); ++#ifdef NET_21 ++ error |= sock_unregister(PF_KEY); ++#else /* NET_21 */ ++ error |= sock_unregister(pfkey_proto_ops.family); ++#endif /* NET_21 */ ++ ++ error |= supported_remove_all(SADB_SATYPE_AH); ++ error |= supported_remove_all(SADB_SATYPE_ESP); ++#ifdef CONFIG_IPSEC_IPCOMP ++ error |= supported_remove_all(SADB_X_SATYPE_COMP); ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ error |= supported_remove_all(SADB_X_SATYPE_IPIP); ++ ++#ifdef CONFIG_PROC_FS ++# ifndef PROC_FS_2325 ++ if (proc_net_unregister(proc_net_pfkey.low_ino) != 0) ++ printk("klips_debug:pfkey_cleanup: " ++ "cannot unregister /proc/net/pf_key\n"); ++ if (proc_net_unregister(proc_net_pfkey_supported.low_ino) != 0) ++ printk("klips_debug:pfkey_cleanup: " ++ "cannot unregister /proc/net/pf_key_supported\n"); ++ if (proc_net_unregister(proc_net_pfkey_registered.low_ino) != 0) ++ printk("klips_debug:pfkey_cleanup: " ++ "cannot unregister /proc/net/pf_key_registered\n"); ++# else /* !PROC_FS_2325 */ ++ proc_net_remove ("pf_key"); ++ proc_net_remove ("pf_key_supported"); ++ proc_net_remove ("pf_key_registered"); ++# endif /* !PROC_FS_2325 */ ++#endif /* CONFIG_PROC_FS */ ++ ++ /* other module unloading cleanup happens here */ ++ return error; ++} ++ ++#ifdef MODULE ++#if 0 ++int ++init_module(void) ++{ ++ pfkey_init(); ++ return 0; ++} ++ ++void ++cleanup_module(void) ++{ ++ pfkey_cleanup(); ++} ++#endif /* 0 */ ++#else /* MODULE */ ++void ++pfkey_proto_init(struct net_proto *pro) ++{ ++ pfkey_init(); ++} ++#endif /* MODULE */ ++ ++/* ++ * $Log: pfkey_v2.c,v $ ++ * Revision 1.81 2004/04/25 21:23:11 ken ++ * Pull in dhr's changes from FreeS/WAN 2.06 ++ * ++ * Revision 1.80 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.79.4.1 2003/12/22 15:25:52 jjo ++ * . Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.79 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.78.4.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.78 2003/04/03 17:38:09 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * ++ * Revision 1.77 2002/10/17 16:49:36 mcr ++ * sock->ops should reference the unwrapped options so that ++ * we get hacked in locking on SMP systems. ++ * ++ * Revision 1.76 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.75 2002/09/20 05:01:57 rgb ++ * Added memory allocation debugging. ++ * ++ * Revision 1.74 2002/09/19 02:42:50 mcr ++ * do not define the pfkey_ops function for now. ++ * ++ * Revision 1.73 2002/09/17 17:29:23 mcr ++ * #if 0 out some dead code - pfkey_ops is never used as written. ++ * ++ * Revision 1.72 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.71 2002/05/23 07:14:11 rgb ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.70 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.69 2002/04/24 07:36:33 mcr ++ * Moved from ./klips/net/ipsec/pfkey_v2.c,v ++ * ++ * Revision 1.68 2002/03/08 01:15:17 mcr ++ * put some internal structure only debug messages behind ++ * && sysctl_ipsec_debug_verbose. ++ * ++ * Revision 1.67 2002/01/29 17:17:57 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.66 2002/01/29 04:00:54 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.65 2002/01/29 02:13:18 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.64 2001/11/26 09:23:51 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.61.2.1 2001/09/25 02:28:44 mcr ++ * cleaned up includes. ++ * ++ * Revision 1.63 2001/11/12 19:38:00 rgb ++ * Continue trying other sockets even if one fails and return only original ++ * error. ++ * ++ * Revision 1.62 2001/10/18 04:45:22 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.61 2001/09/20 15:32:59 rgb ++ * Min/max cleanup. ++ * ++ * Revision 1.60 2001/06/14 19:35:12 rgb ++ * Update copyright date. ++ * ++ * Revision 1.59 2001/06/13 15:35:48 rgb ++ * Fixed #endif comments. ++ * ++ * Revision 1.58 2001/05/04 16:37:24 rgb ++ * Remove erroneous checking of return codes for proc_net_* in 2.4. ++ * ++ * Revision 1.57 2001/05/03 19:43:36 rgb ++ * Initialise error return variable. ++ * Check error return codes in startup and shutdown. ++ * Standardise on SENDERR() macro. ++ * ++ * Revision 1.56 2001/04/21 23:05:07 rgb ++ * Define out skb->used for 2.4 kernels. ++ * ++ * Revision 1.55 2001/02/28 05:03:28 rgb ++ * Clean up and rationalise startup messages. ++ * ++ * Revision 1.54 2001/02/27 22:24:55 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.53 2001/02/27 06:48:18 rgb ++ * Fixed pfkey socket unregister log message to reflect type and function. ++ * ++ * Revision 1.52 2001/02/26 22:34:38 rgb ++ * Fix error return code that was getting overwritten by the error return ++ * code of an upmsg. ++ * ++ * Revision 1.51 2001/01/30 23:42:47 rgb ++ * Allow pfkey msgs from pid other than user context required for ACQUIRE ++ * and subsequent ADD or UDATE. ++ * ++ * Revision 1.50 2001/01/23 20:22:59 rgb ++ * 2.4 fix to remove removed is_clone member. ++ * ++ * Revision 1.49 2000/11/06 04:33:47 rgb ++ * Changed non-exported functions to DEBUG_NO_STATIC. ++ * ++ * Revision 1.48 2000/09/29 19:47:41 rgb ++ * Update copyright. ++ * ++ * Revision 1.47 2000/09/22 04:23:04 rgb ++ * Added more debugging to pfkey_upmsg() call from pfkey_sendmsg() error. ++ * ++ * Revision 1.46 2000/09/21 04:20:44 rgb ++ * Fixed array size off-by-one error. (Thanks Svenning!) ++ * ++ * Revision 1.45 2000/09/20 04:01:26 rgb ++ * Changed static functions to DEBUG_NO_STATIC for revealing function names ++ * in oopsen. ++ * ++ * Revision 1.44 2000/09/19 00:33:17 rgb ++ * 2.0 fixes. ++ * ++ * Revision 1.43 2000/09/16 01:28:13 rgb ++ * Fixed use of 0 in p format warning. ++ * ++ * Revision 1.42 2000/09/16 01:09:41 rgb ++ * Fixed debug format warning for pointers that was expecting ints. ++ * ++ * Revision 1.41 2000/09/13 15:54:00 rgb ++ * Rewrote pfkey_get_info(), added pfkey_{supported,registered}_get_info(). ++ * Moved supported algos add and remove to functions. ++ * ++ * Revision 1.40 2000/09/12 18:49:28 rgb ++ * Added IPIP tunnel and IPCOMP register support. ++ * ++ * Revision 1.39 2000/09/12 03:23:49 rgb ++ * Converted #if0 debugs to sysctl. ++ * Removed debug_pfkey initialisations that prevented no_debug loading or ++ * linking. ++ * ++ * Revision 1.38 2000/09/09 06:38:02 rgb ++ * Return positive errno in pfkey_reply error message. ++ * ++ * Revision 1.37 2000/09/08 19:19:09 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Clean-up of long-unused crud... ++ * Create pfkey error message on on failure. ++ * Give pfkey_list_{insert,remove}_{socket,supported}() some error ++ * checking. ++ * ++ * Revision 1.36 2000/09/01 18:49:38 rgb ++ * Reap experimental NET_21_ bits. ++ * Turned registered sockets list into an array of one list per satype. ++ * Remove references to deprecated sklist_{insert,remove}_socket. ++ * Removed leaking socket debugging code. ++ * Removed duplicate pfkey_insert_socket in pfkey_create. ++ * Removed all references to pfkey msg->msg_name, since it is not used for ++ * pfkey. ++ * Added a supported algorithms array lists, one per satype and registered ++ * existing algorithms. ++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to ++ * list. ++ * Only send pfkey_expire() messages to sockets registered for that satype. ++ * ++ * Revision 1.35 2000/08/24 17:03:00 rgb ++ * Corrected message size error return code for PF_KEYv2. ++ * Removed downward error prohibition. ++ * ++ * Revision 1.34 2000/08/21 16:32:26 rgb ++ * Re-formatted for cosmetic consistency and readability. ++ * ++ * Revision 1.33 2000/08/20 21:38:24 rgb ++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil) ++ * Extended the upward message initiation of pfkey_sendmsg(). (Momchil) ++ * ++ * Revision 1.32 2000/07/28 14:58:31 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.31 2000/05/16 03:04:00 rgb ++ * Updates for 2.3.99pre8 from MB. ++ * ++ * Revision 1.30 2000/05/10 19:22:21 rgb ++ * Use sklist private functions for 2.3.xx compatibility. ++ * ++ * Revision 1.29 2000/03/22 16:17:03 rgb ++ * Fixed SOCKOPS_WRAPPED macro for SMP (MB). ++ * ++ * Revision 1.28 2000/02/21 19:30:45 rgb ++ * Removed references to pkt_bridged for 2.3.47 compatibility. ++ * ++ * Revision 1.27 2000/02/14 21:07:00 rgb ++ * Fixed /proc/net/pf-key legend spacing. ++ * ++ * Revision 1.26 2000/01/22 03:46:59 rgb ++ * Fixed pfkey error return mechanism so that we are able to free the ++ * local copy of the pfkey_msg, plugging a memory leak and silencing ++ * the bad object free complaints. ++ * ++ * Revision 1.25 2000/01/21 06:19:44 rgb ++ * Moved pfkey_list_remove_socket() calls to before MOD_USE_DEC_COUNT. ++ * Added debugging to pfkey_upmsg. ++ * ++ * Revision 1.24 2000/01/10 16:38:23 rgb ++ * MB fixups for 2.3.x. ++ * ++ * Revision 1.23 1999/12/09 23:22:16 rgb ++ * Added more instrumentation for debugging 2.0 socket ++ * selection/reading. ++ * Removed erroneous 2.0 wait==NULL check bug in select. ++ * ++ * Revision 1.22 1999/12/08 20:32:16 rgb ++ * Tidied up 2.0.xx support, after major pfkey work, eliminating ++ * msg->msg_name twiddling in the process, since it is not defined ++ * for PF_KEYv2. ++ * ++ * Revision 1.21 1999/12/01 22:17:19 rgb ++ * Set skb->dev to zero on new skb in case it is a reused skb. ++ * Added check for skb_put overflow and freeing to avoid upmsg on error. ++ * Added check for wrong pfkey version and freeing to avoid upmsg on ++ * error. ++ * Shut off content dumping in pfkey_destroy. ++ * Added debugging message for size of buffer allocated for upmsg. ++ * ++ * Revision 1.20 1999/11/27 12:11:00 rgb ++ * Minor clean-up, enabling quiet operation of pfkey if desired. ++ * ++ * Revision 1.19 1999/11/25 19:04:21 rgb ++ * Update proc_fs code for pfkey to use dynamic registration. ++ * ++ * Revision 1.18 1999/11/25 09:07:17 rgb ++ * Implemented SENDERR macro for propagating error codes. ++ * Fixed error return code bug. ++ * ++ * Revision 1.17 1999/11/23 23:07:20 rgb ++ * Change name of pfkey_msg_parser to pfkey_msg_interp since it no longer ++ * parses. (PJO) ++ * Sort out pfkey and freeswan headers, putting them in a library path. ++ * ++ * Revision 1.16 1999/11/20 22:00:22 rgb ++ * Moved socketlist type declarations and prototypes for shared use. ++ * Renamed reformatted and generically extended for use by other socket ++ * lists pfkey_{del,add}_open_socket to pfkey_list_{remove,insert}_socket. ++ * ++ * Revision 1.15 1999/11/18 04:15:09 rgb ++ * Make pfkey_data_ready temporarily available for 2.2.x testing. ++ * Clean up pfkey_destroy_socket() debugging statements. ++ * Add Peter Onion's code to send messages up to all listening sockets. ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * Replaced all kernel version macros to shorter, readable form. ++ * Added CONFIG_PROC_FS compiler directives in case it is shut off. ++ * ++ * Revision 1.14 1999/11/17 16:01:00 rgb ++ * Make pfkey_data_ready temporarily available for 2.2.x testing. ++ * Clean up pfkey_destroy_socket() debugging statements. ++ * Add Peter Onion's code to send messages up to all listening sockets. ++ * Changed #include "../../../lib/freeswan.h" to #include ++ * which works due to -Ilibfreeswan in the klips/net/ipsec/Makefile. ++ * ++ * Revision 1.13 1999/10/27 19:59:51 rgb ++ * Removed af_unix comments that are no longer relevant. ++ * Added debug prink statements. ++ * Added to the /proc output in pfkey_get_info. ++ * Made most functions non-static to enable oops tracing. ++ * Re-enable skb dequeueing and freeing. ++ * Fix skb_alloc() and skb_put() size bug in pfkey_upmsg(). ++ * ++ * Revision 1.12 1999/10/26 17:05:42 rgb ++ * Complete re-ordering based on proto_ops structure order. ++ * Separated out proto_ops structures for 2.0.x and 2.2.x for clarity. ++ * Simplification to use built-in socket ops where possible for 2.2.x. ++ * Add shorter macros for compiler directives to visually clean-up. ++ * Add lots of sk skb dequeueing debugging statements. ++ * Added to the /proc output in pfkey_get_info. ++ * ++ * Revision 1.11 1999/09/30 02:55:10 rgb ++ * Bogus skb detection. ++ * Fix incorrect /proc/net/ipsec-eroute printk message. ++ * ++ * Revision 1.10 1999/09/21 15:22:13 rgb ++ * Temporary fix while I figure out the right way to destroy sockets. ++ * ++ * Revision 1.9 1999/07/08 19:19:44 rgb ++ * Fix pointer format warning. ++ * Fix missing member error under 2.0.xx kernels. ++ * ++ * Revision 1.8 1999/06/13 07:24:04 rgb ++ * Add more debugging. ++ * ++ * Revision 1.7 1999/06/10 05:24:17 rgb ++ * Clarified compiler directives. ++ * Renamed variables to reduce confusion. ++ * Used sklist_*_socket() kernel functions to simplify 2.2.x socket support. ++ * Added lots of sanity checking. ++ * ++ * Revision 1.6 1999/06/03 18:59:50 rgb ++ * More updates to 2.2.x socket support. Almost works, oops at end of call. ++ * ++ * Revision 1.5 1999/05/25 22:44:05 rgb ++ * Start fixing 2.2 sockets. ++ * ++ * Revision 1.4 1999/04/29 15:21:34 rgb ++ * Move log to the end of the file. ++ * Eliminate min/max redefinition in #include . ++ * Correct path for pfkey #includes ++ * Standardise an error return method. ++ * Add debugging instrumentation. ++ * Move message type checking to pfkey_msg_parse(). ++ * Add check for errno incorrectly set. ++ * Add check for valid PID. ++ * Add check for reserved illegally set. ++ * Add check for message out of bounds. ++ * ++ * Revision 1.3 1999/04/15 17:58:07 rgb ++ * Add RCSID labels. ++ * ++ * Revision 1.2 1999/04/15 15:37:26 rgb ++ * Forward check changes from POST1_00 branch. ++ * ++ * Revision 1.1.2.2 1999/04/13 20:37:12 rgb ++ * Header Title correction. ++ * ++ * Revision 1.1.2.1 1999/03/26 20:58:55 rgb ++ * Add pfkeyv2 support to KLIPS. ++ * ++ * ++ * RFC 2367 ++ * PF_KEY_v2 Key Management API ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/pfkey_v2_ext_process.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,922 @@ ++/* ++ * @(#) RFC2367 PF_KEYv2 Key management API message parser ++ * Copyright (C) 1998-2003 Richard Guy Briggs. ++ * Copyright (C) 2004 Michael Richardson ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_ext_process.c,v 1.15 2004/04/06 02:49:26 mcr Exp $ ++ */ ++ ++/* ++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c. ++ */ ++ ++char pfkey_v2_ext_process_c_version[] = "$Id: pfkey_v2_ext_process.c,v 1.15 2004/04/06 02:49:26 mcr Exp $"; ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++ ++#include ++ ++#include ++ ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define ip_chk_addr inet_addr_type ++# define IS_MYADDR RTN_LOCAL ++#endif ++#include ++#include ++#ifdef NETLINK_SOCK ++# include ++#else ++# include ++#endif ++ ++#include /* get_random_bytes() */ ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipcomp.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++int ++pfkey_sa_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; ++ int error = 0; ++ struct ipsec_sa* ipsp; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sa_process: .\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sa_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_ext->sadb_ext_type) { ++ case SADB_EXT_SA: ++ ipsp = extr->ips; ++ break; ++ case SADB_X_EXT_SA2: ++ if(extr->ips2 == NULL) { ++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ ++ } ++ if(extr->ips2 == NULL) { ++ SENDERR(-error); ++ } ++ ipsp = extr->ips2; ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sa_process: " ++ "invalid exttype=%d.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); ++ } ++ ++ ipsp->ips_said.spi = pfkey_sa->sadb_sa_spi; ++ ipsp->ips_replaywin = pfkey_sa->sadb_sa_replay; ++ ipsp->ips_state = pfkey_sa->sadb_sa_state; ++ ipsp->ips_flags = pfkey_sa->sadb_sa_flags; ++ ipsp->ips_replaywin_lastseq = ipsp->ips_replaywin_bitmap = 0; ++ ipsp->ips_ref_rel = pfkey_sa->sadb_x_sa_ref; ++ ++ switch(ipsp->ips_said.proto) { ++ case IPPROTO_AH: ++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth; ++ ipsp->ips_encalg = SADB_EALG_NONE; ++ break; ++ case IPPROTO_ESP: ++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth; ++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt; ++#ifdef CONFIG_IPSEC_ALG ++ ipsec_alg_sa_init(ipsp); ++#endif /* CONFIG_IPSEC_ALG */ ++ break; ++ case IPPROTO_IPIP: ++ ipsp->ips_authalg = AH_NONE; ++ ipsp->ips_encalg = ESP_NONE; ++ break; ++#ifdef CONFIG_IPSEC_IPCOMP ++ case IPPROTO_COMP: ++ ipsp->ips_authalg = AH_NONE; ++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt; ++ break; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ case IPPROTO_INT: ++ ipsp->ips_authalg = AH_NONE; ++ ipsp->ips_encalg = ESP_NONE; ++ break; ++ case 0: ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sa_process: " ++ "unknown proto=%d.\n", ++ ipsp->ips_said.proto); ++ SENDERR(EINVAL); ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_lifetime_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_lifetime_process: .\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_lifetime_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_lifetime->sadb_lifetime_exttype) { ++ case SADB_EXT_LIFETIME_CURRENT: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_lifetime_process: " ++ "lifetime_current not supported yet.\n"); ++ SENDERR(EINVAL); ++ break; ++ case SADB_EXT_LIFETIME_HARD: ++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_allocations, ++ pfkey_lifetime->sadb_lifetime_allocations); ++ ++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_bytes, ++ pfkey_lifetime->sadb_lifetime_bytes); ++ ++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_addtime, ++ pfkey_lifetime->sadb_lifetime_addtime); ++ ++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_usetime, ++ pfkey_lifetime->sadb_lifetime_usetime); ++ ++ break; ++ ++ case SADB_EXT_LIFETIME_SOFT: ++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_allocations, ++ pfkey_lifetime->sadb_lifetime_allocations); ++ ++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_bytes, ++ pfkey_lifetime->sadb_lifetime_bytes); ++ ++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_addtime, ++ pfkey_lifetime->sadb_lifetime_addtime); ++ ++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_usetime, ++ pfkey_lifetime->sadb_lifetime_usetime); ++ ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_lifetime_process: " ++ "invalid exttype=%d.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_address_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ int saddr_len = 0; ++ char ipaddr_txt[ADDRTOA_BUF]; ++ unsigned char **sap; ++ unsigned short * portp = 0; ++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; ++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); ++ struct ipsec_sa* ipsp; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process:\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(s->sa_family) { ++ case AF_INET: ++ saddr_len = sizeof(struct sockaddr_in); ++ addrtoa(((struct sockaddr_in*)s)->sin_addr, 0, ipaddr_txt, sizeof(ipaddr_txt)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found address family=%d, AF_INET, %s.\n", ++ s->sa_family, ++ ipaddr_txt); ++ break; ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ case AF_INET6: ++ saddr_len = sizeof(struct sockaddr_in6); ++ break; ++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "s->sa_family=%d not supported.\n", ++ s->sa_family); ++ SENDERR(EPFNOSUPPORT); ++ } ++ ++ switch(pfkey_address->sadb_address_exttype) { ++ case SADB_EXT_ADDRESS_SRC: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found src address.\n"); ++ sap = (unsigned char **)&(extr->ips->ips_addr_s); ++ extr->ips->ips_addr_s_size = saddr_len; ++ break; ++ case SADB_EXT_ADDRESS_DST: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found dst address.\n"); ++ sap = (unsigned char **)&(extr->ips->ips_addr_d); ++ extr->ips->ips_addr_d_size = saddr_len; ++ break; ++ case SADB_EXT_ADDRESS_PROXY: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found proxy address.\n"); ++ sap = (unsigned char **)&(extr->ips->ips_addr_p); ++ extr->ips->ips_addr_p_size = saddr_len; ++ break; ++ case SADB_X_EXT_ADDRESS_DST2: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found 2nd dst address.\n"); ++ if(extr->ips2 == NULL) { ++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ ++ } ++ if(extr->ips2 == NULL) { ++ SENDERR(-error); ++ } ++ sap = (unsigned char **)&(extr->ips2->ips_addr_d); ++ extr->ips2->ips_addr_d_size = saddr_len; ++ break; ++ case SADB_X_EXT_ADDRESS_SRC_FLOW: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found src flow address.\n"); ++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { ++ SENDERR(ENOMEM); ++ } ++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_src); ++ portp = &(extr->eroute->er_eaddr.sen_sport); ++ break; ++ case SADB_X_EXT_ADDRESS_DST_FLOW: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found dst flow address.\n"); ++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { ++ SENDERR(ENOMEM); ++ } ++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_dst); ++ portp = &(extr->eroute->er_eaddr.sen_dport); ++ break; ++ case SADB_X_EXT_ADDRESS_SRC_MASK: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found src mask address.\n"); ++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { ++ SENDERR(ENOMEM); ++ } ++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_src); ++ portp = &(extr->eroute->er_emask.sen_sport); ++ break; ++ case SADB_X_EXT_ADDRESS_DST_MASK: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found dst mask address.\n"); ++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) { ++ SENDERR(ENOMEM); ++ } ++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_dst); ++ portp = &(extr->eroute->er_emask.sen_dport); ++ break; ++#ifdef NAT_TRAVERSAL ++ case SADB_X_EXT_NAT_T_OA: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "found NAT-OA address.\n"); ++ sap = (unsigned char **)&(extr->ips->ips_natt_oa); ++ extr->ips->ips_natt_oa_size = saddr_len; ++ break; ++#endif ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "unrecognised ext_type=%d.\n", ++ pfkey_address->sadb_address_exttype); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_address->sadb_address_exttype) { ++ case SADB_EXT_ADDRESS_SRC: ++ case SADB_EXT_ADDRESS_DST: ++ case SADB_EXT_ADDRESS_PROXY: ++ case SADB_X_EXT_ADDRESS_DST2: ++#ifdef NAT_TRAVERSAL ++ case SADB_X_EXT_NAT_T_OA: ++#endif ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "allocating %d bytes for saddr.\n", ++ saddr_len); ++ if(!(*sap = kmalloc(saddr_len, GFP_KERNEL))) { ++ SENDERR(ENOMEM); ++ } ++ memcpy(*sap, s, saddr_len); ++ break; ++ default: ++ if(s->sa_family != AF_INET) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "s->sa_family=%d not supported.\n", ++ s->sa_family); ++ SENDERR(EPFNOSUPPORT); ++ } ++ (unsigned long)(*sap) = ((struct sockaddr_in*)s)->sin_addr.s_addr; ++ if (portp != 0) ++ *portp = ((struct sockaddr_in*)s)->sin_port; ++#ifdef CONFIG_IPSEC_DEBUG ++ if(extr->eroute) { ++ char buf1[64], buf2[64]; ++ if (debug_pfkey) { ++ subnettoa(extr->eroute->er_eaddr.sen_ip_src, ++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst, ++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_parse: " ++ "extr->eroute set to %s:%d->%s:%d\n", ++ buf1, ++ ntohs(extr->eroute->er_eaddr.sen_sport), ++ buf2, ++ ntohs(extr->eroute->er_eaddr.sen_dport)); ++ } ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ } ++ ++ ipsp = extr->ips; ++ switch(pfkey_address->sadb_address_exttype) { ++ case SADB_X_EXT_ADDRESS_DST2: ++ ipsp = extr->ips2; ++ case SADB_EXT_ADDRESS_DST: ++ if(s->sa_family == AF_INET) { ++ ipsp->ips_said.dst.u.v4.sin_addr.s_addr = ((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr.s_addr; ++ ipsp->ips_said.dst.u.v4.sin_family = AF_INET; ++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr, ++ 0, ++ ipaddr_txt, ++ sizeof(ipaddr_txt)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "ips_said.dst set to %s.\n", ++ ipaddr_txt); ++ } else { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: " ++ "uh, ips_said.dst doesn't do address family=%d yet, said will be invalid.\n", ++ s->sa_family); ++ } ++ default: ++ break; ++ } ++ ++ /* XXX check if port!=0 */ ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_address_process: successful.\n"); ++ errlab: ++ return error; ++} ++ ++int ++pfkey_key_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: .\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_key->sadb_key_exttype) { ++ case SADB_EXT_KEY_AUTH: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "allocating %d bytes for authkey.\n", ++ DIVUP(pfkey_key->sadb_key_bits, 8)); ++ if(!(extr->ips->ips_key_a = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "memory allocation error.\n"); ++ SENDERR(ENOMEM); ++ } ++ extr->ips->ips_key_bits_a = pfkey_key->sadb_key_bits; ++ extr->ips->ips_key_a_size = DIVUP(pfkey_key->sadb_key_bits, 8); ++ memcpy(extr->ips->ips_key_a, ++ (char*)pfkey_key + sizeof(struct sadb_key), ++ extr->ips->ips_key_a_size); ++ break; ++ case SADB_EXT_KEY_ENCRYPT: /* Key(s) */ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "allocating %d bytes for enckey.\n", ++ DIVUP(pfkey_key->sadb_key_bits, 8)); ++ if(!(extr->ips->ips_key_e = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "memory allocation error.\n"); ++ SENDERR(ENOMEM); ++ } ++ extr->ips->ips_key_bits_e = pfkey_key->sadb_key_bits; ++ extr->ips->ips_key_e_size = DIVUP(pfkey_key->sadb_key_bits, 8); ++ memcpy(extr->ips->ips_key_e, ++ (char*)pfkey_key + sizeof(struct sadb_key), ++ extr->ips->ips_key_e_size); ++ break; ++ default: ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_key_process: " ++ "success.\n"); ++errlab: ++ return error; ++} ++ ++int ++pfkey_ident_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; ++ int data_len; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ident_process: .\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ident_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_ident->sadb_ident_exttype) { ++ case SADB_EXT_IDENTITY_SRC: ++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ ++ extr->ips->ips_ident_s.type = pfkey_ident->sadb_ident_type; ++ extr->ips->ips_ident_s.id = pfkey_ident->sadb_ident_id; ++ extr->ips->ips_ident_s.len = pfkey_ident->sadb_ident_len; ++ if(data_len) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ident_process: " ++ "allocating %d bytes for ident_s.\n", ++ data_len); ++ if(!(extr->ips->ips_ident_s.data ++ = kmalloc(data_len, GFP_KERNEL))) { ++ SENDERR(ENOMEM); ++ } ++ memcpy(extr->ips->ips_ident_s.data, ++ (char*)pfkey_ident + sizeof(struct sadb_ident), ++ data_len); ++ } else { ++ extr->ips->ips_ident_s.data = NULL; ++ } ++ break; ++ case SADB_EXT_IDENTITY_DST: /* Identity(ies) */ ++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); ++ ++ extr->ips->ips_ident_d.type = pfkey_ident->sadb_ident_type; ++ extr->ips->ips_ident_d.id = pfkey_ident->sadb_ident_id; ++ extr->ips->ips_ident_d.len = pfkey_ident->sadb_ident_len; ++ if(data_len) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ident_process: " ++ "allocating %d bytes for ident_d.\n", ++ data_len); ++ if(!(extr->ips->ips_ident_d.data ++ = kmalloc(data_len, GFP_KERNEL))) { ++ SENDERR(ENOMEM); ++ } ++ memcpy(extr->ips->ips_ident_d.data, ++ (char*)pfkey_ident + sizeof(struct sadb_ident), ++ data_len); ++ } else { ++ extr->ips->ips_ident_d.data = NULL; ++ } ++ break; ++ default: ++ SENDERR(EINVAL); ++ } ++errlab: ++ return error; ++} ++ ++int ++pfkey_sens_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_sens_process: " ++ "Sorry, I can't process exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ errlab: ++ return error; ++} ++ ++int ++pfkey_prop_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_prop_process: " ++ "Sorry, I can't process exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++ errlab: ++ return error; ++} ++ ++int ++pfkey_supported_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_supported_process: " ++ "Sorry, I can't process exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_spirange_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_spirange_process: .\n"); ++/* errlab: */ ++ return error; ++} ++ ++int ++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_kmprivate_process: " ++ "Sorry, I can't process exttype=%d yet.\n", ++ pfkey_ext->sadb_ext_type); ++ SENDERR(EINVAL); /* don't process these yet */ ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_x_satype_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_satype_process: .\n"); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_satype_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(extr->ips2 == NULL) { ++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */ ++ } ++ if(extr->ips2 == NULL) { ++ SENDERR(-error); ++ } ++ if(!(extr->ips2->ips_said.proto = satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_satype_process: " ++ "proto lookup from satype=%d failed.\n", ++ pfkey_x_satype->sadb_x_satype_satype); ++ SENDERR(EINVAL); ++ } ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_satype_process: " ++ "protocol==%d decoded from satype==%d(%s).\n", ++ extr->ips2->ips_said.proto, ++ pfkey_x_satype->sadb_x_satype_satype, ++ satype2name(pfkey_x_satype->sadb_x_satype_satype)); ++ ++errlab: ++ return error; ++} ++ ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++int ++pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)pfkey_ext; ++ ++ if(!pfkey_x_nat_t_type) { ++ printk("klips_debug:pfkey_x_nat_t_type_process: " ++ "null pointer passed in\n"); ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_nat_t_type_process: %d.\n", ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_nat_t_type_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_x_nat_t_type->sadb_x_nat_t_type_type) { ++ case ESPINUDP_WITH_NON_IKE: /* with Non-IKE (older version) */ ++ case ESPINUDP_WITH_NON_ESP: /* with Non-ESP */ ++ ++ extr->ips->ips_natt_type = pfkey_x_nat_t_type->sadb_x_nat_t_type_type; ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_nat_t_type_process: " ++ "unknown type %d.\n", ++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type); ++ SENDERR(EINVAL); ++ break; ++ } ++ ++errlab: ++ return error; ++} ++ ++int ++pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)pfkey_ext; ++ ++ if(!pfkey_x_nat_t_port) { ++ printk("klips_debug:pfkey_x_nat_t_port_process: " ++ "null pointer passed in\n"); ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_nat_t_port_process: %d/%d.\n", ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype, ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_nat_t_type_process: " ++ "extr or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype) { ++ case SADB_X_EXT_NAT_T_SPORT: ++ extr->ips->ips_natt_sport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port; ++ break; ++ case SADB_X_EXT_NAT_T_DPORT: ++ extr->ips->ips_natt_dport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port; ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_nat_t_port_process: " ++ "unknown exttype %d.\n", ++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype); ++ SENDERR(EINVAL); ++ break; ++ } ++ ++errlab: ++ return error; ++} ++#endif ++ ++int ++pfkey_x_debug_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; ++ ++ if(!pfkey_x_debug) { ++ printk("klips_debug:pfkey_x_debug_process: " ++ "null pointer passed in\n"); ++ SENDERR(EINVAL); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_debug_process: .\n"); ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if(pfkey_x_debug->sadb_x_debug_netlink >> ++ (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 - 1)) { ++ pfkey_x_debug->sadb_x_debug_netlink &= ++ ~(1 << (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 -1)); ++ debug_tunnel |= pfkey_x_debug->sadb_x_debug_tunnel; ++ debug_netlink |= pfkey_x_debug->sadb_x_debug_netlink; ++ debug_xform |= pfkey_x_debug->sadb_x_debug_xform; ++ debug_eroute |= pfkey_x_debug->sadb_x_debug_eroute; ++ debug_spi |= pfkey_x_debug->sadb_x_debug_spi; ++ debug_radij |= pfkey_x_debug->sadb_x_debug_radij; ++ debug_esp |= pfkey_x_debug->sadb_x_debug_esp; ++ debug_ah |= pfkey_x_debug->sadb_x_debug_ah; ++ debug_rcv |= pfkey_x_debug->sadb_x_debug_rcv; ++ debug_pfkey |= pfkey_x_debug->sadb_x_debug_pfkey; ++#ifdef CONFIG_IPSEC_IPCOMP ++ sysctl_ipsec_debug_ipcomp |= pfkey_x_debug->sadb_x_debug_ipcomp; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ sysctl_ipsec_debug_verbose |= pfkey_x_debug->sadb_x_debug_verbose; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_debug_process: " ++ "set\n"); ++ } else { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_debug_process: " ++ "unset\n"); ++ debug_tunnel &= pfkey_x_debug->sadb_x_debug_tunnel; ++ debug_netlink &= pfkey_x_debug->sadb_x_debug_netlink; ++ debug_xform &= pfkey_x_debug->sadb_x_debug_xform; ++ debug_eroute &= pfkey_x_debug->sadb_x_debug_eroute; ++ debug_spi &= pfkey_x_debug->sadb_x_debug_spi; ++ debug_radij &= pfkey_x_debug->sadb_x_debug_radij; ++ debug_esp &= pfkey_x_debug->sadb_x_debug_esp; ++ debug_ah &= pfkey_x_debug->sadb_x_debug_ah; ++ debug_rcv &= pfkey_x_debug->sadb_x_debug_rcv; ++ debug_pfkey &= pfkey_x_debug->sadb_x_debug_pfkey; ++#ifdef CONFIG_IPSEC_IPCOMP ++ sysctl_ipsec_debug_ipcomp &= pfkey_x_debug->sadb_x_debug_ipcomp; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ sysctl_ipsec_debug_verbose &= pfkey_x_debug->sadb_x_debug_verbose; ++ } ++#else /* CONFIG_IPSEC_DEBUG */ ++ printk("klips_debug:pfkey_x_debug_process: " ++ "debugging not enabled\n"); ++ SENDERR(EINVAL); ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++errlab: ++ return error; ++} ++ ++/* ++ * $Log: pfkey_v2_ext_process.c,v $ ++ * Revision 1.15 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.14 2004/02/03 03:13:59 mcr ++ * no longer #ifdef out NON_ESP mode. That was a mistake. ++ * ++ * Revision 1.13 2003/12/15 18:13:12 mcr ++ * when compiling with NAT traversal, don't assume that the ++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP ++ * is set. ++ * ++ * Revision 1.12.2.1 2003/12/22 15:25:52 jjo ++ * Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.12 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.11 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.10.4.2 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.10.4.1 2003/09/21 13:59:56 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.10 2003/02/06 01:51:41 rgb ++ * Removed no longer relevant comment ++ * ++ * Revision 1.9 2003/01/30 02:32:44 rgb ++ * ++ * Transmit error code through to caller from callee for better diagnosis of problems. ++ * ++ * Revision 1.8 2002/12/13 22:42:22 mcr ++ * restored sa_ref code ++ * ++ * Revision 1.7 2002/12/13 22:40:48 mcr ++ * temporarily removed sadb_x_sa_ref reference for 2.xx ++ * ++ * Revision 1.6 2002/10/05 05:02:58 dhr ++ * ++ * C labels go on statements ++ * ++ * Revision 1.5 2002/09/20 15:41:08 rgb ++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc(). ++ * Added sadb_x_sa_ref to struct sadb_sa. ++ * ++ * Revision 1.4 2002/09/20 05:02:02 rgb ++ * Added memory allocation debugging. ++ * ++ * Revision 1.3 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.2 2002/05/27 18:55:03 rgb ++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. ++ * ++ * Revision 1.1 2002/05/14 02:33:51 rgb ++ * Moved all the extension processing functions to pfkey_v2_ext_process.c. ++ * ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/pfkey_v2_parser.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,4018 @@ ++/* ++ * @(#) RFC2367 PF_KEYv2 Key management API message parser ++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: pfkey_v2_parser.c,v 1.123 2004/04/06 02:49:26 mcr Exp $ ++ */ ++ ++/* ++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c. ++ */ ++ ++char pfkey_v2_parser_c_version[] = "$Id: pfkey_v2_parser.c,v 1.123 2004/04/06 02:49:26 mcr Exp $"; ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++ ++#include ++ ++#include ++ ++#ifdef SPINLOCK ++# ifdef SPINLOCK_23 ++# include /* *lock* */ ++# else /* SPINLOCK_23 */ ++# include /* *lock* */ ++# endif /* SPINLOCK_23 */ ++#endif /* SPINLOCK */ ++#ifdef NET_21 ++# include ++# include ++# define ip_chk_addr inet_addr_type ++# define IS_MYADDR RTN_LOCAL ++#endif ++#include ++#include ++#ifdef NETLINK_SOCK ++# include ++#else ++# include ++#endif ++ ++#include /* get_random_bytes() */ ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_sa.h" ++ ++#include "openswan/ipsec_radij.h" ++#include "openswan/ipsec_xform.h" ++#include "openswan/ipsec_ah.h" ++#include "openswan/ipsec_esp.h" ++#include "openswan/ipsec_tunnel.h" ++#include "openswan/ipsec_rcv.h" ++#include "openswan/ipcomp.h" ++ ++#include ++#include ++ ++#include "openswan/ipsec_proto.h" ++#include "openswan/ipsec_alg.h" ++ ++ ++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) ++ ++struct sklist_t { ++ struct socket *sk; ++ struct sklist_t* next; ++} pfkey_sklist_head, *pfkey_sklist, *pfkey_sklist_prev; ++ ++__u32 pfkey_msg_seq = 0; ++ ++ ++#if 0 ++#define DUMP_SAID dump_said(&extr->ips->ips_said, __LINE__) ++#define DUMP_SAID2 dump_said(&extr.ips->ips_said, __LINE__) ++static void dump_said(ip_said *s, int line) ++{ ++ char msa[SATOT_BUF]; ++ size_t msa_len; ++ ++ msa_len = satot(s, 0, msa, sizeof(msa)); ++ ++ printk("line: %d msa: %s\n", line, msa); ++} ++#endif ++ ++ ++int ++pfkey_alloc_eroute(struct eroute** eroute) ++{ ++ int error = 0; ++ if(*eroute) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_alloc_eroute: " ++ "eroute struct already allocated\n"); ++ SENDERR(EEXIST); ++ } ++ ++ if((*eroute = kmalloc(sizeof(**eroute), GFP_ATOMIC) ) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_alloc_eroute: " ++ "memory allocation error\n"); ++ SENDERR(ENOMEM); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_alloc_eroute: " ++ "allocating %lu bytes for an eroute at 0p%p\n", ++ (unsigned long) sizeof(**eroute), *eroute); ++ ++ memset((caddr_t)*eroute, 0, sizeof(**eroute)); ++ (*eroute)->er_eaddr.sen_len = ++ (*eroute)->er_emask.sen_len = sizeof(struct sockaddr_encap); ++ (*eroute)->er_eaddr.sen_family = ++ (*eroute)->er_emask.sen_family = AF_ENCAP; ++ (*eroute)->er_eaddr.sen_type = SENT_IP4; ++ (*eroute)->er_emask.sen_type = 255; ++ (*eroute)->er_pid = 0; ++ (*eroute)->er_count = 0; ++ (*eroute)->er_lasttime = jiffies/HZ; ++ ++ errlab: ++ return(error); ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_protocol_process(struct sadb_ext *pfkey_ext, ++ struct pfkey_extracted_data *extr) ++{ ++ int error = 0; ++ struct sadb_protocol * p = (struct sadb_protocol *)pfkey_ext; ++ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_protocol_process: %p\n", extr); ++ ++ if (extr == 0) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_protocol_process:" ++ "extr is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ if (extr->eroute == 0) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_protocol_process:" ++ "extr->eroute is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ extr->eroute->er_eaddr.sen_proto = p->sadb_protocol_proto; ++ extr->eroute->er_emask.sen_proto = p->sadb_protocol_proto ? ~0:0; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_protocol_process: protocol = %d.\n", ++ p->sadb_protocol_proto); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_ipsec_sa_init(struct ipsec_sa *ipsp, struct sadb_ext **extensions) ++{ ++ int i; ++ int error = 0; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ char ipaddr_txt[ADDRTOA_BUF]; ++ char ipaddr2_txt[ADDRTOA_BUF]; ++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1) ++ unsigned char kb[AHMD596_BLKLEN]; ++#endif ++#ifdef CONFIG_IPSEC_ALG ++ struct ipsec_alg_enc *ixt_e = NULL; ++ struct ipsec_alg_auth *ixt_a = NULL; ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ if(ipsp == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "ipsp is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len = satot(&ipsp->ips_said, 0, sa, sizeof(sa)); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "(pfkey defined) called for SA:%s\n", ++ sa_len ? sa : " (error)"); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "calling init routine of %s%s%s\n", ++ IPS_XFORM_NAME(ipsp)); ++ ++ switch(ipsp->ips_said.proto) { ++ ++#ifdef CONFIG_IPSEC_IPIP ++ case IPPROTO_IPIP: { ++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr, ++ 0, ++ ipaddr_txt, sizeof(ipaddr_txt)); ++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr, ++ 0, ++ ipaddr2_txt, sizeof(ipaddr_txt)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "(pfkey defined) IPIP ipsec_sa set for %s->%s.\n", ++ ipaddr_txt, ++ ipaddr2_txt); ++ } ++ break; ++#endif /* !CONFIG_IPSEC_IPIP */ ++#ifdef CONFIG_IPSEC_AH ++ case IPPROTO_AH: ++ switch(ipsp->ips_authalg) { ++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: { ++ unsigned char *akp; ++ unsigned int aks; ++ MD5_CTX *ictx; ++ MD5_CTX *octx; ++ ++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, ++ ipsp->ips_key_bits_a, AHMD596_KLEN * 8); ++ SENDERR(EINVAL); ++ } ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n", ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ ++ ipsp->ips_auth_bits = AHMD596_ALEN * 8; ++ ++ /* save the pointer to the key material */ ++ akp = ipsp->ips_key_a; ++ aks = ipsp->ips_key_a_size; ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %lu bytes for md5_ctx.\n", ++ (unsigned long) sizeof(struct md5_ctx)); ++ if((ipsp->ips_key_a = (caddr_t) ++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { ++ ipsp->ips_key_a = akp; ++ SENDERR(ENOMEM); ++ } ++ ipsp->ips_key_a_size = sizeof(struct md5_ctx); ++ ++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { ++ kb[i] = akp[i] ^ HMAC_IPAD; ++ } ++ for (; i < AHMD596_BLKLEN; i++) { ++ kb[i] = HMAC_IPAD; ++ } ++ ++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx); ++ MD5Init(ictx); ++ MD5Update(ictx, kb, AHMD596_BLKLEN); ++ ++ for (i = 0; i < AHMD596_BLKLEN; i++) { ++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); ++ } ++ ++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx); ++ MD5Init(octx); ++ MD5Update(octx, kb, AHMD596_BLKLEN); ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ++ ((__u32*)ictx)[0], ++ ((__u32*)ictx)[1], ++ ((__u32*)ictx)[2], ++ ((__u32*)ictx)[3], ++ ((__u32*)octx)[0], ++ ((__u32*)octx)[1], ++ ((__u32*)octx)[2], ++ ((__u32*)octx)[3] ); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ ++ /* zero key buffer -- paranoid */ ++ memset(akp, 0, aks); ++ kfree(akp); ++ } ++ break; ++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: { ++ unsigned char *akp; ++ unsigned int aks; ++ SHA1_CTX *ictx; ++ SHA1_CTX *octx; ++ ++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, ++ ipsp->ips_key_bits_a, AHSHA196_KLEN * 8); ++ SENDERR(EINVAL); ++ } ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ ++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8; ++ ++ /* save the pointer to the key material */ ++ akp = ipsp->ips_key_a; ++ aks = ipsp->ips_key_a_size; ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %lu bytes for sha1_ctx.\n", ++ (unsigned long) sizeof(struct sha1_ctx)); ++ if((ipsp->ips_key_a = (caddr_t) ++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { ++ ipsp->ips_key_a = akp; ++ SENDERR(ENOMEM); ++ } ++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx); ++ ++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { ++ kb[i] = akp[i] ^ HMAC_IPAD; ++ } ++ for (; i < AHMD596_BLKLEN; i++) { ++ kb[i] = HMAC_IPAD; ++ } ++ ++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx); ++ SHA1Init(ictx); ++ SHA1Update(ictx, kb, AHSHA196_BLKLEN); ++ ++ for (i = 0; i < AHSHA196_BLKLEN; i++) { ++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); ++ } ++ ++ octx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->octx); ++ SHA1Init(octx); ++ SHA1Update(octx, kb, AHSHA196_BLKLEN); ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ++ ((__u32*)ictx)[0], ++ ((__u32*)ictx)[1], ++ ((__u32*)ictx)[2], ++ ((__u32*)ictx)[3], ++ ((__u32*)octx)[0], ++ ((__u32*)octx)[1], ++ ((__u32*)octx)[2], ++ ((__u32*)octx)[3] ); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ /* zero key buffer -- paranoid */ ++ memset(akp, 0, aks); ++ kfree(akp); ++ } ++ break; ++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "authalg=%d support not available in the kernel", ++ ipsp->ips_authalg); ++ SENDERR(EINVAL); ++ } ++ break; ++#endif /* CONFIG_IPSEC_AH */ ++#ifdef CONFIG_IPSEC_ESP ++ case IPPROTO_ESP: { ++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1) ++ unsigned char *akp; ++ unsigned int aks; ++#endif ++#if defined (CONFIG_IPSEC_ENC_3DES) ++ unsigned char *ekp; ++ unsigned int eks; ++#endif ++ ++ ipsp->ips_iv_size = 0; ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_e=ipsp->ips_alg_enc)) { ++ ipsp->ips_iv_size = ixt_e->ixt_ivlen/8; ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ipsp->ips_encalg) { ++# ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++# endif /* CONFIG_IPSEC_ENC_3DES */ ++# if defined(CONFIG_IPSEC_ENC_3DES) ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %u bytes for iv.\n", ++ EMT_ESPDES_IV_SZ); ++ if((ipsp->ips_iv = (caddr_t) ++ kmalloc((ipsp->ips_iv_size = EMT_ESPDES_IV_SZ), GFP_ATOMIC)) == NULL) { ++ SENDERR(ENOMEM); ++ } ++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, EMT_ESPDES_IV_SZ); ++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8; ++ ipsp->ips_iv_size = EMT_ESPDES_IV_SZ; ++ break; ++# endif /* defined(CONFIG_IPSEC_ENC_3DES) */ ++ case ESP_NONE: ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "encalg=%d support not available in the kernel", ++ ipsp->ips_encalg); ++ SENDERR(EINVAL); ++ } ++ ++ /* Create IV */ ++ if (ipsp->ips_iv_size) { ++ if((ipsp->ips_iv = (caddr_t) ++ kmalloc(ipsp->ips_iv_size, GFP_ATOMIC)) == NULL) { ++ SENDERR(ENOMEM); ++ } ++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, ipsp->ips_iv_size); ++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8; ++ } ++ ++#ifdef CONFIG_IPSEC_ALG ++ if (ixt_e) { ++ if ((error=ipsec_alg_enc_key_create(ipsp)) < 0) ++ SENDERR(-error); ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ switch(ipsp->ips_encalg) { ++# ifdef CONFIG_IPSEC_ENC_3DES ++ case ESP_3DES: ++ if(ipsp->ips_key_bits_e != (EMT_ESP3DES_KEY_SZ * 8)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "incorrect encryption key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, ++ ipsp->ips_key_bits_e, EMT_ESP3DES_KEY_SZ * 8); ++ SENDERR(EINVAL); ++ } ++ ++ /* save encryption key pointer */ ++ ekp = ipsp->ips_key_e; ++ eks = ipsp->ips_key_e_size; ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %lu bytes for 3des.\n", ++ (unsigned long) (3 * sizeof(struct des_eks))); ++ if((ipsp->ips_key_e = (caddr_t) ++ kmalloc(3 * sizeof(struct des_eks), GFP_ATOMIC)) == NULL) { ++ ipsp->ips_key_e = ekp; ++ SENDERR(ENOMEM); ++ } ++ ipsp->ips_key_e_size = 3 * sizeof(struct des_eks); ++ ++ for(i = 0; i < 3; i++) { ++#if KLIPS_DIVULGE_CYPHER_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "3des key %d/3 is 0x%08x%08x\n", ++ i + 1, ++ ntohl(*((__u32 *)ekp + i * 2)), ++ ntohl(*((__u32 *)ekp + i * 2 + 1))); ++# endif ++#if KLIPS_FIXES_DES_PARITY ++ /* force parity */ ++ des_set_odd_parity((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i)); ++#endif ++ error = des_set_key((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i), ++ ((struct des_eks *)(ipsp->ips_key_e))[i].ks); ++ if (error == -1) ++ printk("klips_debug:pfkey_ipsec_sa_init: " ++ "parity error in des key %d/3\n", ++ i + 1); ++ else if (error == -2) ++ printk("klips_debug:pfkey_ipsec_sa_init: " ++ "illegal weak des key %d/3\n", i + 1); ++ if (error) { ++ memset(ekp, 0, eks); ++ kfree(ekp); ++ SENDERR(EINVAL); ++ } ++ } ++ ++ /* paranoid */ ++ memset(ekp, 0, eks); ++ kfree(ekp); ++ break; ++# endif /* CONFIG_IPSEC_ENC_3DES */ ++ case ESP_NONE: ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "encalg=%d support not available in the kernel", ++ ipsp->ips_encalg); ++ SENDERR(EINVAL); ++ } ++ ++#ifdef CONFIG_IPSEC_ALG ++ if ((ixt_a=ipsp->ips_alg_auth)) { ++ if ((error=ipsec_alg_auth_key_create(ipsp)) < 0) ++ SENDERR(-error); ++ } else ++#endif /* CONFIG_IPSEC_ALG */ ++ ++ switch(ipsp->ips_authalg) { ++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 ++ case AH_MD5: { ++ MD5_CTX *ictx; ++ MD5_CTX *octx; ++ ++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, ++ ipsp->ips_key_bits_a, ++ AHMD596_KLEN * 8); ++ SENDERR(EINVAL); ++ } ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n", ++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+0)), ++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+1)), ++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+2)), ++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+3))); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ ipsp->ips_auth_bits = AHMD596_ALEN * 8; ++ ++ /* save the pointer to the key material */ ++ akp = ipsp->ips_key_a; ++ aks = ipsp->ips_key_a_size; ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %lu bytes for md5_ctx.\n", ++ (unsigned long) sizeof(struct md5_ctx)); ++ if((ipsp->ips_key_a = (caddr_t) ++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) { ++ ipsp->ips_key_a = akp; ++ SENDERR(ENOMEM); ++ } ++ ipsp->ips_key_a_size = sizeof(struct md5_ctx); ++ ++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { ++ kb[i] = akp[i] ^ HMAC_IPAD; ++ } ++ for (; i < AHMD596_BLKLEN; i++) { ++ kb[i] = HMAC_IPAD; ++ } ++ ++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx); ++ MD5Init(ictx); ++ MD5Update(ictx, kb, AHMD596_BLKLEN); ++ ++ for (i = 0; i < AHMD596_BLKLEN; i++) { ++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); ++ } ++ ++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx); ++ MD5Init(octx); ++ MD5Update(octx, kb, AHMD596_BLKLEN); ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ++ ((__u32*)ictx)[0], ++ ((__u32*)ictx)[1], ++ ((__u32*)ictx)[2], ++ ((__u32*)ictx)[3], ++ ((__u32*)octx)[0], ++ ((__u32*)octx)[1], ++ ((__u32*)octx)[2], ++ ((__u32*)octx)[3] ); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ /* paranoid */ ++ memset(akp, 0, aks); ++ kfree(akp); ++ break; ++ } ++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ ++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 ++ case AH_SHA: { ++ SHA1_CTX *ictx; ++ SHA1_CTX *octx; ++ ++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/, ++ ipsp->ips_key_bits_a, ++ AHSHA196_KLEN * 8); ++ SENDERR(EINVAL); ++ } ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n", ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)), ++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3))); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8; ++ ++ /* save the pointer to the key material */ ++ akp = ipsp->ips_key_a; ++ aks = ipsp->ips_key_a_size; ++ ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "allocating %lu bytes for sha1_ctx.\n", ++ (unsigned long) sizeof(struct sha1_ctx)); ++ if((ipsp->ips_key_a = (caddr_t) ++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) { ++ ipsp->ips_key_a = akp; ++ SENDERR(ENOMEM); ++ } ++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx); ++ ++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) { ++ kb[i] = akp[i] ^ HMAC_IPAD; ++ } ++ for (; i < AHMD596_BLKLEN; i++) { ++ kb[i] = HMAC_IPAD; ++ } ++ ++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx); ++ SHA1Init(ictx); ++ SHA1Update(ictx, kb, AHSHA196_BLKLEN); ++ ++ for (i = 0; i < AHSHA196_BLKLEN; i++) { ++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); ++ } ++ ++ octx = &((struct sha1_ctx*)(ipsp->ips_key_a))->octx; ++ SHA1Init(octx); ++ SHA1Update(octx, kb, AHSHA196_BLKLEN); ++ ++# if KLIPS_DIVULGE_HMAC_KEY ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n", ++ ((__u32*)ictx)[0], ++ ((__u32*)ictx)[1], ++ ((__u32*)ictx)[2], ++ ((__u32*)ictx)[3], ++ ((__u32*)octx)[0], ++ ((__u32*)octx)[1], ++ ((__u32*)octx)[2], ++ ((__u32*)octx)[3] ); ++# endif /* KLIPS_DIVULGE_HMAC_KEY */ ++ memset(akp, 0, aks); ++ kfree(akp); ++ break; ++ } ++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ ++ case AH_NONE: ++ break; ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "authalg=%d support not available in the kernel.\n", ++ ipsp->ips_authalg); ++ SENDERR(EINVAL); ++ } ++ } ++ break; ++#endif /* !CONFIG_IPSEC_ESP */ ++#ifdef CONFIG_IPSEC_IPCOMP ++ case IPPROTO_COMP: ++ ipsp->ips_comp_adapt_tries = 0; ++ ipsp->ips_comp_adapt_skip = 0; ++ ipsp->ips_comp_ratio_cbytes = 0; ++ ipsp->ips_comp_ratio_dbytes = 0; ++ break; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ default: ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_ipsec_sa_init: " ++ "proto=%d unknown.\n", ++ ipsp->ips_said.proto); ++ SENDERR(EINVAL); ++ } ++ ++ errlab: ++ return(error); ++} ++ ++ ++int ++pfkey_safe_build(int error, struct sadb_ext *extensions[SADB_MAX+1]) ++{ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build: " ++ "error=%d\n", ++ error); ++ if (!error) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:" ++ "success.\n"); ++ return 1; ++ } else { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:" ++ "caught error %d\n", ++ error); ++ pfkey_extensions_free(extensions); ++ return 0; ++ } ++} ++ ++ ++DEBUG_NO_STATIC int ++pfkey_getspi_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ipsec_spi_t minspi = htonl(256), maxspi = htonl(-1L); ++ int found_avail = 0; ++ struct ipsec_sa *ipsq; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(extr == NULL || extr->ips == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: " ++ "error, extr or extr->ipsec_sa pointer NULL\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(extensions[SADB_EXT_SPIRANGE]) { ++ minspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_min; ++ maxspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_max; ++ } ++ ++ if(maxspi == minspi) { ++ extr->ips->ips_said.spi = maxspi; ++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if(ipsq != NULL) { ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ipsec_sa_put(ipsq); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: " ++ "EMT_GETSPI found an old ipsec_sa for SA: %s, delete it first.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(EEXIST); ++ } else { ++ found_avail = 1; ++ } ++ } else { ++ int i = 0; ++ __u32 rand_val; ++ __u32 spi_diff; ++ while( ( i < (spi_diff = (ntohl(maxspi) - ntohl(minspi)))) && !found_avail ) { ++ prng_bytes(&ipsec_prng, (char *) &(rand_val), ++ ( (spi_diff < (2^8)) ? 1 : ++ ( (spi_diff < (2^16)) ? 2 : ++ ( (spi_diff < (2^24)) ? 3 : ++ 4 ) ) ) ); ++ extr->ips->ips_said.spi = htonl(ntohl(minspi) + ++ (rand_val % ++ (spi_diff + 1))); ++ i++; ++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if(ipsq == NULL) { ++ found_avail = 1; ++ } else { ++ ipsec_sa_put(ipsq); ++ } ++ } ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ if (!found_avail) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: " ++ "found an old ipsec_sa for SA: %s, delete it first.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(EEXIST); ++ } ++ ++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) { ++ extr->ips->ips_flags |= EMT_INBOUND; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: " ++ "existing ipsec_sa not found (this is good) for SA: %s, %s-bound, allocating.\n", ++ sa_len ? sa : " (error)", ++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); ++ ++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ ++ extr->ips->ips_rcvif = NULL; ++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies/HZ; ++ ++ extr->ips->ips_state = SADB_SASTATE_LARVAL; ++ ++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) { ++ extr->ips->ips_life.ipl_allocations.ipl_count += 1; ++ } ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_GETSPI, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ 0, ++ SADB_SASTATE_LARVAL, ++ 0, ++ 0, ++ 0, ++ extr->ips->ips_ref), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " ++ "failed to build the getspi reply message extensions\n"); ++ goto errlab; ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " ++ "failed to build the getspi reply message\n"); ++ SENDERR(-error); ++ } ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " ++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " ++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ if((error = ipsec_sa_add(extr->ips))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: " ++ "failed to add the larval SA=%s with error=%d.\n", ++ sa_len ? sa : " (error)", ++ error); ++ SENDERR(-error); ++ } ++ extr->ips = NULL; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_getspi_parse: " ++ "successful for SA: %s\n", ++ sa_len ? sa : " (error)"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_update_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct ipsec_sa* ipsq; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ struct ipsec_sa *nat_t_ips_saved = NULL; ++#endif ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "error, sa_state=%d must be MATURE=%d\n", ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, ++ SADB_SASTATE_MATURE); ++ SENDERR(EINVAL); ++ } ++ ++ if(extr == NULL || extr->ips == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "error, extr or extr->ips pointer NULL\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if (ipsq == NULL) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "reserved ipsec_sa for SA: %s not found. Call SADB_GETSPI first or call SADB_ADD instead.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(ENOENT); ++ } ++ ++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) { ++ extr->ips->ips_flags |= EMT_INBOUND; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "existing ipsec_sa found (this is good) for SA: %s, %s-bound, updating.\n", ++ sa_len ? sa : " (error)", ++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (extr->ips->ips_natt_sport || extr->ips->ips_natt_dport) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: only updating NAT-T ports " ++ "(%u:%u -> %u:%u)\n", ++ ipsq->ips_natt_sport, ipsq->ips_natt_dport, ++ extr->ips->ips_natt_sport, extr->ips->ips_natt_dport); ++ ++ if (extr->ips->ips_natt_sport) { ++ ipsq->ips_natt_sport = extr->ips->ips_natt_sport; ++ if (ipsq->ips_addr_s->sa_family == AF_INET) { ++ ((struct sockaddr_in *)(ipsq->ips_addr_s))->sin_port = htons(extr->ips->ips_natt_sport); ++ } ++ } ++ ++ if (extr->ips->ips_natt_dport) { ++ ipsq->ips_natt_dport = extr->ips->ips_natt_dport; ++ if (ipsq->ips_addr_d->sa_family == AF_INET) { ++ ((struct sockaddr_in *)(ipsq->ips_addr_d))->sin_port = htons(extr->ips->ips_natt_dport); ++ } ++ } ++ ++ nat_t_ips_saved = extr->ips; ++ extr->ips = ipsq; ++ } ++ else { ++#endif ++ ++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ ++ extr->ips->ips_rcvif = NULL; ++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) { ++ ipsec_sa_put(ipsq); ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "not successful for SA: %s, deleting.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(-error); ++ } ++ ++ extr->ips->ips_life.ipl_addtime.ipl_count = ipsq->ips_life.ipl_addtime.ipl_count; ++ ipsec_sa_put(ipsq); ++ if((error = ipsec_sa_delchain(ipsq))) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "error=%d, trouble deleting intermediate ipsec_sa for SA=%s.\n", ++ error, ++ sa_len ? sa : " (error)"); ++ SENDERR(-error); ++ } ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ } ++#endif ++ ++ spin_unlock_bh(&tdb_lock); ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_UPDATE, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ /* The 3 lifetime extentions should only be sent if non-zero. */ ++ && (extensions[SADB_EXT_LIFETIME_HARD] ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], ++ SADB_EXT_LIFETIME_HARD, ++ extr->ips->ips_life.ipl_allocations.ipl_hard, ++ extr->ips->ips_life.ipl_bytes.ipl_hard, ++ extr->ips->ips_life.ipl_addtime.ipl_hard, ++ extr->ips->ips_life.ipl_usetime.ipl_hard, ++ extr->ips->ips_life.ipl_packets.ipl_hard), ++ extensions_reply) : 1) ++ && (extensions[SADB_EXT_LIFETIME_SOFT] ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], ++ SADB_EXT_LIFETIME_SOFT, ++ extr->ips->ips_life.ipl_allocations.ipl_count, ++ extr->ips->ips_life.ipl_bytes.ipl_count, ++ extr->ips->ips_life.ipl_addtime.ipl_count, ++ extr->ips->ips_life.ipl_usetime.ipl_count, ++ extr->ips->ips_life.ipl_packets.ipl_count), ++ extensions_reply) : 1) ++ && (extr->ips->ips_life.ipl_allocations.ipl_count ++ || extr->ips->ips_life.ipl_bytes.ipl_count ++ || extr->ips->ips_life.ipl_addtime.ipl_count ++ || extr->ips->ips_life.ipl_usetime.ipl_count ++ || extr->ips->ips_life.ipl_packets.ipl_count ++ ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT], ++ SADB_EXT_LIFETIME_CURRENT, ++ extr->ips->ips_life.ipl_allocations.ipl_count, ++ extr->ips->ips_life.ipl_bytes.ipl_count, ++ extr->ips->ips_life.ipl_addtime.ipl_count, ++ extr->ips->ips_life.ipl_usetime.ipl_count, ++ extr->ips->ips_life.ipl_packets.ipl_count), ++ extensions_reply) : 1) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) ++ && (extr->ips->ips_ident_s.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], ++ SADB_EXT_IDENTITY_SRC, ++ extr->ips->ips_ident_s.type, ++ extr->ips->ips_ident_s.id, ++ extr->ips->ips_ident_s.len, ++ extr->ips->ips_ident_s.data), ++ extensions_reply) : 1) ++ && (extr->ips->ips_ident_d.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], ++ SADB_EXT_IDENTITY_DST, ++ extr->ips->ips_ident_d.type, ++ extr->ips->ips_ident_d.id, ++ extr->ips->ips_ident_d.len, ++ extr->ips->ips_ident_d.data), ++ extensions_reply) : 1) ++#if 0 ++ /* FIXME: This won't work yet because I have not finished ++ it. */ ++ && (extr->ips->ips_sens_ ++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], ++ extr->ips->ips_sens_dpd, ++ extr->ips->ips_sens_sens_level, ++ extr->ips->ips_sens_sens_len, ++ extr->ips->ips_sens_sens_bitmap, ++ extr->ips->ips_sens_integ_level, ++ extr->ips->ips_sens_integ_len, ++ extr->ips->ips_sens_integ_bitmap), ++ extensions_reply) : 1) ++#endif ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " ++ "failed to build the update reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " ++ "failed to build the update reply message\n"); ++ SENDERR(-error); ++ } ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " ++ "sending up update reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " ++ "sending up update reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if (nat_t_ips_saved) { ++ /** ++ * As we _really_ update existing SA, we keep tdbq and need to delete ++ * parsed ips (nat_t_ips_saved, was extr->ips). ++ * ++ * goto errlab with extr->ips = nat_t_ips_saved will free it. ++ */ ++ ++ extr->ips = nat_t_ips_saved; ++ ++ error = 0; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse (NAT-T ports): " ++ "successful for SA: %s\n", ++ sa_len ? sa : " (error)"); ++ ++ goto errlab; ++ } ++#endif ++ ++ if((error = ipsec_sa_add(extr->ips))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: " ++ "failed to update the mature SA=%s with error=%d.\n", ++ sa_len ? sa : " (error)", ++ error); ++ SENDERR(-error); ++ } ++ extr->ips = NULL; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_update_parse: " ++ "successful for SA: %s\n", ++ sa_len ? sa : " (error)"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_add_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct ipsec_sa* ipsq; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "error, sa_state=%d must be MATURE=%d\n", ++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, ++ SADB_SASTATE_MATURE); ++ SENDERR(EINVAL); ++ } ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "extr or extr->ips pointer NULL\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if(ipsq != NULL) { ++ ipsec_sa_put(ipsq); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "found an old ipsec_sa for SA%s, delete it first.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(EEXIST); ++ } ++ ++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) { ++ extr->ips->ips_flags |= EMT_INBOUND; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "existing ipsec_sa not found (this is good) for SA%s, %s-bound, allocating.\n", ++ sa_len ? sa : " (error)", ++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out"); ++ ++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/ ++ extr->ips->ips_rcvif = NULL; ++ ++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "not successful for SA: %s, deleting.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(-error); ++ } ++ ++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies / HZ; ++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) { ++ extr->ips->ips_life.ipl_allocations.ipl_count += 1; ++ } ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_ADD, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ /* The 3 lifetime extentions should only be sent if non-zero. */ ++ && (extensions[SADB_EXT_LIFETIME_HARD] ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], ++ SADB_EXT_LIFETIME_HARD, ++ extr->ips->ips_life.ipl_allocations.ipl_hard, ++ extr->ips->ips_life.ipl_bytes.ipl_hard, ++ extr->ips->ips_life.ipl_addtime.ipl_hard, ++ extr->ips->ips_life.ipl_usetime.ipl_hard, ++ extr->ips->ips_life.ipl_packets.ipl_hard), ++ extensions_reply) : 1) ++ && (extensions[SADB_EXT_LIFETIME_SOFT] ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], ++ SADB_EXT_LIFETIME_SOFT, ++ extr->ips->ips_life.ipl_allocations.ipl_soft, ++ extr->ips->ips_life.ipl_bytes.ipl_soft, ++ extr->ips->ips_life.ipl_addtime.ipl_soft, ++ extr->ips->ips_life.ipl_usetime.ipl_soft, ++ extr->ips->ips_life.ipl_packets.ipl_soft), ++ extensions_reply) : 1) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) ++ && (extr->ips->ips_ident_s.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], ++ SADB_EXT_IDENTITY_SRC, ++ extr->ips->ips_ident_s.type, ++ extr->ips->ips_ident_s.id, ++ extr->ips->ips_ident_s.len, ++ extr->ips->ips_ident_s.data), ++ extensions_reply) : 1) ++ && (extr->ips->ips_ident_d.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], ++ SADB_EXT_IDENTITY_DST, ++ extr->ips->ips_ident_d.type, ++ extr->ips->ips_ident_d.id, ++ extr->ips->ips_ident_d.len, ++ extr->ips->ips_ident_d.data), ++ extensions_reply) : 1) ++#if 0 ++ /* FIXME: This won't work yet because I have not finished ++ it. */ ++ && (extr->ips->ips_sens_ ++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], ++ extr->ips->ips_sens_dpd, ++ extr->ips->ips_sens_sens_level, ++ extr->ips->ips_sens_sens_len, ++ extr->ips->ips_sens_sens_bitmap, ++ extr->ips->ips_sens_integ_level, ++ extr->ips->ips_sens_integ_len, ++ extr->ips->ips_sens_integ_bitmap), ++ extensions_reply) : 1) ++#endif ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " ++ "failed to build the add reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " ++ "failed to build the add reply message\n"); ++ SENDERR(-error); ++ } ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " ++ "sending up add reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " ++ "sending up add reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ if((error = ipsec_sa_add(extr->ips))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: " ++ "failed to add the mature SA=%s with error=%d.\n", ++ sa_len ? sa : " (error)", ++ error); ++ SENDERR(-error); ++ } ++ extr->ips = NULL; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_add_parse: " ++ "successful for SA: %s\n", ++ sa_len ? sa : " (error)"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_delete_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ struct ipsec_sa *ipsp; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ int error = 0; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_delete_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_delete_parse: " ++ "extr or extr->ips pointer NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if (ipsp == NULL) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_delete_parse: " ++ "ipsec_sa not found for SA:%s, could not delete.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(ESRCH); ++ } ++ ++ ipsec_sa_put(ipsp); ++ if((error = ipsec_sa_delchain(ipsp))) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_delete_parse: " ++ "error=%d returned trying to delete ipsec_sa for SA:%s.\n", ++ error, ++ sa_len ? sa : " (error)"); ++ SENDERR(-error); ++ } ++ spin_unlock_bh(&tdb_lock); ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_DELETE, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ extr->ips->ips_ref), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " ++ "failed to build the delete reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " ++ "failed to build the delete reply message\n"); ++ SENDERR(-error); ++ } ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " ++ "sending up delete reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: " ++ "sending up delete reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_get_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct ipsec_sa *ipsp; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_get_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_get_parse: " ++ "extr or extr->ips pointer NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ spin_lock_bh(&tdb_lock); ++ ++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if (ipsp == NULL) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " ++ "ipsec_sa not found for SA=%s, could not get.\n", ++ sa_len ? sa : " (error)"); ++ SENDERR(ESRCH); ++ } ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_GET, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ /* The 3 lifetime extentions should only be sent if non-zero. */ ++ && (ipsp->ips_life.ipl_allocations.ipl_count ++ || ipsp->ips_life.ipl_bytes.ipl_count ++ || ipsp->ips_life.ipl_addtime.ipl_count ++ || ipsp->ips_life.ipl_usetime.ipl_count ++ || ipsp->ips_life.ipl_packets.ipl_count ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT], ++ SADB_EXT_LIFETIME_CURRENT, ++ ipsp->ips_life.ipl_allocations.ipl_count, ++ ipsp->ips_life.ipl_bytes.ipl_count, ++ ipsp->ips_life.ipl_addtime.ipl_count, ++ ipsp->ips_life.ipl_usetime.ipl_count, ++ ipsp->ips_life.ipl_packets.ipl_count), ++ extensions_reply) : 1) ++ && (ipsp->ips_life.ipl_allocations.ipl_hard ++ || ipsp->ips_life.ipl_bytes.ipl_hard ++ || ipsp->ips_life.ipl_addtime.ipl_hard ++ || ipsp->ips_life.ipl_usetime.ipl_hard ++ || ipsp->ips_life.ipl_packets.ipl_hard ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD], ++ SADB_EXT_LIFETIME_HARD, ++ ipsp->ips_life.ipl_allocations.ipl_hard, ++ ipsp->ips_life.ipl_bytes.ipl_hard, ++ ipsp->ips_life.ipl_addtime.ipl_hard, ++ ipsp->ips_life.ipl_usetime.ipl_hard, ++ ipsp->ips_life.ipl_packets.ipl_hard), ++ extensions_reply) : 1) ++ && (ipsp->ips_life.ipl_allocations.ipl_soft ++ || ipsp->ips_life.ipl_bytes.ipl_soft ++ || ipsp->ips_life.ipl_addtime.ipl_soft ++ || ipsp->ips_life.ipl_usetime.ipl_soft ++ || ipsp->ips_life.ipl_packets.ipl_soft ++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT], ++ SADB_EXT_LIFETIME_SOFT, ++ ipsp->ips_life.ipl_allocations.ipl_soft, ++ ipsp->ips_life.ipl_bytes.ipl_soft, ++ ipsp->ips_life.ipl_addtime.ipl_soft, ++ ipsp->ips_life.ipl_usetime.ipl_soft, ++ ipsp->ips_life.ipl_packets.ipl_soft), ++ extensions_reply) : 1) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) ++ && (extr->ips->ips_addr_p ++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_PROXY], ++ SADB_EXT_ADDRESS_PROXY, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_p), ++ extensions_reply) : 1) ++#if 0 ++ /* FIXME: This won't work yet because the keys are not ++ stored directly in the ipsec_sa. They are stored as ++ contexts. */ ++ && (extr->ips->ips_key_a_size ++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_AUTH], ++ SADB_EXT_KEY_AUTH, ++ extr->ips->ips_key_a_size * 8, ++ extr->ips->ips_key_a), ++ extensions_reply) : 1) ++ /* FIXME: This won't work yet because the keys are not ++ stored directly in the ipsec_sa. They are stored as ++ key schedules. */ ++ && (extr->ips->ips_key_e_size ++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_ENCRYPT], ++ SADB_EXT_KEY_ENCRYPT, ++ extr->ips->ips_key_e_size * 8, ++ extr->ips->ips_key_e), ++ extensions_reply) : 1) ++#endif ++ && (extr->ips->ips_ident_s.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC], ++ SADB_EXT_IDENTITY_SRC, ++ extr->ips->ips_ident_s.type, ++ extr->ips->ips_ident_s.id, ++ extr->ips->ips_ident_s.len, ++ extr->ips->ips_ident_s.data), ++ extensions_reply) : 1) ++ && (extr->ips->ips_ident_d.data ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST], ++ SADB_EXT_IDENTITY_DST, ++ extr->ips->ips_ident_d.type, ++ extr->ips->ips_ident_d.id, ++ extr->ips->ips_ident_d.len, ++ extr->ips->ips_ident_d.data), ++ extensions_reply) : 1) ++#if 0 ++ /* FIXME: This won't work yet because I have not finished ++ it. */ ++ && (extr->ips->ips_sens_ ++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY], ++ extr->ips->ips_sens_dpd, ++ extr->ips->ips_sens_sens_level, ++ extr->ips->ips_sens_sens_len, ++ extr->ips->ips_sens_sens_bitmap, ++ extr->ips->ips_sens_integ_level, ++ extr->ips->ips_sens_integ_len, ++ extr->ips->ips_sens_integ_bitmap), ++ extensions_reply) : 1) ++#endif ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " ++ "failed to build the get reply message extensions\n"); ++ ipsec_sa_put(ipsp); ++ spin_unlock_bh(&tdb_lock); ++ SENDERR(-error); ++ } ++ ++ ipsec_sa_put(ipsp); ++ spin_unlock_bh(&tdb_lock); ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " ++ "failed to build the get reply message\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_upmsg(sk->socket, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " ++ "failed to send the get reply message\n"); ++ SENDERR(-error); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: " ++ "succeeded in sending get reply message.\n"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_acquire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_acquire_parse: .\n"); ++ ++ /* XXX I don't know if we want an upper bound, since userspace may ++ want to register itself for an satype > SADB_SATYPE_MAX. */ ++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_acquire_parse: " ++ "SATYPE=%d invalid.\n", ++ satype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(pfkey_registered_sockets[satype])) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " ++ "no sockets registered for SAtype=%d(%s).\n", ++ satype, ++ satype2name(satype)); ++ SENDERR(EPROTONOSUPPORT); ++ } ++ ++ for(pfkey_socketsp = pfkey_registered_sockets[satype]; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " ++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: " ++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_register_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_parse: .\n"); ++ ++ /* XXX I don't know if we want an upper bound, since userspace may ++ want to register itself for an satype > SADB_SATYPE_MAX. */ ++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_parse: " ++ "SATYPE=%d invalid.\n", ++ satype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!pfkey_list_insert_socket(sk->socket, ++ &(pfkey_registered_sockets[satype]))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_parse: " ++ "SATYPE=%02d(%s) successfully registered by KMd (pid=%d).\n", ++ satype, ++ satype2name(satype), ++ key_pid(sk)); ++ }; ++ ++ /* send up register msg with supported SATYPE algos */ ++ ++ error=pfkey_register_reply(satype, (struct sadb_msg*)extensions[SADB_EXT_RESERVED]); ++ errlab: ++ return error; ++} ++ ++int ++pfkey_register_reply(int satype, struct sadb_msg *sadb_msg) ++{ ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ struct supported_list *pfkey_supported_listp; ++ unsigned int alg_num_a = 0, alg_num_e = 0; ++ struct sadb_alg *alg_a = NULL, *alg_e = NULL, *alg_ap = NULL, *alg_ep = NULL; ++ int error = 0; ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "SAtype=%d unspecified or unknown.\n", ++ satype); ++ SENDERR(EINVAL); ++ } ++ if(!(pfkey_registered_sockets[satype])) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "no sockets registered for SAtype=%d(%s).\n", ++ satype, ++ satype2name(satype)); ++ SENDERR(EPROTONOSUPPORT); ++ } ++ /* send up register msg with supported SATYPE algos */ ++ pfkey_supported_listp = pfkey_supported_list[satype]; ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "pfkey_supported_list[%d]=0p%p\n", ++ satype, ++ pfkey_supported_list[satype]); ++ while(pfkey_supported_listp) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "checking supported=0p%p\n", ++ pfkey_supported_listp); ++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "adding auth alg.\n"); ++ alg_num_a++; ++ } ++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "adding encrypt alg.\n"); ++ alg_num_e++; ++ } ++ pfkey_supported_listp = pfkey_supported_listp->next; ++ } ++ ++ if(alg_num_a) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "allocating %lu bytes for auth algs.\n", ++ (unsigned long) (alg_num_a * sizeof(struct sadb_alg))); ++ if((alg_a = kmalloc(alg_num_a * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "auth alg memory allocation error\n"); ++ SENDERR(ENOMEM); ++ } ++ alg_ap = alg_a; ++ } ++ ++ if(alg_num_e) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "allocating %lu bytes for enc algs.\n", ++ (unsigned long) (alg_num_e * sizeof(struct sadb_alg))); ++ if((alg_e = kmalloc(alg_num_e * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "enc alg memory allocation error\n"); ++ SENDERR(ENOMEM); ++ } ++ alg_ep = alg_e; ++ } ++ ++ pfkey_supported_listp = pfkey_supported_list[satype]; ++ while(pfkey_supported_listp) { ++ if(alg_num_a) { ++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) { ++ alg_ap->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id; ++ alg_ap->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen; ++ alg_ap->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits; ++ alg_ap->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits; ++ alg_ap->sadb_alg_reserved = 0; ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_register_reply: " ++ "adding auth=0p%p\n", ++ alg_ap); ++ alg_ap++; ++ } ++ } ++ if(alg_num_e) { ++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) { ++ alg_ep->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id; ++ alg_ep->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen; ++ alg_ep->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits; ++ alg_ep->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits; ++ alg_ep->sadb_alg_reserved = 0; ++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose, ++ "klips_debug:pfkey_register_reply: " ++ "adding encrypt=0p%p\n", ++ alg_ep); ++ alg_ep++; ++ } ++ } ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_register_reply: " ++ "found satype=%d(%s) exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_supported_listp->supportedp->supported_alg_exttype, ++ pfkey_supported_listp->supportedp->supported_alg_id, ++ pfkey_supported_listp->supportedp->supported_alg_ivlen, ++ pfkey_supported_listp->supportedp->supported_alg_minbits, ++ pfkey_supported_listp->supportedp->supported_alg_maxbits); ++ pfkey_supported_listp = pfkey_supported_listp->next; ++ } ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_REGISTER, ++ satype, ++ 0, ++ sadb_msg? sadb_msg->sadb_msg_seq : ++pfkey_msg_seq, ++ sadb_msg? sadb_msg->sadb_msg_pid: current->pid), ++ extensions_reply) && ++ (alg_num_a ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_AUTH], ++ SADB_EXT_SUPPORTED_AUTH, ++ alg_num_a, ++ alg_a), ++ extensions_reply) : 1) && ++ (alg_num_e ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_ENCRYPT], ++ SADB_EXT_SUPPORTED_ENCRYPT, ++ alg_num_e, ++ alg_e), ++ extensions_reply) : 1))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "failed to build the register message extensions_reply\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "failed to build the register message\n"); ++ SENDERR(-error); ++ } ++ /* this should go to all registered sockets for that satype only */ ++ for(pfkey_socketsp = pfkey_registered_sockets[satype]; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: " ++ "sending up register message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ if(alg_a) { ++ kfree(alg_a); ++ } ++ if(alg_e) { ++ kfree(alg_e); ++ } ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_expire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct socket_list *pfkey_socketsp; ++#ifdef CONFIG_IPSEC_DEBUG ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_expire_parse: .\n"); ++ ++ if(pfkey_open_sockets) { ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: " ++ "sending up expire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: " ++ "sending up expire reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ } ++ ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_flush_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ uint8_t proto = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_flush_parse: " ++ "flushing type %d SAs\n", ++ satype); ++ ++ if(satype && !(proto = satype2proto(satype))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_flush_parse: " ++ "satype %d lookup failed.\n", ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); ++ SENDERR(EINVAL); ++ } ++ ++ if ((error = ipsec_sadb_cleanup(proto))) { ++ SENDERR(-error); ++ } ++ ++ if(pfkey_open_sockets) { ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: " ++ "sending up flush reply message for satype=%d(%s) (proto=%d) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ proto, ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: " ++ "sending up flush reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ } ++ ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_dump_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_dump_parse: .\n"); ++ ++ SENDERR(ENOSYS); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_promisc_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_promisc_parse: .\n"); ++ ++ SENDERR(ENOSYS); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_pchange_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_pchange_parse: .\n"); ++ ++ SENDERR(ENOSYS); ++ errlab: ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_grpsa_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ struct ipsec_sa *ips1p, *ips2p, *ipsp; ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ char sa1[SATOT_BUF], sa2[SATOT_BUF]; ++ size_t sa_len1, sa_len2 = 0; ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ if(extr == NULL || extr->ips == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "extr or extr->ips is NULL, fatal.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ sa_len1 = satot(&extr->ips->ips_said, 0, sa1, sizeof(sa1)); ++ if(extr->ips2 != NULL) { ++ sa_len2 = satot(&extr->ips2->ips_said, 0, sa2, sizeof(sa2)); ++ } ++ ++ spin_lock_bh(&tdb_lock); ++ ++ ips1p = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if(ips1p == NULL) { ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "reserved ipsec_sa for SA1: %s not found. Call SADB_ADD/UPDATE first.\n", ++ sa_len1 ? sa1 : " (error)"); ++ SENDERR(ENOENT); ++ } ++ if(extr->ips2) { /* GRPSA */ ++ ips2p = ipsec_sa_getbyid(&(extr->ips2->ips_said)); ++ if(ips2p == NULL) { ++ ipsec_sa_put(ips1p); ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "reserved ipsec_sa for SA2: %s not found. Call SADB_ADD/UPDATE first.\n", ++ sa_len2 ? sa2 : " (error)"); ++ SENDERR(ENOENT); ++ } ++ ++ /* Is either one already linked? */ ++ if(ips1p->ips_onext) { ++ ipsec_sa_put(ips1p); ++ ipsec_sa_put(ips2p); ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "ipsec_sa for SA: %s is already linked.\n", ++ sa_len1 ? sa1 : " (error)"); ++ SENDERR(EEXIST); ++ } ++ if(ips2p->ips_inext) { ++ ipsec_sa_put(ips1p); ++ ipsec_sa_put(ips2p); ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "ipsec_sa for SA: %s is already linked.\n", ++ sa_len2 ? sa2 : " (error)"); ++ SENDERR(EEXIST); ++ } ++ ++ /* Is extr->ips already linked to extr->ips2? */ ++ ipsp = ips2p; ++ while(ipsp) { ++ if(ipsp == ips1p) { ++ ipsec_sa_put(ips1p); ++ ipsec_sa_put(ips2p); ++ spin_unlock_bh(&tdb_lock); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "ipsec_sa for SA: %s is already linked to %s.\n", ++ sa_len1 ? sa1 : " (error)", ++ sa_len2 ? sa2 : " (error)"); ++ SENDERR(EEXIST); ++ } ++ ipsp = ipsp->ips_onext; ++ } ++ ++ /* link 'em */ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "linking ipsec_sa SA: %s with %s.\n", ++ sa_len1 ? sa1 : " (error)", ++ sa_len2 ? sa2 : " (error)"); ++ ips1p->ips_onext = ips2p; ++ ips2p->ips_inext = ips1p; ++ } else { /* UNGRPSA */ ++ ipsec_sa_put(ips1p); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_grpsa_parse: " ++ "unlinking ipsec_sa SA: %s.\n", ++ sa_len1 ? sa1 : " (error)"); ++ while(ips1p->ips_onext) { ++ ips1p = ips1p->ips_onext; ++ } ++ while(ips1p->ips_inext) { ++ ipsp = ips1p; ++ ips1p = ips1p->ips_inext; ++ ipsec_sa_put(ips1p); ++ ipsp->ips_inext = NULL; ++ ipsec_sa_put(ipsp); ++ ips1p->ips_onext = NULL; ++ } ++ } ++ ++ spin_unlock_bh(&tdb_lock); ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_X_GRPSA, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) ++ && (extr->ips2 ++ ? (pfkey_safe_build(error = pfkey_x_satype_build(&extensions_reply[SADB_X_EXT_SATYPE2], ++ ((struct sadb_x_satype*)extensions[SADB_X_EXT_SATYPE2])->sadb_x_satype_satype ++ /* proto2satype(extr->ips2->ips_said.proto) */), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_X_EXT_SA2], ++ SADB_X_EXT_SA2, ++ extr->ips2->ips_said.spi, ++ extr->ips2->ips_replaywin, ++ extr->ips2->ips_state, ++ extr->ips2->ips_authalg, ++ extr->ips2->ips_encalg, ++ extr->ips2->ips_flags, ++ extr->ips2->ips_ref), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST2], ++ SADB_X_EXT_ADDRESS_DST2, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips2->ips_addr_d), ++ extensions_reply) ) : 1 ) ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " ++ "failed to build the x_grpsa reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " ++ "failed to build the x_grpsa reply message\n"); ++ SENDERR(-error); ++ } ++ ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " ++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " ++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: " ++ "succeeded in sending x_grpsa reply message.\n"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_addflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++#ifdef CONFIG_IPSEC_DEBUG ++ char buf1[64], buf2[64]; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ip_address srcflow, dstflow, srcmask, dstmask; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ memset((caddr_t)&srcflow, 0, sizeof(srcflow)); ++ memset((caddr_t)&dstflow, 0, sizeof(dstflow)); ++ memset((caddr_t)&srcmask, 0, sizeof(srcmask)); ++ memset((caddr_t)&dstmask, 0, sizeof(dstmask)); ++ ++ if(!extr || !(extr->ips) || !(extr->eroute)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "missing extr, ipsec_sa or eroute data.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ srcflow.u.v4.sin_family = AF_INET; ++ dstflow.u.v4.sin_family = AF_INET; ++ srcmask.u.v4.sin_family = AF_INET; ++ dstmask.u.v4.sin_family = AF_INET; ++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src; ++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst; ++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src; ++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_pfkey) { ++ subnettoa(extr->eroute->er_eaddr.sen_ip_src, ++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst, ++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "calling breakeroute and/or makeroute for %s->%s\n", ++ buf1, buf2); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_INFLOW) { ++/* if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) */ ++ struct ipsec_sa *ipsp, *ipsq; ++ char sa[SATOT_BUF]; ++ size_t sa_len; ++ ++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said)); ++ if(ipsq == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "ipsec_sa not found, cannot set incoming policy.\n"); ++ SENDERR(ENOENT); ++ } ++ ++ ipsp = ipsq; ++ while(ipsp && ipsp->ips_said.proto != IPPROTO_IPIP) { ++ ipsp = ipsp->ips_inext; ++ } ++ ++ if(ipsp == NULL) { ++ ipsec_sa_put(ipsq); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "SA chain does not have an IPIP SA, cannot set incoming policy.\n"); ++ SENDERR(ENOENT); ++ } ++ ++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa)); ++ ++ ipsp->ips_flags |= SADB_X_SAFLAGS_INFLOW; ++ ipsp->ips_flow_s = srcflow; ++ ipsp->ips_flow_d = dstflow; ++ ipsp->ips_mask_s = srcmask; ++ ipsp->ips_mask_d = dstmask; ++ ++ ipsec_sa_put(ipsq); ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "inbound eroute, setting incoming policy information in IPIP ipsec_sa for SA: %s.\n", ++ sa_len ? sa : " (error)"); ++ } else { ++ struct sk_buff *first = NULL, *last = NULL; ++ ++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_REPLACEFLOW) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "REPLACEFLOW flag set, calling breakeroute.\n"); ++ if ((error = ipsec_breakroute(&(extr->eroute->er_eaddr), ++ &(extr->eroute->er_emask), ++ &first, &last))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "breakeroute returned %d. first=0p%p, last=0p%p\n", ++ error, ++ first, ++ last); ++ if(first != NULL) { ++ ipsec_kfree_skb(first); ++ } ++ if(last != NULL) { ++ ipsec_kfree_skb(last); ++ } ++ SENDERR(-error); ++ } ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "calling makeroute.\n"); ++ ++ if ((error = ipsec_makeroute(&(extr->eroute->er_eaddr), ++ &(extr->eroute->er_emask), ++ extr->ips->ips_said, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid, ++ NULL, ++ &(extr->ips->ips_ident_s), ++ &(extr->ips->ips_ident_d)))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "makeroute returned %d.\n", error); ++ SENDERR(-error); ++ } ++ if(first != NULL) { ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "first=0p%p HOLD packet re-injected.\n", ++ first); ++ DEV_QUEUE_XMIT(first, first->dev, SOPRI_NORMAL); ++ } ++ if(last != NULL) { ++ KLIPS_PRINT(debug_eroute, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "last=0p%p HOLD packet re-injected.\n", ++ last); ++ DEV_QUEUE_XMIT(last, last->dev, SOPRI_NORMAL); ++ } ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "makeroute call successful.\n"); ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_X_ADDFLOW, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ && (extensions[SADB_EXT_ADDRESS_SRC] ++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_s), ++ extensions_reply) : 1) ++ && (extensions[SADB_EXT_ADDRESS_DST] ++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ extr->ips->ips_addr_d), ++ extensions_reply) : 1) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW], ++ SADB_X_EXT_ADDRESS_SRC_FLOW, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&srcflow), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW], ++ SADB_X_EXT_ADDRESS_DST_FLOW, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&dstflow), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK], ++ SADB_X_EXT_ADDRESS_SRC_MASK, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&srcmask), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK], ++ SADB_X_EXT_ADDRESS_DST_MASK, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&dstmask), ++ extensions_reply) ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " ++ "failed to build the x_addflow reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " ++ "failed to build the x_addflow reply message\n"); ++ SENDERR(-error); ++ } ++ ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " ++ "sending up x_addflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: " ++ "sending up x_addflow reply message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ extr->ips->ips_said.proto, ++ pfkey_socketsp->socketp); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_addflow_parse: " ++ "extr->ips cleaned up and freed.\n"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_delflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++#ifdef CONFIG_IPSEC_DEBUG ++ char buf1[64], buf2[64]; ++#endif /* CONFIG_IPSEC_DEBUG */ ++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_reply = NULL; ++ struct socket_list *pfkey_socketsp; ++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype; ++ ip_address srcflow, dstflow, srcmask, dstmask; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: .\n"); ++ ++ pfkey_extensions_init(extensions_reply); ++ ++ memset((caddr_t)&srcflow, 0, sizeof(srcflow)); ++ memset((caddr_t)&dstflow, 0, sizeof(dstflow)); ++ memset((caddr_t)&srcmask, 0, sizeof(srcmask)); ++ memset((caddr_t)&dstmask, 0, sizeof(dstmask)); ++ ++ if(!extr || !(extr->ips)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "extr, or extr->ips is NULL, fatal\n"); ++ SENDERR(EINVAL); ++ } ++ ++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_CLEARFLOW) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "CLEARFLOW flag set, calling cleareroutes.\n"); ++ if ((error = ipsec_cleareroutes())) ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "cleareroutes returned %d.\n", error); ++ SENDERR(-error); ++ } else { ++ struct sk_buff *first = NULL, *last = NULL; ++ ++ if(!(extr->eroute)) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "extr->eroute is NULL, fatal.\n"); ++ SENDERR(EINVAL); ++ } ++ ++ srcflow.u.v4.sin_family = AF_INET; ++ dstflow.u.v4.sin_family = AF_INET; ++ srcmask.u.v4.sin_family = AF_INET; ++ dstmask.u.v4.sin_family = AF_INET; ++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src; ++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst; ++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src; ++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst; ++ ++#ifdef CONFIG_IPSEC_DEBUG ++ if (debug_pfkey) { ++ subnettoa(extr->eroute->er_eaddr.sen_ip_src, ++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); ++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst, ++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "calling breakeroute for %s->%s\n", ++ buf1, buf2); ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ error = ipsec_breakroute(&(extr->eroute->er_eaddr), ++ &(extr->eroute->er_emask), ++ &first, &last); ++ if(error) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "breakeroute returned %d. first=0p%p, last=0p%p\n", ++ error, ++ first, ++ last); ++ } ++ if(first != NULL) { ++ ipsec_kfree_skb(first); ++ } ++ if(last != NULL) { ++ ipsec_kfree_skb(last); ++ } ++ if(error) { ++ SENDERR(-error); ++ } ++ } ++ ++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0], ++ SADB_X_DELFLOW, ++ satype, ++ 0, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq, ++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW], ++ SADB_X_EXT_ADDRESS_SRC_FLOW, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&srcflow), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW], ++ SADB_X_EXT_ADDRESS_DST_FLOW, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&dstflow), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK], ++ SADB_X_EXT_ADDRESS_SRC_MASK, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&srcmask), ++ extensions_reply) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK], ++ SADB_X_EXT_ADDRESS_DST_MASK, ++ 0, /*extr->ips->ips_said.proto,*/ ++ 0, ++ (struct sockaddr*)&dstmask), ++ extensions_reply) ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " ++ "failed to build the x_delflow reply message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " ++ "failed to build the x_delflow reply message\n"); ++ SENDERR(-error); ++ } ++ ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " ++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: " ++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_delflow_parse: " ++ "extr->ips cleaned up and freed.\n"); ++ ++ errlab: ++ if (pfkey_reply) { ++ pfkey_msg_free(&pfkey_reply); ++ } ++ pfkey_extensions_free(extensions_reply); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_msg_debug_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ int error = 0; ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_x_msg_debug_parse: .\n"); ++ ++/* errlab:*/ ++ return error; ++} ++ ++/* pfkey_expire expects the ipsec_sa table to be locked before being called. */ ++int ++pfkey_expire(struct ipsec_sa *ipsp, int hard) ++{ ++ struct sadb_ext *extensions[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_msg = NULL; ++ struct socket_list *pfkey_socketsp; ++ int error = 0; ++ uint8_t satype; ++ ++ pfkey_extensions_init(extensions); ++ ++ if(!(satype = proto2satype(ipsp->ips_said.proto))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_expire: " ++ "satype lookup for protocol %d lookup failed.\n", ++ ipsp->ips_said.proto); ++ SENDERR(EINVAL); ++ } ++ ++ if(!pfkey_open_sockets) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " ++ "no sockets listening.\n"); ++ SENDERR(EPROTONOSUPPORT); ++ } ++ ++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0], ++ SADB_EXPIRE, ++ satype, ++ 0, ++ ++pfkey_msg_seq, ++ 0), ++ extensions) ++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions[SADB_EXT_SA], ++ SADB_EXT_SA, ++ ipsp->ips_said.spi, ++ ipsp->ips_replaywin, ++ ipsp->ips_state, ++ ipsp->ips_authalg, ++ ipsp->ips_encalg, ++ ipsp->ips_flags, ++ ipsp->ips_ref), ++ extensions) ++ && pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_CURRENT], ++ SADB_EXT_LIFETIME_CURRENT, ++ ipsp->ips_life.ipl_allocations.ipl_count, ++ ipsp->ips_life.ipl_bytes.ipl_count, ++ ipsp->ips_life.ipl_addtime.ipl_count, ++ ipsp->ips_life.ipl_usetime.ipl_count, ++ ipsp->ips_life.ipl_packets.ipl_count), ++ extensions) ++ && (hard ? ++ pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_HARD], ++ SADB_EXT_LIFETIME_HARD, ++ ipsp->ips_life.ipl_allocations.ipl_hard, ++ ipsp->ips_life.ipl_bytes.ipl_hard, ++ ipsp->ips_life.ipl_addtime.ipl_hard, ++ ipsp->ips_life.ipl_usetime.ipl_hard, ++ ipsp->ips_life.ipl_packets.ipl_hard), ++ extensions) ++ : pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_SOFT], ++ SADB_EXT_LIFETIME_SOFT, ++ ipsp->ips_life.ipl_allocations.ipl_soft, ++ ipsp->ips_life.ipl_bytes.ipl_soft, ++ ipsp->ips_life.ipl_addtime.ipl_soft, ++ ipsp->ips_life.ipl_usetime.ipl_soft, ++ ipsp->ips_life.ipl_packets.ipl_soft), ++ extensions)) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ 0, /* ipsp->ips_said.proto, */ ++ 0, ++ ipsp->ips_addr_s), ++ extensions) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ 0, /* ipsp->ips_said.proto, */ ++ 0, ++ ipsp->ips_addr_d), ++ extensions))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " ++ "failed to build the expire message extensions\n"); ++ spin_unlock(&tdb_lock); ++ goto errlab; ++ } ++ ++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " ++ "failed to build the expire message\n"); ++ SENDERR(-error); ++ } ++ ++ for(pfkey_socketsp = pfkey_open_sockets; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " ++ "sending up expire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: " ++ "sending up expire message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ ipsp->ips_said.proto, ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ if (pfkey_msg) { ++ pfkey_msg_free(&pfkey_msg); ++ } ++ pfkey_extensions_free(extensions); ++ return error; ++} ++ ++int ++pfkey_acquire(struct ipsec_sa *ipsp) ++{ ++ struct sadb_ext *extensions[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_msg = NULL; ++ struct socket_list *pfkey_socketsp; ++ int error = 0; ++ struct sadb_comb comb[] = { ++ /* auth; encrypt; flags; */ ++ /* auth_minbits; auth_maxbits; encrypt_minbits; encrypt_maxbits; */ ++ /* reserved; soft_allocations; hard_allocations; soft_bytes; hard_bytes; */ ++ /* soft_addtime; hard_addtime; soft_usetime; hard_usetime; */ ++ /* soft_packets; hard_packets; */ ++ { SADB_AALG_MD5HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS, ++ 128, 128, 168, 168, ++ 0, 0, 0, 0, 0, ++ 57600, 86400, 57600, 86400, ++ 0, 0 }, ++ { SADB_AALG_SHA1HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS, ++ 160, 160, 168, 168, ++ 0, 0, 0, 0, 0, ++ 57600, 86400, 57600, 86400, ++ 0, 0 } ++ }; ++ ++ /* XXX This should not be hard-coded. It should be taken from the spdb */ ++ uint8_t satype = SADB_SATYPE_ESP; ++ ++ pfkey_extensions_init(extensions); ++ ++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: " ++ "SAtype=%d unspecified or unknown.\n", ++ satype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(pfkey_registered_sockets[satype])) { ++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " ++ "no sockets registered for SAtype=%d(%s).\n", ++ satype, ++ satype2name(satype)); ++ SENDERR(EPROTONOSUPPORT); ++ } ++ ++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0], ++ SADB_ACQUIRE, ++ satype, ++ 0, ++ ++pfkey_msg_seq, ++ 0), ++ extensions) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ ipsp->ips_transport_protocol, ++ 0, ++ ipsp->ips_addr_s), ++ extensions) ++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ ipsp->ips_transport_protocol, ++ 0, ++ ipsp->ips_addr_d), ++ extensions) ++#if 0 ++ && (ipsp->ips_addr_p ++ ? pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_PROXY], ++ SADB_EXT_ADDRESS_PROXY, ++ ipsp->ips_transport_protocol, ++ 0, ++ ipsp->ips_addr_p), ++ extensions) : 1) ++#endif ++ && (ipsp->ips_ident_s.type != SADB_IDENTTYPE_RESERVED ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_SRC], ++ SADB_EXT_IDENTITY_SRC, ++ ipsp->ips_ident_s.type, ++ ipsp->ips_ident_s.id, ++ ipsp->ips_ident_s.len, ++ ipsp->ips_ident_s.data), ++ extensions) : 1) ++ ++ && (ipsp->ips_ident_d.type != SADB_IDENTTYPE_RESERVED ++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_DST], ++ SADB_EXT_IDENTITY_DST, ++ ipsp->ips_ident_d.type, ++ ipsp->ips_ident_d.id, ++ ipsp->ips_ident_d.len, ++ ipsp->ips_ident_d.data), ++ extensions) : 1) ++#if 0 ++ /* FIXME: This won't work yet because I have not finished ++ it. */ ++ && (ipsp->ips_sens_ ++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions[SADB_EXT_SENSITIVITY], ++ ipsp->ips_sens_dpd, ++ ipsp->ips_sens_sens_level, ++ ipsp->ips_sens_sens_len, ++ ipsp->ips_sens_sens_bitmap, ++ ipsp->ips_sens_integ_level, ++ ipsp->ips_sens_integ_len, ++ ipsp->ips_sens_integ_bitmap), ++ extensions) : 1) ++#endif ++ && pfkey_safe_build(error = pfkey_prop_build(&extensions[SADB_EXT_PROPOSAL], ++ 64, /* replay */ ++ sizeof(comb)/sizeof(struct sadb_comb), ++ &(comb[0])), ++ extensions) ++ )) { ++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " ++ "failed to build the acquire message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { ++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " ++ "failed to build the acquire message\n"); ++ SENDERR(-error); ++ } ++ ++#if KLIPS_PFKEY_ACQUIRE_LOSSAGE > 0 ++ if(sysctl_ipsec_regress_pfkey_lossage) { ++ return(0); ++ } ++#endif ++ ++ /* this should go to all registered sockets for that satype only */ ++ for(pfkey_socketsp = pfkey_registered_sockets[satype]; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { ++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: " ++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: " ++ "sending up acquire message for satype=%d(%s) to socket=0p%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ if (pfkey_msg) { ++ pfkey_msg_free(&pfkey_msg); ++ } ++ pfkey_extensions_free(extensions); ++ return error; ++} ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++int ++pfkey_nat_t_new_mapping(struct ipsec_sa *ipsp, struct sockaddr *ipaddr, ++ __u16 sport) ++{ ++ struct sadb_ext *extensions[SADB_EXT_MAX+1]; ++ struct sadb_msg *pfkey_msg = NULL; ++ struct socket_list *pfkey_socketsp; ++ int error = 0; ++ uint8_t satype = (ipsp->ips_said.proto==IPPROTO_ESP) ? SADB_SATYPE_ESP : 0; ++ ++ /* Construct SADB_X_NAT_T_NEW_MAPPING message */ ++ ++ pfkey_extensions_init(extensions); ++ ++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "SAtype=%d unspecified or unknown.\n", ++ satype); ++ SENDERR(EINVAL); ++ } ++ ++ if(!(pfkey_registered_sockets[satype])) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "no sockets registered for SAtype=%d(%s).\n", ++ satype, ++ satype2name(satype)); ++ SENDERR(EPROTONOSUPPORT); ++ } ++ ++ if (!(pfkey_safe_build ++ (error = pfkey_msg_hdr_build(&extensions[0], SADB_X_NAT_T_NEW_MAPPING, ++ satype, 0, ++pfkey_msg_seq, 0), extensions) ++ /* SA */ ++ && pfkey_safe_build ++ (error = pfkey_sa_build(&extensions[SADB_EXT_SA], ++ SADB_EXT_SA, ipsp->ips_said.spi, 0, 0, 0, 0, 0), extensions) ++ /* ADDRESS_SRC = old addr */ ++ && pfkey_safe_build ++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ipsp->ips_said.proto, 0, ipsp->ips_addr_s), ++ extensions) ++ /* NAT_T_SPORT = old port */ ++ && pfkey_safe_build ++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_SPORT], ++ SADB_X_EXT_NAT_T_SPORT, ipsp->ips_natt_sport), extensions) ++ /* ADDRESS_DST = new addr */ ++ && pfkey_safe_build ++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ipsp->ips_said.proto, 0, ipaddr), extensions) ++ /* NAT_T_DPORT = new port */ ++ && pfkey_safe_build ++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_DPORT], ++ SADB_X_EXT_NAT_T_DPORT, sport), extensions) ++ )) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "failed to build the nat_t_new_mapping message extensions\n"); ++ SENDERR(-error); ++ } ++ ++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "failed to build the nat_t_new_mapping message\n"); ++ SENDERR(-error); ++ } ++ ++ /* this should go to all registered sockets for that satype only */ ++ for(pfkey_socketsp = pfkey_registered_sockets[satype]; ++ pfkey_socketsp; ++ pfkey_socketsp = pfkey_socketsp->next) { ++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p failed with error=%d.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp, ++ error); ++ SENDERR(-error); ++ } ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: " ++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p succeeded.\n", ++ satype, ++ satype2name(satype), ++ pfkey_socketsp->socketp); ++ } ++ ++ errlab: ++ if (pfkey_msg) { ++ pfkey_msg_free(&pfkey_msg); ++ } ++ pfkey_extensions_free(extensions); ++ return error; ++} ++ ++DEBUG_NO_STATIC int ++pfkey_x_nat_t_new_mapping_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr) ++{ ++ /* SADB_X_NAT_T_NEW_MAPPING not used in kernel */ ++ return -EINVAL; ++} ++#endif ++ ++DEBUG_NO_STATIC int (*ext_processors[SADB_EXT_MAX+1])(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) = ++{ ++ NULL, /* pfkey_msg_process, */ ++ pfkey_sa_process, ++ pfkey_lifetime_process, ++ pfkey_lifetime_process, ++ pfkey_lifetime_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_key_process, ++ pfkey_key_process, ++ pfkey_ident_process, ++ pfkey_ident_process, ++ pfkey_sens_process, ++ pfkey_prop_process, ++ pfkey_supported_process, ++ pfkey_supported_process, ++ pfkey_spirange_process, ++ pfkey_x_kmprivate_process, ++ pfkey_x_satype_process, ++ pfkey_sa_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_address_process, ++ pfkey_x_debug_process, ++ pfkey_x_protocol_process ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ , ++ pfkey_x_nat_t_type_process, ++ pfkey_x_nat_t_port_process, ++ pfkey_x_nat_t_port_process, ++ pfkey_address_process ++#endif ++}; ++ ++ ++DEBUG_NO_STATIC int (*msg_parsers[SADB_MAX +1])(struct sock *sk, struct sadb_ext *extensions[], struct pfkey_extracted_data* extr) ++ = ++{ ++ NULL, /* RESERVED */ ++ pfkey_getspi_parse, ++ pfkey_update_parse, ++ pfkey_add_parse, ++ pfkey_delete_parse, ++ pfkey_get_parse, ++ pfkey_acquire_parse, ++ pfkey_register_parse, ++ pfkey_expire_parse, ++ pfkey_flush_parse, ++ pfkey_dump_parse, ++ pfkey_x_promisc_parse, ++ pfkey_x_pchange_parse, ++ pfkey_x_grpsa_parse, ++ pfkey_x_addflow_parse, ++ pfkey_x_delflow_parse, ++ pfkey_x_msg_debug_parse ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ , pfkey_x_nat_t_new_mapping_parse ++#endif ++}; ++ ++int ++pfkey_build_reply(struct sadb_msg *pfkey_msg, struct pfkey_extracted_data *extr, ++ struct sadb_msg **pfkey_reply) ++{ ++ struct sadb_ext *extensions[SADB_EXT_MAX+1]; ++ int error = 0; ++ int msg_type = pfkey_msg->sadb_msg_type; ++ int seq = pfkey_msg->sadb_msg_seq; ++ ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " ++ "building reply with type: %d\n", ++ msg_type); ++ pfkey_extensions_init(extensions); ++ if (!extr || !extr->ips) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " ++ "bad ipsec_sa passed\n"); ++ return EINVAL; ++ } ++ error = pfkey_safe_build(pfkey_msg_hdr_build(&extensions[0], ++ msg_type, ++ proto2satype(extr->ips->ips_said.proto), ++ 0, ++ seq, ++ pfkey_msg->sadb_msg_pid), ++ extensions) && ++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & ++ 1 << SADB_EXT_SA) ++ || pfkey_safe_build(pfkey_sa_ref_build(&extensions[SADB_EXT_SA], ++ SADB_EXT_SA, ++ extr->ips->ips_said.spi, ++ extr->ips->ips_replaywin, ++ extr->ips->ips_state, ++ extr->ips->ips_authalg, ++ extr->ips->ips_encalg, ++ extr->ips->ips_flags, ++ extr->ips->ips_ref), ++ extensions)) && ++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & ++ 1 << SADB_EXT_LIFETIME_CURRENT) ++ || pfkey_safe_build(pfkey_lifetime_build(&extensions ++ [SADB_EXT_LIFETIME_CURRENT], ++ SADB_EXT_LIFETIME_CURRENT, ++ extr->ips->ips_life.ipl_allocations.ipl_count, ++ extr->ips->ips_life.ipl_bytes.ipl_count, ++ extr->ips->ips_life.ipl_addtime.ipl_count, ++ extr->ips->ips_life.ipl_usetime.ipl_count, ++ extr->ips->ips_life.ipl_packets.ipl_count), ++ extensions)) && ++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & ++ 1 << SADB_EXT_ADDRESS_SRC) ++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC], ++ SADB_EXT_ADDRESS_SRC, ++ extr->ips->ips_said.proto, ++ 0, ++ extr->ips->ips_addr_s), ++ extensions)) && ++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] & ++ 1 << SADB_EXT_ADDRESS_DST) ++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST], ++ SADB_EXT_ADDRESS_DST, ++ extr->ips->ips_said.proto, ++ 0, ++ extr->ips->ips_addr_d), ++ extensions)); ++ ++ if (error == 0) { ++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: " ++ "building extensions failed\n"); ++ return EINVAL; ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_build_reply: " ++ "built extensions, proceed to build the message\n"); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_build_reply: " ++ "extensions[1]=0p%p\n", ++ extensions[1]); ++ error = pfkey_msg_build(pfkey_reply, extensions, EXT_BITS_OUT); ++ pfkey_extensions_free(extensions); ++ ++ return error; ++} ++ ++int ++pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg, ++ struct sadb_msg **pfkey_reply) ++{ ++ int error = 0; ++ int i; ++ struct sadb_ext *extensions[SADB_EXT_MAX+1]; ++ struct pfkey_extracted_data extr = {NULL, NULL, NULL}; ++ ++ pfkey_extensions_init(extensions); ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "parsing message ver=%d, type=%d, errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", ++ pfkey_msg->sadb_msg_version, ++ pfkey_msg->sadb_msg_type, ++ pfkey_msg->sadb_msg_errno, ++ pfkey_msg->sadb_msg_satype, ++ satype2name(pfkey_msg->sadb_msg_satype), ++ pfkey_msg->sadb_msg_len, ++ pfkey_msg->sadb_msg_reserved, ++ pfkey_msg->sadb_msg_seq, ++ pfkey_msg->sadb_msg_pid); ++ ++ extr.ips = ipsec_sa_alloc(&error); /* pass in error var by pointer */ ++ if(extr.ips == NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "memory allocation error.\n"); ++ SENDERR(-error); ++ } ++ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "allocated extr->ips=0p%p.\n", ++ extr.ips); ++ ++ if(pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "satype %d > max %d\n", ++ pfkey_msg->sadb_msg_satype, ++ SADB_SATYPE_MAX); ++ SENDERR(EINVAL); ++ } ++ ++ switch(pfkey_msg->sadb_msg_type) { ++ case SADB_GETSPI: ++ case SADB_UPDATE: ++ case SADB_ADD: ++ case SADB_DELETE: ++ case SADB_X_GRPSA: ++ case SADB_X_ADDFLOW: ++ ++ if(!(extr.ips->ips_said.proto = satype2proto(pfkey_msg->sadb_msg_satype))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "satype %d lookup failed.\n", ++ pfkey_msg->sadb_msg_satype); ++ SENDERR(EINVAL); ++ } else { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "satype %d lookups to proto=%d.\n", ++ pfkey_msg->sadb_msg_satype, ++ extr.ips->ips_said.proto); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ /* The NULL below causes the default extension parsers to be used */ ++ /* Parse the extensions */ ++ if((error = pfkey_msg_parse(pfkey_msg, NULL, extensions, EXT_BITS_IN))) ++ { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "message parsing failed with error %d.\n", ++ error); ++ SENDERR(-error); ++ } ++ ++ /* Process the extensions */ ++ for(i=1; i <= SADB_EXT_MAX;i++) { ++ if(extensions[i] != NULL) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "processing ext %d 0p%p with processor 0p%p.\n", ++ i, extensions[i], ext_processors[i]); ++ if((error = ext_processors[i](extensions[i], &extr))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "extension processing for type %d failed with error %d.\n", ++ i, ++ error); ++ SENDERR(-error); ++ } ++ ++ } ++ ++ } ++ ++ /* Parse the message types */ ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "parsing message type %d(%s) with msg_parser 0p%p.\n", ++ pfkey_msg->sadb_msg_type, ++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), ++ msg_parsers[pfkey_msg->sadb_msg_type]); ++ if((error = msg_parsers[pfkey_msg->sadb_msg_type](sk, extensions, &extr))) { ++ KLIPS_PRINT(debug_pfkey, ++ "klips_debug:pfkey_msg_interp: " ++ "message parsing failed with error %d.\n", ++ error); ++ SENDERR(-error); ++ } ++ ++#if 0 ++ error = pfkey_build_reply(pfkey_msg, &extr, pfkey_reply); ++ if (error) { ++ *pfkey_reply = NULL; ++ } ++#endif ++ errlab: ++ if(extr.ips != NULL) { ++ ipsec_sa_wipe(extr.ips); ++ } ++ if(extr.ips2 != NULL) { ++ ipsec_sa_wipe(extr.ips2); ++ } ++ if (extr.eroute != NULL) { ++ kfree(extr.eroute); ++ } ++ return(error); ++} ++ ++/* ++ * $Log: pfkey_v2_parser.c,v $ ++ * Revision 1.123 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.122.2.2 2004/04/05 04:30:46 mcr ++ * patches for alg-branch to compile/work with 2.x openswan ++ * ++ * Revision 1.122.2.1 2003/12/22 15:25:52 jjo ++ * . Merged algo-0.8.1-rc11-test1 into alg-branch ++ * ++ * Revision 1.122 2003/12/10 01:14:27 mcr ++ * NAT-traversal patches to KLIPS. ++ * ++ * Revision 1.121 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.120.4.2 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.120.4.1 2003/09/21 13:59:56 mcr ++ * pre-liminary X.509 patch - does not yet pass tests. ++ * ++ * Revision 1.120 2003/04/03 17:38:09 rgb ++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. ++ * ++ * Revision 1.119 2003/02/06 01:52:37 rgb ++ * Removed no longer relevant comment ++ * ++ * Revision 1.118 2003/01/30 02:32:44 rgb ++ * ++ * Transmit error code through to caller from callee for better diagnosis of problems. ++ * ++ * Revision 1.117 2003/01/16 18:48:13 rgb ++ * ++ * Fixed sign bug in error return from an sa allocation call in ++ * pfkey_msg_interp. ++ * ++ * Revision 1.116 2002/10/17 16:38:01 rgb ++ * Change pfkey_alloc_eroute() to never static since its consumers ++ * have been moved outside the file. ++ * ++ * Revision 1.115 2002/10/12 23:11:53 dhr ++ * ++ * [KenB + DHR] more 64-bit cleanup ++ * ++ * Revision 1.114 2002/10/05 05:02:58 dhr ++ * ++ * C labels go on statements ++ * ++ * Revision 1.113 2002/09/30 19:11:22 rgb ++ * Turn on debugging for upgoing acquire messages to test for reliability. ++ * ++ * Revision 1.112 2002/09/20 15:41:16 rgb ++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc(). ++ * Added sadb_x_sa_ref to struct sadb_sa. ++ * Added ref parameter to pfkey_sa_build(). ++ * ++ * Revision 1.111 2002/09/20 05:02:08 rgb ++ * Added memory allocation debugging. ++ * Convert to switch to divulge hmac keys for debugging. ++ * Added text labels to elucidate numeric values presented. ++ * ++ * Revision 1.110 2002/08/03 18:03:05 mcr ++ * loop that checks for SPI's to have been already linked ++ * fails to actually step to next pointer, but continuously ++ * resets to head of list. Wrong pointer used. ++ * test east-icmp-02 revealed this. ++ * ++ * Revision 1.109 2002/07/26 08:48:31 rgb ++ * Added SA ref table code. ++ * ++ * Revision 1.108 2002/05/27 18:55:03 rgb ++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. ++ * ++ * Revision 1.107 2002/05/23 07:16:08 rgb ++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount. ++ * Pointer clean-up. ++ * Added refcount code. ++ * ++ * Revision 1.106 2002/05/14 02:34:13 rgb ++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion ++ * with "put" usage in the kernel. ++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, ++ * ipsec_sa or ipsec_sa. ++ * Moved all the extension parsing functions to pfkey_v2_ext_process.c. ++ * ++ * Revision 1.105 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.104 2002/04/24 07:36:34 mcr ++ * Moved from ./klips/net/ipsec/pfkey_v2_parser.c,v ++ * ++ * Revision 1.103 2002/04/20 00:12:25 rgb ++ * Added esp IV CBC attack fix, disabled. ++ * ++ * Revision 1.102 2002/03/08 01:15:17 mcr ++ * put some internal structure only debug messages behind ++ * && sysctl_ipsec_debug_verbose. ++ * ++ * Revision 1.101 2002/01/29 17:17:57 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.100 2002/01/29 04:00:54 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.99 2002/01/29 02:13:19 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.98 2002/01/12 02:57:57 mcr ++ * first regression test causes acquire messages to be lost ++ * 100% of the time. This is to help testing of pluto. ++ * ++ * Revision 1.97 2001/11/26 09:23:52 rgb ++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. ++ * ++ * Revision 1.93.2.4 2001/10/23 04:20:27 mcr ++ * parity was forced on wrong structure! prototypes help here. ++ * ++ * Revision 1.93.2.3 2001/10/22 21:14:59 mcr ++ * include des.h, removed phony prototypes and fixed calling ++ * conventions to match real prototypes. ++ * ++ * Revision 1.93.2.2 2001/10/15 05:39:03 mcr ++ * %08lx is not the right format for u32. Use %08x. 64-bit safe? ha. ++ * ++ * Revision 1.93.2.1 2001/09/25 02:30:14 mcr ++ * struct tdb -> struct ipsec_sa. ++ * use new lifetime structure. common format routines for debug. ++ * ++ * Revision 1.96 2001/11/06 20:47:54 rgb ++ * Fixed user context call to ipsec_dev_start_xmit() bug. Call ++ * dev_queue_xmit() instead. ++ * ++ * Revision 1.95 2001/11/06 19:47:46 rgb ++ * Added packet parameter to lifetime and comb structures. ++ * ++ * Revision 1.94 2001/10/18 04:45:23 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.93 2001/09/20 15:32:59 rgb ++ * Min/max cleanup. ++ * ++ * Revision 1.92 2001/09/19 16:35:48 rgb ++ * PF_KEY ident fix for getspi from NetCelo (puttdb duplication). ++ * ++ * Revision 1.91 2001/09/15 16:24:06 rgb ++ * Re-inject first and last HOLD packet when an eroute REPLACE is done. ++ * ++ * Revision 1.90 2001/09/14 16:58:38 rgb ++ * Added support for storing the first and last packets through a HOLD. ++ * ++ * Revision 1.89 2001/09/08 21:14:07 rgb ++ * Added pfkey ident extension support for ISAKMPd. (NetCelo) ++ * Better state coherency (error management) between pf_key and IKE daemon. ++ * (NetCelo) ++ * ++ * Revision 1.88 2001/08/27 19:42:44 rgb ++ * Fix memory leak of encrypt and auth structs in pfkey register. ++ * ++ * Revision 1.87 2001/07/06 19:50:46 rgb ++ * Removed unused debugging code. ++ * Added inbound policy checking code for IPIP SAs. ++ * ++ * Revision 1.86 2001/06/20 06:26:04 rgb ++ * Changed missing SA errors from EEXIST to ENOENT and added debug output ++ * for already linked SAs. ++ * ++ * Revision 1.85 2001/06/15 04:57:02 rgb ++ * Remove single error return condition check and check for all errors in ++ * the case of a replace eroute delete operation. This means that ++ * applications must expect to be deleting something before replacing it ++ * and if nothing is found, complain. ++ * ++ * Revision 1.84 2001/06/14 19:35:12 rgb ++ * Update copyright date. ++ * ++ * Revision 1.83 2001/06/12 00:03:19 rgb ++ * Silence debug set/unset under normal conditions. ++ * ++ * Revision 1.82 2001/05/30 08:14:04 rgb ++ * Removed vestiges of esp-null transforms. ++ * ++ * Revision 1.81 2001/05/27 06:12:12 rgb ++ * Added structures for pid, packet count and last access time to eroute. ++ * Added packet count to beginning of /proc/net/ipsec_eroute. ++ * ++ * Revision 1.80 2001/05/03 19:43:59 rgb ++ * Check error return codes for all build function calls. ++ * Standardise on SENDERR() macro. ++ * ++ * Revision 1.79 2001/04/20 21:09:16 rgb ++ * Cleaned up fixed tdbwipes. ++ * Free pfkey_reply and clean up extensions_reply for grpsa, addflow and ++ * delflow (Per Cederqvist) plugging memleaks. ++ * ++ * Revision 1.78 2001/04/19 19:02:39 rgb ++ * Fixed extr.tdb freeing, stealing it for getspi, update and add. ++ * Refined a couple of spinlocks, fixed the one in update. ++ * ++ * Revision 1.77 2001/04/18 20:26:16 rgb ++ * Wipe/free eroute and both tdbs from extr at end of pfkey_msg_interp() ++ * instead of inside each message type parser. This fixes two memleaks. ++ * ++ * Revision 1.76 2001/04/17 23:51:18 rgb ++ * Quiet down pfkey_x_debug_process(). ++ * ++ * Revision 1.75 2001/03/29 01:55:05 rgb ++ * Fixed pfkey key init memleak. ++ * Fixed pfkey encryption key debug output. ++ * ++ * Revision 1.74 2001/03/27 05:29:14 rgb ++ * Debug output cleanup/silencing. ++ * ++ * Revision 1.73 2001/02/28 05:03:28 rgb ++ * Clean up and rationalise startup messages. ++ * ++ * Revision 1.72 2001/02/27 22:24:56 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.71 2001/02/27 06:59:30 rgb ++ * Added satype2name() conversions most places satype is debug printed. ++ * ++ * Revision 1.70 2001/02/26 22:37:08 rgb ++ * Fixed 'unknown proto' INT bug in new code. ++ * Added satype to protocol debugging instrumentation. ++ * ++ * Revision 1.69 2001/02/26 19:57:51 rgb ++ * Re-formatted debug output (split lines, consistent spacing). ++ * Fixed as yet undetected FLUSH bug which called ipsec_tdbcleanup() ++ * with an satype instead of proto. ++ * Checked for satype consistency and fixed minor bugs. ++ * Fixed undetected ungrpspi bug that tried to upmsg a second tdb. ++ * Check for satype sanity in pfkey_expire(). ++ * Added satype sanity check to addflow. ++ * ++ * Revision 1.68 2001/02/12 23:14:40 rgb ++ * Remove double spin lock in pfkey_expire(). ++ * ++ * Revision 1.67 2001/01/31 19:23:40 rgb ++ * Fixed double-unlock bug introduced by grpsa upmsg (found by Lars Heete). ++ * ++ * Revision 1.66 2001/01/29 22:20:04 rgb ++ * Fix minor add upmsg lifetime bug. ++ * ++ * Revision 1.65 2001/01/24 06:12:33 rgb ++ * Fixed address extension compile bugs just introduced. ++ * ++ * Revision 1.64 2001/01/24 00:31:15 rgb ++ * Added upmsg for addflow/delflow. ++ * ++ * Revision 1.63 2001/01/23 22:02:55 rgb ++ * Added upmsg to x_grpsa. ++ * Fixed lifetimes extentions to add/update/get upmsg. ++ * ++ * Revision 1.62 2000/11/30 21:47:51 rgb ++ * Fix error return bug after returning from pfkey_tdb_init(). ++ * ++ * Revision 1.61 2000/11/17 18:10:29 rgb ++ * Fixed bugs mostly relating to spirange, to treat all spi variables as ++ * network byte order since this is the way PF_KEYv2 stored spis. ++ * ++ * Revision 1.60 2000/11/06 04:34:53 rgb ++ * Changed non-exported functions to DEBUG_NO_STATIC. ++ * Add Svenning's adaptive content compression. ++ * Ditched spin_lock_irqsave in favour of spin_lock/_bh. ++ * Fixed double unlock bug (Svenning). ++ * Fixed pfkey_msg uninitialized bug in pfkey_{expire,acquire}(). ++ * Fixed incorrect extension type (prop) in pfkey)acquire(). ++ * ++ * Revision 1.59 2000/10/11 15:25:12 rgb ++ * Fixed IPCOMP disabled compile bug. ++ * ++ * Revision 1.58 2000/10/11 14:54:03 rgb ++ * Fixed pfkey_acquire() satype to SADB_SATYPE_ESP and removed pfkey ++ * protocol violations of setting pfkey_address_build() protocol parameter ++ * to non-zero except in the case of pfkey_acquire(). ++ * ++ * Revision 1.57 2000/10/10 20:10:18 rgb ++ * Added support for debug_ipcomp and debug_verbose to klipsdebug. ++ * ++ * Revision 1.56 2000/10/06 20:24:36 rgb ++ * Fixes to pfkey_acquire to initialize extensions[] and use correct ++ * ipproto. ++ * ++ * Revision 1.55 2000/10/03 03:20:57 rgb ++ * Added brackets to get a?b:c scope right for pfkey_register reply. ++ * ++ * Revision 1.54 2000/09/29 19:49:30 rgb ++ * As-yet-unused-bits cleanup. ++ * ++ * Revision 1.53 2000/09/28 00:35:45 rgb ++ * Padded SATYPE printout in pfkey_register for vertical alignment. ++ * ++ * Revision 1.52 2000/09/20 16:21:58 rgb ++ * Cleaned up ident string alloc/free. ++ * ++ * Revision 1.51 2000/09/20 04:04:20 rgb ++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in ++ * oopsen. ++ * ++ * Revision 1.50 2000/09/16 01:10:53 rgb ++ * Fixed unused var warning with debug off. ++ * ++ * Revision 1.49 2000/09/15 11:37:02 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.48 2000/09/15 04:57:57 rgb ++ * Cleaned up existing IPCOMP code before svenning addition. ++ * Initialize pfkey_reply and extensions_reply in case of early error in ++ * message parsing functions (thanks Kai!). ++ * ++ * Revision 1.47 2000/09/13 08:02:56 rgb ++ * Added KMd registration notification. ++ * ++ * Revision 1.46 2000/09/12 22:35:36 rgb ++ * Restructured to remove unused extensions from CLEARFLOW messages. ++ * ++ * Revision 1.45 2000/09/12 03:24:23 rgb ++ * Converted #if0 debugs to sysctl. ++ * ++ * Revision 1.44 2000/09/09 06:38:39 rgb ++ * Correct SADB message type for update, add and delete. ++ * ++ * Revision 1.43 2000/09/08 19:19:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * Removed all references to CONFIG_IPSEC_PFKEYv2. ++ * Put in sanity checks in most msg type parsers to catch invalid satypes ++ * and empty socket lists. ++ * Moved spin-locks in pfkey_get_parse() to simplify. ++ * Added pfkey_acquire(). ++ * Added upwards messages to update, add, delete, acquire_parse, ++ * expire_parse and flush. ++ * Fix pfkey_prop_build() parameter to be only single indirection. ++ * Changed all replies to use pfkey_reply. ++ * Check return code on puttdb() and deltdbchain() in getspi, update, ++ * add, delete. ++ * Fixed up all pfkey replies to open and registered sockets. ++ * ++ * Revision 1.42 2000/09/01 18:50:26 rgb ++ * Added a supported algorithms array lists, one per satype and registered ++ * existing algorithms. ++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to ++ * list. ++ * Only send pfkey_expire() messages to sockets registered for that satype. ++ * Added reply to pfkey_getspi_parse(). ++ * Added reply to pfkey_get_parse(). ++ * Fixed debug output label bug in pfkey_lifetime_process(). ++ * Cleaned up pfkey_sa_process a little. ++ * Moved pfkey_safe_build() above message type parsers to make it available ++ * for creating replies. ++ * Added comments for future work in pfkey_acquire_parse(). ++ * Fleshed out guts of pfkey_register_parse(). ++ * ++ * Revision 1.41 2000/08/24 16:58:11 rgb ++ * Fixed key debugging variables. ++ * Fixed error return code for a failed search. ++ * Changed order of pfkey_get operations. ++ * ++ * Revision 1.40 2000/08/21 16:32:27 rgb ++ * Re-formatted for cosmetic consistency and readability. ++ * ++ * Revision 1.39 2000/08/20 21:38:57 rgb ++ * Bugfixes to as-yet-unused pfkey_update_parse() and ++ * pfkey_register_parse(). (Momchil) ++ * Added functions pfkey_safe_build(), pfkey_expire() and ++ * pfkey_build_reply(). (Momchil) ++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil) ++ * ++ * Revision 1.38 2000/08/18 21:30:41 rgb ++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear. ++ * ++ * Revision 1.37 2000/08/18 18:18:02 rgb ++ * Cosmetic and descriptive changes made to debug test. ++ * getspi and update fixes from Momchil. ++ * ++ * Revision 1.36 2000/08/15 15:41:55 rgb ++ * Fixed the (as yet unused and untested) pfkey_getspi() routine. ++ * ++ * Revision 1.35 2000/08/01 14:51:52 rgb ++ * Removed _all_ remaining traces of DES. ++ * ++ * Revision 1.34 2000/07/28 14:58:32 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.33 2000/06/28 05:50:11 rgb ++ * Actually set iv_bits. ++ * ++ * Revision 1.32 2000/05/30 18:36:56 rgb ++ * Fix AH auth hash setup bug. This breaks interop with previous PF_KEY ++ * FreeS/WAN, but fixes interop with other implementations. ++ * ++ * Revision 1.31 2000/03/16 14:05:48 rgb ++ * Fixed brace scope preventing non-debug compile. ++ * Added null parameter check for pfkey_x_debug(). ++ * ++ * Revision 1.30 2000/01/22 23:21:13 rgb ++ * Use new function satype2proto(). ++ * ++ * Revision 1.29 2000/01/22 08:40:21 rgb ++ * Invert condition to known value to avoid AF_INET6 in 2.0.36. ++ * ++ * Revision 1.28 2000/01/22 07:58:57 rgb ++ * Fixed REPLACEFLOW bug, missing braces around KLIPS_PRINT *and* SENDERR. ++ * ++ * Revision 1.27 2000/01/22 03:48:01 rgb ++ * Added extr pointer component debugging. ++ * ++ * Revision 1.26 2000/01/21 09:41:25 rgb ++ * Changed a (void*) to (char*) cast to do proper pointer math. ++ * Don't call tdbwipe if tdb2 is NULL. ++ * ++ * Revision 1.25 2000/01/21 06:21:01 rgb ++ * Added address cases for eroute flows. ++ * Tidied up compiler directive indentation for readability. ++ * Added ictx,octx vars for simplification. ++ * Added macros for HMAC padding magic numbers. ++ * Converted from double tdb arguments to one structure (extr) ++ * containing pointers to all temporary information structures ++ * and checking for valid arguments to all ext processors and ++ * msg type parsers. ++ * Added spiungrp'ing. ++ * Added klipsdebug switching capability. ++ * Removed sa_process() check for zero protocol. ++ * Added address case for DST2 for grouping. ++ * Added/changed minor debugging instrumentation. ++ * Fixed spigrp for single said, ungrouping case. ++ * Added code to parse addflow and delflow messages. ++ * Removed redundant statements duplicating tdbwipe() functionality ++ * and causing double kfrees. ++ * Permit addflow to have a protocol of 0. ++ * ++ * Revision 1.24 1999/12/09 23:23:00 rgb ++ * Added check to pfkey_sa_process() to do eroutes. ++ * Converted to DIVUP() macro. ++ * Converted if() to switch() in pfkey_register_parse(). ++ * Use new pfkey_extensions_init() instead of memset(). ++ * ++ * Revision 1.23 1999/12/01 22:18:13 rgb ++ * Preset minspi and maxspi values in case and spirange extension is not ++ * included and check for the presence of an spirange extension before ++ * using it. Initialise tdb_sastate to LARVAL. ++ * Fixed debugging output typo. ++ * Fixed authentication context initialisation bugs (4 places). ++ * ++ * Revision 1.22 1999/11/27 11:53:08 rgb ++ * Moved pfkey_msg_parse prototype to pfkey.h ++ * Moved exts_permitted/required prototype to pfkey.h. ++ * Moved sadb_satype2proto protocol lookup table to lib/pfkey_v2_parse.c. ++ * Deleted SADB_X_EXT_SA2 code from pfkey_sa_process() since it will never ++ * be called. ++ * Moved protocol/algorithm checks to lib/pfkey_v2_parse.c ++ * Debugging error messages added. ++ * Enable lifetime_current checking. ++ * Remove illegal requirement for SA extension to be present in an ++ * originating GETSPI call. ++ * Re-instate requirement for UPDATE or ADD message to be MATURE. ++ * Add argument to pfkey_msg_parse() for direction. ++ * Fixed IPIP dst address bug and purged redundant, leaky code. ++ * ++ * Revision 1.21 1999/11/24 05:24:20 rgb ++ * hanged 'void*extensions' to 'struct sadb_ext*extensions'. ++ * Fixed indention. ++ * Ditched redundant replay check. ++ * Fixed debug message text from 'parse' to 'process'. ++ * Added more debug output. ++ * Forgot to zero extensions array causing bug, fixed. ++ * ++ * Revision 1.20 1999/11/23 23:08:13 rgb ++ * Move all common parsing code to lib/pfkey_v2_parse.c and rename ++ * remaining bits to *_process. (PJO) ++ * Add macros for dealing with alignment and rounding up more opaquely. ++ * Use provided macro ADDRTOA_BUF instead of hardcoded value. ++ * Sort out pfkey and freeswan headers, putting them in a library path. ++ * Corrected a couple of bugs in as-yet-inactive code. ++ * ++ * Revision 1.19 1999/11/20 22:01:10 rgb ++ * Add more descriptive error messages for non-zero reserved fields. ++ * Add more descriptive error message for spirange parsing. ++ * Start on supported extension parsing. ++ * Start on register and get message parsing. ++ * ++ * Revision 1.18 1999/11/18 04:09:20 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.17 1999/11/17 15:53:41 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.16 1999/10/26 16:57:43 rgb ++ * Add shorter macros for compiler directives to visually clean-up. ++ * Give ipv6 code meaningful compiler directive. ++ * Add comments to other #if 0 debug code. ++ * Remove unused *_bh_atomic() calls. ++ * Fix mis-placed spinlock. ++ * ++ * Revision 1.15 1999/10/16 18:27:10 rgb ++ * Clean-up unused cruft. ++ * Fix-up lifetime_allocations_c and lifetime_addtime_c initialisations. ++ * ++ * Revision 1.14 1999/10/08 18:37:34 rgb ++ * Fix end-of-line spacing to sate whining PHMs. ++ * ++ * Revision 1.13 1999/10/03 18:49:12 rgb ++ * Spinlock fixes for 2.0.xx and 2.3.xx. ++ * ++ * Revision 1.12 1999/10/01 15:44:54 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.11 1999/10/01 00:05:45 rgb ++ * Added tdb structure locking. ++ * Use 'jiffies' instead of do_get_timeofday(). ++ * Fix lifetime assignments. ++ * ++ * Revision 1.10 1999/09/21 15:24:45 rgb ++ * Rework spirange code to save entropy and prevent endless loops. ++ * ++ * Revision 1.9 1999/09/16 12:10:21 rgb ++ * Minor fixes to random spi selection for correctness and entropy conservation. ++ * ++ * Revision 1.8 1999/05/25 22:54:46 rgb ++ * Fix comparison that should be an assignment in an if. ++ * ++ * Revision 1.7 1999/05/09 03:25:37 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.6 1999/05/08 21:32:30 rgb ++ * Fix error return reporting. ++ * ++ * Revision 1.5 1999/05/05 22:02:33 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.4 1999/04/29 15:22:40 rgb ++ * Standardise an error return method. ++ * Add debugging instrumentation. ++ * Add check for existence of macros min/max. ++ * Add extensions permitted/required in/out filters. ++ * Add satype-to-protocol table. ++ * Add a second tdb pointer to each parser to accomodate GRPSA. ++ * Move AH & no_algo_set to GETSPI, UPDATE and ADD. ++ * Add OOO window check. ++ * Add support for IPPROTO_IPIP and hooks for IPPROTO_COMP. ++ * Add timestamp to lifetime parse. ++ * Fix address structure length checking bug. ++ * Fix address structure allocation bug (forgot to kmalloc!). ++ * Add checks for extension lengths. ++ * Add checks for extension reserved illegal values. ++ * Add check for spirange legal values. ++ * Add an extension type for parsing a second satype, SA and ++ * DST_ADDRESS. ++ * Make changes to tdb_init() template to get pfkey_tdb_init(), ++ * eliminating any mention of xformsw. ++ * Implement getspi, update and grpsa (not tested). ++ * Add stubs for as yet unimplemented message types. ++ * Add table of message parsers to substitute for msg_parse switch. ++ * ++ * Revision 1.3 1999/04/15 17:58:07 rgb ++ * Add RCSID labels. ++ * ++ * Revision 1.2 1999/04/15 15:37:26 rgb ++ * Forward check changes from POST1_00 branch. ++ * ++ * Revision 1.1.2.1 1999/03/26 20:58:56 rgb ++ * Add pfkeyv2 support to KLIPS. ++ * ++ * Local variables: ++ * c-file-style: "linux" ++ * End: ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/radij.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,1225 @@ ++char radij_c_version[] = "RCSID $Id: radij.c,v 1.46 2004/04/06 02:49:26 mcr Exp $"; ++ ++/* ++ * This file is defived from ${SRC}/sys/net/radix.c of BSD 4.4lite ++ * ++ * Variable and procedure names have been modified so that they don't ++ * conflict with the original BSD code, as a small number of modifications ++ * have been introduced and we may want to reuse this code in BSD. ++ * ++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek ++ * chi or a German ch sound (as `doch', not as in `milch'), or even a ++ * spanish j as in Juan. It is not as far back in the throat like ++ * the corresponding Hebrew sound, nor is it a soft breath like the English h. ++ * It has nothing to do with the Dutch ij sound. ++ * ++ * Here is the appropriate copyright notice: ++ */ ++ ++/* ++ * Copyright (c) 1988, 1989, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. 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. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * This product includes software developed by the University of ++ * California, Berkeley and its contributors. ++ * 4. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)radix.c 8.2 (Berkeley) 1/4/94 ++ */ ++ ++/* ++ * Routines to build and maintain radix trees for routing lookups. ++ */ ++ ++#include ++#include ++#include /* printk() */ ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef MALLOC_SLAB ++# include /* kmalloc() */ ++#else /* MALLOC_SLAB */ ++# include /* kmalloc() */ ++#endif /* MALLOC_SLAB */ ++#include /* error codes */ ++#include /* size_t */ ++#include /* mark_bh */ ++ ++#include /* struct device, and other headers */ ++#include /* eth_type_trans */ ++#include /* struct iphdr */ ++#include ++#ifdef NET_21 ++# include ++# include ++#endif /* NET_21 */ ++#include ++#include ++ ++#include ++ ++#include "openswan/radij.h" ++#include "openswan/ipsec_encap.h" ++#include "openswan/ipsec_radij.h" ++ ++int maj_keylen; ++struct radij_mask *rj_mkfreelist; ++struct radij_node_head *mask_rjhead; ++static int gotOddMasks; ++static char *maskedKey; ++static char *rj_zeroes, *rj_ones; ++ ++#define rj_masktop (mask_rjhead->rnh_treetop) ++#ifdef Bcmp ++# undef Bcmp ++#endif /* Bcmp */ ++#define Bcmp(a, b, l) (l == 0 ? 0 : memcmp((caddr_t)(b), (caddr_t)(a), (size_t)l)) ++/* ++ * The data structure for the keys is a radix tree with one way ++ * branching removed. The index rj_b at an internal node n represents a bit ++ * position to be tested. The tree is arranged so that all descendants ++ * of a node n have keys whose bits all agree up to position rj_b - 1. ++ * (We say the index of n is rj_b.) ++ * ++ * There is at least one descendant which has a one bit at position rj_b, ++ * and at least one with a zero there. ++ * ++ * A route is determined by a pair of key and mask. We require that the ++ * bit-wise logical and of the key and mask to be the key. ++ * We define the index of a route to associated with the mask to be ++ * the first bit number in the mask where 0 occurs (with bit number 0 ++ * representing the highest order bit). ++ * ++ * We say a mask is normal if every bit is 0, past the index of the mask. ++ * If a node n has a descendant (k, m) with index(m) == index(n) == rj_b, ++ * and m is a normal mask, then the route applies to every descendant of n. ++ * If the index(m) < rj_b, this implies the trailing last few bits of k ++ * before bit b are all 0, (and hence consequently true of every descendant ++ * of n), so the route applies to all descendants of the node as well. ++ * ++ * The present version of the code makes no use of normal routes, ++ * but similar logic shows that a non-normal mask m such that ++ * index(m) <= index(n) could potentially apply to many children of n. ++ * Thus, for each non-host route, we attach its mask to a list at an internal ++ * node as high in the tree as we can go. ++ */ ++ ++struct radij_node * ++rj_search(v_arg, head) ++ void *v_arg; ++ struct radij_node *head; ++{ ++ register struct radij_node *x; ++ register caddr_t v; ++ ++ for (x = head, v = v_arg; x->rj_b >= 0;) { ++ if (x->rj_bmask & v[x->rj_off]) ++ x = x->rj_r; ++ else ++ x = x->rj_l; ++ } ++ return (x); ++}; ++ ++struct radij_node * ++rj_search_m(v_arg, head, m_arg) ++ struct radij_node *head; ++ void *v_arg, *m_arg; ++{ ++ register struct radij_node *x; ++ register caddr_t v = v_arg, m = m_arg; ++ ++ for (x = head; x->rj_b >= 0;) { ++ if ((x->rj_bmask & m[x->rj_off]) && ++ (x->rj_bmask & v[x->rj_off])) ++ x = x->rj_r; ++ else ++ x = x->rj_l; ++ } ++ return x; ++}; ++ ++int ++rj_refines(m_arg, n_arg) ++ void *m_arg, *n_arg; ++{ ++ register caddr_t m = m_arg, n = n_arg; ++ register caddr_t lim, lim2 = lim = n + *(u_char *)n; ++ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); ++ int masks_are_equal = 1; ++ ++ if (longer > 0) ++ lim -= longer; ++ while (n < lim) { ++ if (*n & ~(*m)) ++ return 0; ++ if (*n++ != *m++) ++ masks_are_equal = 0; ++ ++ } ++ while (n < lim2) ++ if (*n++) ++ return 0; ++ if (masks_are_equal && (longer < 0)) ++ for (lim2 = m - longer; m < lim2; ) ++ if (*m++) ++ return 1; ++ return (!masks_are_equal); ++} ++ ++ ++struct radij_node * ++rj_match(v_arg, head) ++ void *v_arg; ++ struct radij_node_head *head; ++{ ++ caddr_t v = v_arg; ++ register struct radij_node *t = head->rnh_treetop, *x; ++ register caddr_t cp = v, cp2, cp3; ++ caddr_t cplim, mstart; ++ struct radij_node *saved_t, *top = t; ++ int off = t->rj_off, vlen = *(u_char *)cp, matched_off; ++ ++ /* ++ * Open code rj_search(v, top) to avoid overhead of extra ++ * subroutine call. ++ */ ++ for (; t->rj_b >= 0; ) { ++ if (t->rj_bmask & cp[t->rj_off]) ++ t = t->rj_r; ++ else ++ t = t->rj_l; ++ } ++ /* ++ * See if we match exactly as a host destination ++ */ ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "* See if we match exactly as a host destination\n"); ++ ++ cp += off; cp2 = t->rj_key + off; cplim = v + vlen; ++ for (; cp < cplim; cp++, cp2++) ++ if (*cp != *cp2) ++ goto on1; ++ /* ++ * This extra grot is in case we are explicitly asked ++ * to look up the default. Ugh! ++ */ ++ if ((t->rj_flags & RJF_ROOT) && t->rj_dupedkey) ++ t = t->rj_dupedkey; ++ return t; ++on1: ++ matched_off = cp - v; ++ saved_t = t; ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "** try to match a leaf, t=0p%p\n", t); ++ do { ++ if (t->rj_mask) { ++ /* ++ * Even if we don't match exactly as a hosts; ++ * we may match if the leaf we wound up at is ++ * a route to a net. ++ */ ++ cp3 = matched_off + t->rj_mask; ++ cp2 = matched_off + t->rj_key; ++ for (; cp < cplim; cp++) ++ if ((*cp2++ ^ *cp) & *cp3++) ++ break; ++ if (cp == cplim) ++ return t; ++ cp = matched_off + v; ++ } ++ } while ((t = t->rj_dupedkey)); ++ t = saved_t; ++ /* start searching up the tree */ ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "*** start searching up the tree, t=0p%p\n", ++ t); ++ do { ++ register struct radij_mask *m; ++ ++ t = t->rj_p; ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "**** t=0p%p\n", ++ t); ++ if ((m = t->rj_mklist)) { ++ /* ++ * After doing measurements here, it may ++ * turn out to be faster to open code ++ * rj_search_m here instead of always ++ * copying and masking. ++ */ ++ /* off = min(t->rj_off, matched_off); */ ++ off = t->rj_off; ++ if (matched_off < off) ++ off = matched_off; ++ mstart = maskedKey + off; ++ do { ++ cp2 = mstart; ++ cp3 = m->rm_mask + off; ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "***** cp2=0p%p cp3=0p%p\n", ++ cp2, cp3); ++ for (cp = v + off; cp < cplim;) ++ *cp2++ = *cp++ & *cp3++; ++ x = rj_search(maskedKey, t); ++ while (x && x->rj_mask != m->rm_mask) ++ x = x->rj_dupedkey; ++ if (x && ++ (Bcmp(mstart, x->rj_key + off, ++ vlen - off) == 0)) ++ return x; ++ } while ((m = m->rm_mklist)); ++ } ++ } while (t != top); ++ KLIPS_PRINT(debug_radij, ++ "klips_debug:rj_match: " ++ "***** not found.\n"); ++ return 0; ++}; ++ ++#ifdef RJ_DEBUG ++int rj_nodenum; ++struct radij_node *rj_clist; ++int rj_saveinfo; ++DEBUG_NO_STATIC void traverse(struct radij_node *); ++#ifdef RJ_DEBUG2 ++int rj_debug = 1; ++#else ++int rj_debug = 0; ++#endif /* RJ_DEBUG2 */ ++#endif /* RJ_DEBUG */ ++ ++struct radij_node * ++rj_newpair(v, b, nodes) ++ void *v; ++ int b; ++ struct radij_node nodes[2]; ++{ ++ register struct radij_node *tt = nodes, *t = tt + 1; ++ t->rj_b = b; t->rj_bmask = 0x80 >> (b & 7); ++ t->rj_l = tt; t->rj_off = b >> 3; ++ tt->rj_b = -1; tt->rj_key = (caddr_t)v; tt->rj_p = t; ++ tt->rj_flags = t->rj_flags = RJF_ACTIVE; ++#ifdef RJ_DEBUG ++ tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++; ++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt; ++#endif /* RJ_DEBUG */ ++ return t; ++} ++ ++struct radij_node * ++rj_insert(v_arg, head, dupentry, nodes) ++ void *v_arg; ++ struct radij_node_head *head; ++ int *dupentry; ++ struct radij_node nodes[2]; ++{ ++ caddr_t v = v_arg; ++ struct radij_node *top = head->rnh_treetop; ++ int head_off = top->rj_off, vlen = (int)*((u_char *)v); ++ register struct radij_node *t = rj_search(v_arg, top); ++ register caddr_t cp = v + head_off; ++ register int b; ++ struct radij_node *tt; ++ /* ++ *find first bit at which v and t->rj_key differ ++ */ ++ { ++ register caddr_t cp2 = t->rj_key + head_off; ++ register int cmp_res; ++ caddr_t cplim = v + vlen; ++ ++ while (cp < cplim) ++ if (*cp2++ != *cp++) ++ goto on1; ++ *dupentry = 1; ++ return t; ++on1: ++ *dupentry = 0; ++ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; ++ for (b = (cp - v) << 3; cmp_res; b--) ++ cmp_res >>= 1; ++ } ++ { ++ register struct radij_node *p, *x = top; ++ cp = v; ++ do { ++ p = x; ++ if (cp[x->rj_off] & x->rj_bmask) ++ x = x->rj_r; ++ else x = x->rj_l; ++ } while (b > (unsigned) x->rj_b); /* x->rj_b < b && x->rj_b >= 0 */ ++#ifdef RJ_DEBUG ++ if (rj_debug) ++ printk("klips_debug:rj_insert: Going In:\n"), traverse(p); ++#endif /* RJ_DEBUG */ ++ t = rj_newpair(v_arg, b, nodes); tt = t->rj_l; ++ if ((cp[p->rj_off] & p->rj_bmask) == 0) ++ p->rj_l = t; ++ else ++ p->rj_r = t; ++ x->rj_p = t; t->rj_p = p; /* frees x, p as temp vars below */ ++ if ((cp[t->rj_off] & t->rj_bmask) == 0) { ++ t->rj_r = x; ++ } else { ++ t->rj_r = tt; t->rj_l = x; ++ } ++#ifdef RJ_DEBUG ++ if (rj_debug) ++ printk("klips_debug:rj_insert: Coming out:\n"), traverse(p); ++#endif /* RJ_DEBUG */ ++ } ++ return (tt); ++} ++ ++struct radij_node * ++rj_addmask(n_arg, search, skip) ++ int search, skip; ++ void *n_arg; ++{ ++ caddr_t netmask = (caddr_t)n_arg; ++ register struct radij_node *x; ++ register caddr_t cp, cplim; ++ register int b, mlen, j; ++ int maskduplicated; ++ ++ mlen = *(u_char *)netmask; ++ if (search) { ++ x = rj_search(netmask, rj_masktop); ++ mlen = *(u_char *)netmask; ++ if (Bcmp(netmask, x->rj_key, mlen) == 0) ++ return (x); ++ } ++ R_Malloc(x, struct radij_node *, maj_keylen + 2 * sizeof (*x)); ++ if (x == 0) ++ return (0); ++ Bzero(x, maj_keylen + 2 * sizeof (*x)); ++ cp = (caddr_t)(x + 2); ++ Bcopy(netmask, cp, mlen); ++ netmask = cp; ++ x = rj_insert(netmask, mask_rjhead, &maskduplicated, x); ++ /* ++ * Calculate index of mask. ++ */ ++ cplim = netmask + mlen; ++ for (cp = netmask + skip; cp < cplim; cp++) ++ if (*(u_char *)cp != 0xff) ++ break; ++ b = (cp - netmask) << 3; ++ if (cp != cplim) { ++ if (*cp != 0) { ++ gotOddMasks = 1; ++ for (j = 0x80; j; b++, j >>= 1) ++ if ((j & *cp) == 0) ++ break; ++ } ++ } ++ x->rj_b = -1 - b; ++ return (x); ++} ++ ++#if 0 ++struct radij_node * ++#endif ++int ++rj_addroute(v_arg, n_arg, head, treenodes) ++ void *v_arg, *n_arg; ++ struct radij_node_head *head; ++ struct radij_node treenodes[2]; ++{ ++ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; ++ register struct radij_node *t, *x=NULL, *tt; ++ struct radij_node *saved_tt, *top = head->rnh_treetop; ++ short b = 0, b_leaf; ++ int mlen, keyduplicated; ++ caddr_t cplim; ++ struct radij_mask *m, **mp; ++ ++ /* ++ * In dealing with non-contiguous masks, there may be ++ * many different routes which have the same mask. ++ * We will find it useful to have a unique pointer to ++ * the mask to speed avoiding duplicate references at ++ * nodes and possibly save time in calculating indices. ++ */ ++ if (netmask) { ++ x = rj_search(netmask, rj_masktop); ++ mlen = *(u_char *)netmask; ++ if (Bcmp(netmask, x->rj_key, mlen) != 0) { ++ x = rj_addmask(netmask, 0, top->rj_off); ++ if (x == 0) ++ return -ENOMEM; /* (0) rgb */ ++ } ++ netmask = x->rj_key; ++ b = -1 - x->rj_b; ++ } ++ /* ++ * Deal with duplicated keys: attach node to previous instance ++ */ ++ saved_tt = tt = rj_insert(v, head, &keyduplicated, treenodes); ++#ifdef RJ_DEBUG ++ printk("addkey: duplicated: %d\n", keyduplicated); ++#endif ++ if (keyduplicated) { ++ do { ++ if (tt->rj_mask == netmask) ++ return -EEXIST; /* -ENXIO; (0) rgb */ ++ t = tt; ++ if (netmask == 0 || ++ (tt->rj_mask && rj_refines(netmask, tt->rj_mask))) ++ break; ++ } while ((tt = tt->rj_dupedkey)); ++ /* ++ * If the mask is not duplicated, we wouldn't ++ * find it among possible duplicate key entries ++ * anyway, so the above test doesn't hurt. ++ * ++ * We sort the masks for a duplicated key the same way as ++ * in a masklist -- most specific to least specific. ++ * This may require the unfortunate nuisance of relocating ++ * the head of the list. ++ */ ++ if (tt && t == saved_tt) { ++ struct radij_node *xx = x; ++ /* link in at head of list */ ++ (tt = treenodes)->rj_dupedkey = t; ++ tt->rj_flags = t->rj_flags; ++ tt->rj_p = x = t->rj_p; ++ if (x->rj_l == t) x->rj_l = tt; else x->rj_r = tt; ++ saved_tt = tt; x = xx; ++ } else { ++ (tt = treenodes)->rj_dupedkey = t->rj_dupedkey; ++ t->rj_dupedkey = tt; ++ } ++#ifdef RJ_DEBUG ++ t=tt+1; tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++; ++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt; ++#endif /* RJ_DEBUG */ ++ t = saved_tt; ++ tt->rj_key = (caddr_t) v; ++ tt->rj_b = -1; ++ tt->rj_flags = t->rj_flags & ~RJF_ROOT; ++ } ++ /* ++ * Put mask in tree. ++ */ ++ if (netmask) { ++ tt->rj_mask = netmask; ++ tt->rj_b = x->rj_b; ++ } ++ t = saved_tt->rj_p; ++ b_leaf = -1 - t->rj_b; ++ if (t->rj_r == saved_tt) x = t->rj_l; else x = t->rj_r; ++ /* Promote general routes from below */ ++ if (x->rj_b < 0) { ++ if (x->rj_mask && (x->rj_b >= b_leaf) && x->rj_mklist == 0) { ++ MKGet(m); ++ if (m) { ++ Bzero(m, sizeof *m); ++ m->rm_b = x->rj_b; ++ m->rm_mask = x->rj_mask; ++ x->rj_mklist = t->rj_mklist = m; ++ } ++ } ++ } else if (x->rj_mklist) { ++ /* ++ * Skip over masks whose index is > that of new node ++ */ ++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) ++ if (m->rm_b >= b_leaf) ++ break; ++ t->rj_mklist = m; *mp = 0; ++ } ++ /* Add new route to highest possible ancestor's list */ ++ if ((netmask == 0) || (b > t->rj_b )) { ++#ifdef RJ_DEBUG ++ printk("klips:radij.c: netmask = %p or b(%d)>t->rjb(%d)\n", netmask, b, t->rj_b); ++#endif ++ return 0; /* tt rgb */ /* can't lift at all */ ++ } ++ b_leaf = tt->rj_b; ++ do { ++ x = t; ++ t = t->rj_p; ++ } while (b <= t->rj_b && x != top); ++ /* ++ * Search through routes associated with node to ++ * insert new route according to index. ++ * For nodes of equal index, place more specific ++ * masks first. ++ */ ++ cplim = netmask + mlen; ++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) { ++ if (m->rm_b < b_leaf) ++ continue; ++ if (m->rm_b > b_leaf) ++ break; ++ if (m->rm_mask == netmask) { ++ m->rm_refs++; ++ tt->rj_mklist = m; ++#ifdef RJ_DEBUG ++ printk("klips:radij.c: m->rm_mask %p == netmask\n", netmask); ++#endif ++ return 0; /* tt rgb */ ++ } ++ if (rj_refines(netmask, m->rm_mask)) ++ break; ++ } ++ MKGet(m); ++ if (m == 0) { ++ printk("klips_debug:rj_addroute: " ++ "Mask for route not entered\n"); ++ return 0; /* (tt) rgb */ ++ } ++ Bzero(m, sizeof *m); ++ m->rm_b = b_leaf; ++ m->rm_mask = netmask; ++ m->rm_mklist = *mp; ++ *mp = m; ++ tt->rj_mklist = m; ++#ifdef RJ_DEBUG ++ printk("klips:radij.c: addroute done\n"); ++#endif ++ return 0; /* tt rgb */ ++} ++ ++int ++rj_delete(v_arg, netmask_arg, head, node) ++ void *v_arg, *netmask_arg; ++ struct radij_node_head *head; ++ struct radij_node **node; ++{ ++ register struct radij_node *t, *p, *x, *tt; ++ struct radij_mask *m, *saved_m, **mp; ++ struct radij_node *dupedkey, *saved_tt, *top; ++ caddr_t v, netmask; ++ int b, head_off, vlen; ++ ++ v = v_arg; ++ netmask = netmask_arg; ++ x = head->rnh_treetop; ++ tt = rj_search(v, x); ++ head_off = x->rj_off; ++ vlen = *(u_char *)v; ++ saved_tt = tt; ++ top = x; ++ if (tt == 0 || ++ Bcmp(v + head_off, tt->rj_key + head_off, vlen - head_off)) ++ return -EFAULT; /* (0) rgb */ ++ /* ++ * Delete our route from mask lists. ++ */ ++ if ((dupedkey = tt->rj_dupedkey)) { ++ if (netmask) ++ netmask = rj_search(netmask, rj_masktop)->rj_key; ++ while (tt->rj_mask != netmask) ++ if ((tt = tt->rj_dupedkey) == 0) ++ return -ENOENT; /* -ENXIO; (0) rgb */ ++ } ++ if (tt->rj_mask == 0 || (saved_m = m = tt->rj_mklist) == 0) ++ goto on1; ++ if (m->rm_mask != tt->rj_mask) { ++ printk("klips_debug:rj_delete: " ++ "inconsistent annotation\n"); ++ goto on1; ++ } ++ if (--m->rm_refs >= 0) ++ goto on1; ++ b = -1 - tt->rj_b; ++ t = saved_tt->rj_p; ++ if (b > t->rj_b) ++ goto on1; /* Wasn't lifted at all */ ++ do { ++ x = t; ++ t = t->rj_p; ++ } while (b <= t->rj_b && x != top); ++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) ++ if (m == saved_m) { ++ *mp = m->rm_mklist; ++ MKFree(m); ++ break; ++ } ++ if (m == 0) ++ printk("klips_debug:rj_delete: " ++ "couldn't find our annotation\n"); ++on1: ++ /* ++ * Eliminate us from tree ++ */ ++ if (tt->rj_flags & RJF_ROOT) ++ return -EFAULT; /* (0) rgb */ ++#ifdef RJ_DEBUG ++ /* Get us out of the creation list */ ++ for (t = rj_clist; t && t->rj_ybro != tt; t = t->rj_ybro) {} ++ if (t) t->rj_ybro = tt->rj_ybro; ++#endif /* RJ_DEBUG */ ++ t = tt->rj_p; ++ if (dupedkey) { ++ if (tt == saved_tt) { ++ x = dupedkey; x->rj_p = t; ++ if (t->rj_l == tt) t->rj_l = x; else t->rj_r = x; ++ } else { ++ for (x = p = saved_tt; p && p->rj_dupedkey != tt;) ++ p = p->rj_dupedkey; ++ if (p) p->rj_dupedkey = tt->rj_dupedkey; ++ else printk("klips_debug:rj_delete: " ++ "couldn't find node that we started with\n"); ++ } ++ t = tt + 1; ++ if (t->rj_flags & RJF_ACTIVE) { ++#ifndef RJ_DEBUG ++ *++x = *t; p = t->rj_p; ++#else ++ b = t->rj_info; *++x = *t; t->rj_info = b; p = t->rj_p; ++#endif /* RJ_DEBUG */ ++ if (p->rj_l == t) p->rj_l = x; else p->rj_r = x; ++ x->rj_l->rj_p = x; x->rj_r->rj_p = x; ++ } ++ goto out; ++ } ++ if (t->rj_l == tt) x = t->rj_r; else x = t->rj_l; ++ p = t->rj_p; ++ if (p->rj_r == t) p->rj_r = x; else p->rj_l = x; ++ x->rj_p = p; ++ /* ++ * Demote routes attached to us. ++ */ ++ if (t->rj_mklist) { ++ if (x->rj_b >= 0) { ++ for (mp = &x->rj_mklist; (m = *mp);) ++ mp = &m->rm_mklist; ++ *mp = t->rj_mklist; ++ } else { ++ for (m = t->rj_mklist; m;) { ++ struct radij_mask *mm = m->rm_mklist; ++ if (m == x->rj_mklist && (--(m->rm_refs) < 0)) { ++ x->rj_mklist = 0; ++ MKFree(m); ++ } else ++ printk("klips_debug:rj_delete: " ++ "Orphaned Mask 0p%p at 0p%p\n", m, x); ++ m = mm; ++ } ++ } ++ } ++ /* ++ * We may be holding an active internal node in the tree. ++ */ ++ x = tt + 1; ++ if (t != x) { ++#ifndef RJ_DEBUG ++ *t = *x; ++#else ++ b = t->rj_info; *t = *x; t->rj_info = b; ++#endif /* RJ_DEBUG */ ++ t->rj_l->rj_p = t; t->rj_r->rj_p = t; ++ p = x->rj_p; ++ if (p->rj_l == x) p->rj_l = t; else p->rj_r = t; ++ } ++out: ++ tt->rj_flags &= ~RJF_ACTIVE; ++ tt[1].rj_flags &= ~RJF_ACTIVE; ++ *node = tt; ++ return 0; /* (tt) rgb */ ++} ++ ++int ++rj_walktree(h, f, w) ++ struct radij_node_head *h; ++ register int (*f)(struct radij_node *,void *); ++ void *w; ++{ ++ int error; ++ struct radij_node *base, *next; ++ register struct radij_node *rn; ++ ++ if(!h || !f /* || !w */) { ++ return -ENODATA; ++ } ++ ++ rn = h->rnh_treetop; ++ /* ++ * This gets complicated because we may delete the node ++ * while applying the function f to it, so we need to calculate ++ * the successor node in advance. ++ */ ++ /* First time through node, go left */ ++ while (rn->rj_b >= 0) ++ rn = rn->rj_l; ++ for (;;) { ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_radij) { ++ printk("klips_debug:rj_walktree: " ++ "for: rn=0p%p rj_b=%d rj_flags=%x", ++ rn, ++ rn->rj_b, ++ rn->rj_flags); ++ rn->rj_b >= 0 ? ++ printk(" node off=%x\n", ++ rn->rj_off) : ++ printk(" leaf key = %08x->%08x\n", ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) ++ ; ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ base = rn; ++ /* If at right child go back up, otherwise, go right */ ++ while (rn->rj_p->rj_r == rn && (rn->rj_flags & RJF_ROOT) == 0) ++ rn = rn->rj_p; ++ /* Find the next *leaf* since next node might vanish, too */ ++ for (rn = rn->rj_p->rj_r; rn->rj_b >= 0;) ++ rn = rn->rj_l; ++ next = rn; ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_radij) { ++ printk("klips_debug:rj_walktree: " ++ "processing leaves, rn=0p%p rj_b=%d rj_flags=%x", ++ rn, ++ rn->rj_b, ++ rn->rj_flags); ++ rn->rj_b >= 0 ? ++ printk(" node off=%x\n", ++ rn->rj_off) : ++ printk(" leaf key = %08x->%08x\n", ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) ++ ; ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ /* Process leaves */ ++ while ((rn = base)) { ++ base = rn->rj_dupedkey; ++#ifdef CONFIG_IPSEC_DEBUG ++ if(debug_radij) { ++ printk("klips_debug:rj_walktree: " ++ "while: base=0p%p rn=0p%p rj_b=%d rj_flags=%x", ++ base, ++ rn, ++ rn->rj_b, ++ rn->rj_flags); ++ rn->rj_b >= 0 ? ++ printk(" node off=%x\n", ++ rn->rj_off) : ++ printk(" leaf key = %08x->%08x\n", ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)) ++ ; ++ } ++#endif /* CONFIG_IPSEC_DEBUG */ ++ if (!(rn->rj_flags & RJF_ROOT) && (error = (*f)(rn, w))) ++ return (-error); ++ } ++ rn = next; ++ if (rn->rj_flags & RJF_ROOT) ++ return (0); ++ } ++ /* NOTREACHED */ ++} ++ ++int ++rj_inithead(head, off) ++ void **head; ++ int off; ++{ ++ register struct radij_node_head *rnh; ++ register struct radij_node *t, *tt, *ttt; ++ if (*head) ++ return (1); ++ R_Malloc(rnh, struct radij_node_head *, sizeof (*rnh)); ++ if (rnh == NULL) ++ return (0); ++ Bzero(rnh, sizeof (*rnh)); ++ *head = rnh; ++ t = rj_newpair(rj_zeroes, off, rnh->rnh_nodes); ++ ttt = rnh->rnh_nodes + 2; ++ t->rj_r = ttt; ++ t->rj_p = t; ++ tt = t->rj_l; ++ tt->rj_flags = t->rj_flags = RJF_ROOT | RJF_ACTIVE; ++ tt->rj_b = -1 - off; ++ *ttt = *tt; ++ ttt->rj_key = rj_ones; ++ rnh->rnh_addaddr = rj_addroute; ++ rnh->rnh_deladdr = rj_delete; ++ rnh->rnh_matchaddr = rj_match; ++ rnh->rnh_walktree = rj_walktree; ++ rnh->rnh_treetop = t; ++ return (1); ++} ++ ++void ++rj_init() ++{ ++ char *cp, *cplim; ++ ++ if (maj_keylen == 0) { ++ printk("klips_debug:rj_init: " ++ "radij functions require maj_keylen be set\n"); ++ return; ++ } ++ R_Malloc(rj_zeroes, char *, 3 * maj_keylen); ++ if (rj_zeroes == NULL) ++ panic("rj_init"); ++ Bzero(rj_zeroes, 3 * maj_keylen); ++ rj_ones = cp = rj_zeroes + maj_keylen; ++ maskedKey = cplim = rj_ones + maj_keylen; ++ while (cp < cplim) ++ *cp++ = -1; ++ if (rj_inithead((void **)&mask_rjhead, 0) == 0) ++ panic("rj_init 2"); ++} ++ ++void ++rj_preorder(struct radij_node *rn, int l) ++{ ++ int i; ++ ++ if (rn == NULL){ ++ printk("klips_debug:rj_preorder: " ++ "NULL pointer\n"); ++ return; ++ } ++ ++ if (rn->rj_b >= 0){ ++ rj_preorder(rn->rj_l, l+1); ++ rj_preorder(rn->rj_r, l+1); ++ printk("klips_debug:"); ++ for (i=0; irj_off); ++ } else { ++ printk("klips_debug:"); ++ for (i=0; irj_flags); ++ if (rn->rj_flags & RJF_ACTIVE) { ++ printk(" @key=0p%p", ++ rn->rj_key); ++ printk(" key = %08x->%08x", ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr), ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr)); ++ printk(" @mask=0p%p", ++ rn->rj_mask); ++ if (rn->rj_mask) ++ printk(" mask = %08x->%08x", ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_src.s_addr), ++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_dst.s_addr)); ++ if (rn->rj_dupedkey) ++ printk(" dupedkey = 0p%p", ++ rn->rj_dupedkey); ++ } ++ printk("\n"); ++ } ++} ++ ++#ifdef RJ_DEBUG ++DEBUG_NO_STATIC void traverse(struct radij_node *p) ++{ ++ rj_preorder(p, 0); ++} ++#endif /* RJ_DEBUG */ ++ ++void ++rj_dumptrees(void) ++{ ++ rj_preorder(rnh->rnh_treetop, 0); ++} ++ ++void ++rj_free_mkfreelist(void) ++{ ++ struct radij_mask *mknp, *mknp2; ++ ++ mknp = rj_mkfreelist; ++ while(mknp) ++ { ++ mknp2 = mknp; ++ mknp = mknp->rm_mklist; ++ kfree(mknp2); ++ } ++} ++ ++int ++radijcleartree(void) ++{ ++ return rj_walktree(rnh, ipsec_rj_walker_delete, NULL); ++} ++ ++int ++radijcleanup(void) ++{ ++ int error = 0; ++ ++ error = radijcleartree(); ++ ++ rj_free_mkfreelist(); ++ ++/* rj_walktree(mask_rjhead, ipsec_rj_walker_delete, NULL); */ ++ if(mask_rjhead) { ++ kfree(mask_rjhead); ++ } ++ ++ if(rj_zeroes) { ++ kfree(rj_zeroes); ++ } ++ ++ if(rnh) { ++ kfree(rnh); ++ } ++ ++ return error; ++} ++ ++/* ++ * $Log: radij.c,v $ ++ * Revision 1.46 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.45 2003/10/31 02:27:55 mcr ++ * pulled up port-selector patches and sa_id elimination. ++ * ++ * Revision 1.44.30.1 2003/10/29 01:30:41 mcr ++ * elimited "struct sa_id". ++ * ++ * Revision 1.44 2002/07/24 18:44:54 rgb ++ * Type fiddling to tame ia64 compiler. ++ * ++ * Revision 1.43 2002/05/23 07:14:11 rgb ++ * Cleaned up %p variants to 0p%p for test suite cleanup. ++ * ++ * Revision 1.42 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.41 2002/04/24 07:36:35 mcr ++ * Moved from ./klips/net/ipsec/radij.c,v ++ * ++ * Revision 1.40 2002/01/29 17:17:58 mcr ++ * moved include of ipsec_param.h to after include of linux/kernel.h ++ * otherwise, it seems that some option that is set in ipsec_param.h ++ * screws up something subtle in the include path to kernel.h, and ++ * it complains on the snprintf() prototype. ++ * ++ * Revision 1.39 2002/01/29 04:00:55 mcr ++ * more excise of kversions.h header. ++ * ++ * Revision 1.38 2002/01/29 02:13:19 mcr ++ * introduction of ipsec_kversion.h means that include of ++ * ipsec_param.h must preceed any decisions about what files to ++ * include to deal with differences in kernel source. ++ * ++ * Revision 1.37 2001/10/18 04:45:23 rgb ++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, ++ * lib/freeswan.h version macros moved to lib/kversions.h. ++ * Other compiler directive cleanups. ++ * ++ * Revision 1.36 2001/08/22 13:43:51 henry ++ * eliminate the single use of min() to avoid problems with Linus changing it ++ * ++ * Revision 1.35 2001/06/15 04:57:29 rgb ++ * Clarified error return codes. ++ * Changed mask add already exists to EEXIST. ++ * Changed mask delete did not exist to ENOENT. ++ * ++ * Revision 1.34 2001/05/03 19:44:26 rgb ++ * Fix sign of error return codes for rj_addroute(). ++ * ++ * Revision 1.33 2001/02/27 22:24:56 rgb ++ * Re-formatting debug output (line-splitting, joining, 1arg/line). ++ * Check for satoa() return codes. ++ * ++ * Revision 1.32 2001/02/27 06:23:15 rgb ++ * Debug line splitting. ++ * ++ * Revision 1.31 2000/11/06 04:35:21 rgb ++ * Clear table *before* releasing other items in radijcleanup. ++ * ++ * Revision 1.30 2000/09/20 04:07:40 rgb ++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in ++ * oopsen. ++ * ++ * Revision 1.29 2000/09/12 03:25:02 rgb ++ * Moved radij_c_version printing to ipsec_version_get_info(). ++ * ++ * Revision 1.28 2000/09/08 19:12:56 rgb ++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. ++ * ++ * Revision 1.27 2000/07/28 14:58:32 rgb ++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5. ++ * ++ * Revision 1.26 2000/05/10 23:11:37 rgb ++ * Comment out most of the startup version information. ++ * ++ * Revision 1.25 2000/01/21 06:21:47 rgb ++ * Change return codes to negative on error. ++ * ++ * Revision 1.24 1999/11/18 04:09:20 rgb ++ * Replaced all kernel version macros to shorter, readable form. ++ * ++ * Revision 1.23 1999/11/17 15:53:41 rgb ++ * Changed all occurrences of #include "../../../lib/freeswan.h" ++ * to #include which works due to -Ilibfreeswan in the ++ * klips/net/ipsec/Makefile. ++ * ++ * Revision 1.22 1999/10/15 22:17:28 rgb ++ * Modify radijcleanup() to call radijcleartree(). ++ * ++ * Revision 1.21 1999/10/08 18:37:34 rgb ++ * Fix end-of-line spacing to sate whining PHMs. ++ * ++ * Revision 1.20 1999/10/01 15:44:54 rgb ++ * Move spinlock header include to 2.1> scope. ++ * ++ * Revision 1.19 1999/10/01 08:35:52 rgb ++ * Add spinlock include to shut up compiler for 2.0.38. ++ * ++ * Revision 1.18 1999/09/23 18:02:52 rgb ++ * De-alarm the search failure message so it doesn't sound so grave. ++ * ++ * Revision 1.17 1999/05/25 21:26:01 rgb ++ * Fix rj_walktree() sanity checking bug. ++ * ++ * Revision 1.16 1999/05/09 03:25:38 rgb ++ * Fix bug introduced by 2.2 quick-and-dirty patch. ++ * ++ * Revision 1.15 1999/05/05 22:02:33 rgb ++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher . ++ * ++ * Revision 1.14 1999/04/29 15:24:15 rgb ++ * Add sanity checking for null pointer arguments. ++ * Standardise an error return method. ++ * ++ * Revision 1.13 1999/04/11 00:29:02 henry ++ * GPL boilerplate ++ * ++ * Revision 1.12 1999/04/06 04:54:28 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ * Revision 1.11 1999/02/17 16:52:53 rgb ++ * Convert DEBUG_IPSEC to KLIPS_PRINT ++ * Clean out unused cruft. ++ * ++ * Revision 1.10 1999/01/22 06:30:05 rgb ++ * Cruft clean-out. ++ * 64-bit clean-up. ++ * ++ * Revision 1.9 1998/12/01 13:22:04 rgb ++ * Added support for debug printing of version info. ++ * ++ * Revision 1.8 1998/11/30 13:22:55 rgb ++ * Rationalised all the klips kernel file headers. They are much shorter ++ * now and won't conflict under RH5.2. ++ * ++ * Revision 1.7 1998/10/25 02:43:26 rgb ++ * Change return type on rj_addroute and rj_delete and add and argument ++ * to the latter to be able to transmit more infomation about errors. ++ * ++ * Revision 1.6 1998/10/19 14:30:06 rgb ++ * Added inclusion of freeswan.h. ++ * ++ * Revision 1.5 1998/10/09 04:33:27 rgb ++ * Added 'klips_debug' prefix to all klips printk debug statements. ++ * Fixed output formatting slightly. ++ * ++ * Revision 1.4 1998/07/28 00:06:59 rgb ++ * Add debug detail to tree traversing. ++ * ++ * Revision 1.3 1998/07/14 18:07:58 rgb ++ * Add a routine to clear the eroute tree. ++ * ++ * Revision 1.2 1998/06/25 20:03:22 rgb ++ * Cleanup #endif comments. Debug output for rj_init. ++ * ++ * Revision 1.1 1998/06/18 21:30:22 henry ++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel ++ * build scripts happier about symlinks ++ * ++ * Revision 1.8 1998/05/25 20:34:15 rgb ++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions. ++ * ++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and ++ * add ipsec_rj_walker_delete. ++ * ++ * Recover memory for eroute table on unload of module. ++ * ++ * Revision 1.7 1998/05/21 12:58:58 rgb ++ * Moved 'extern' definitions to ipsec_radij.h to support /proc 3k limit fix. ++ * ++ * Revision 1.6 1998/04/23 20:57:29 rgb ++ * Cleaned up compiler warnings for unused debugging functions. ++ * ++ * Revision 1.5 1998/04/22 16:51:38 rgb ++ * Tidy up radij debug code from recent rash of modifications to debug code. ++ * ++ * Revision 1.4 1998/04/21 21:28:56 rgb ++ * Rearrange debug switches to change on the fly debug output from user ++ * space. Only kernel changes checked in at this time. radij.c was also ++ * changed to temporarily remove buggy debugging code in rj_delete causing ++ * an OOPS and hence, netlink device open errors. ++ * ++ * Revision 1.3 1998/04/14 17:30:37 rgb ++ * Fix up compiling errors for radij tree memory reclamation. ++ * ++ * Revision 1.2 1998/04/12 22:03:25 rgb ++ * Updated ESP-3DES-HMAC-MD5-96, ++ * ESP-DES-HMAC-MD5-96, ++ * AH-HMAC-MD5-96, ++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository ++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts. ++ * ++ * Fixed eroute references in /proc/net/ipsec*. ++ * ++ * Started to patch module unloading memory leaks in ipsec_netlink and ++ * radij tree unloading. ++ * ++ * Revision 1.1 1998/04/09 03:06:15 henry ++ * sources moved up from linux/net/ipsec ++ * ++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry ++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 ++ * ++ * Revision 0.4 1997/01/15 01:28:15 ji ++ * No changes. ++ * ++ * Revision 0.3 1996/11/20 14:39:04 ji ++ * Minor cleanups. ++ * Rationalized debugging code. ++ * ++ * Revision 0.2 1996/11/02 00:18:33 ji ++ * First limited release. ++ * ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/sysctl_net_ipsec.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,196 @@ ++/* ++ * sysctl interface to net IPSEC subsystem. ++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * RCSID $Id: sysctl_net_ipsec.c,v 1.16 2004/04/06 02:49:26 mcr Exp $ ++ */ ++ ++/* -*- linux-c -*- ++ * ++ * Initiated April 3, 1998, Richard Guy Briggs ++ */ ++ ++#include ++#include ++ ++#include "openswan/ipsec_param.h" ++ ++#ifdef CONFIG_SYSCTL ++ ++#define NET_IPSEC 2112 /* Random number */ ++#ifdef CONFIG_IPSEC_DEBUG ++extern int debug_ah; ++extern int debug_esp; ++extern int debug_tunnel; ++extern int debug_eroute; ++extern int debug_spi; ++extern int debug_radij; ++extern int debug_netlink; ++extern int debug_xform; ++extern int debug_rcv; ++extern int debug_pfkey; ++extern int sysctl_ipsec_debug_verbose; ++#ifdef CONFIG_IPSEC_IPCOMP ++extern int sysctl_ipsec_debug_ipcomp; ++#endif /* CONFIG_IPSEC_IPCOMP */ ++#endif /* CONFIG_IPSEC_DEBUG */ ++ ++extern int sysctl_ipsec_icmp; ++extern int sysctl_ipsec_inbound_policy_check; ++extern int sysctl_ipsec_tos; ++int sysctl_ipsec_regress_pfkey_lossage; ++ ++enum { ++#ifdef CONFIG_IPSEC_DEBUG ++ NET_IPSEC_DEBUG_AH=1, ++ NET_IPSEC_DEBUG_ESP=2, ++ NET_IPSEC_DEBUG_TUNNEL=3, ++ NET_IPSEC_DEBUG_EROUTE=4, ++ NET_IPSEC_DEBUG_SPI=5, ++ NET_IPSEC_DEBUG_RADIJ=6, ++ NET_IPSEC_DEBUG_NETLINK=7, ++ NET_IPSEC_DEBUG_XFORM=8, ++ NET_IPSEC_DEBUG_RCV=9, ++ NET_IPSEC_DEBUG_PFKEY=10, ++ NET_IPSEC_DEBUG_VERBOSE=11, ++ NET_IPSEC_DEBUG_IPCOMP=12, ++#endif /* CONFIG_IPSEC_DEBUG */ ++ NET_IPSEC_ICMP=13, ++ NET_IPSEC_INBOUND_POLICY_CHECK=14, ++ NET_IPSEC_TOS=15, ++ NET_IPSEC_REGRESS_PFKEY_LOSSAGE=16, ++}; ++ ++static ctl_table ipsec_table[] = { ++#ifdef CONFIG_IPSEC_DEBUG ++ { NET_IPSEC_DEBUG_AH, "debug_ah", &debug_ah, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_ESP, "debug_esp", &debug_esp, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_TUNNEL, "debug_tunnel", &debug_tunnel, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_EROUTE, "debug_eroute", &debug_eroute, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_SPI, "debug_spi", &debug_spi, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_RADIJ, "debug_radij", &debug_radij, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_NETLINK, "debug_netlink", &debug_netlink, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_XFORM, "debug_xform", &debug_xform, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_RCV, "debug_rcv", &debug_rcv, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_PFKEY, "debug_pfkey", &debug_pfkey, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_DEBUG_VERBOSE, "debug_verbose",&sysctl_ipsec_debug_verbose, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++#ifdef CONFIG_IPSEC_IPCOMP ++ { NET_IPSEC_DEBUG_IPCOMP, "debug_ipcomp", &sysctl_ipsec_debug_ipcomp, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++#endif /* CONFIG_IPSEC_IPCOMP */ ++ ++#ifdef CONFIG_IPSEC_REGRESS ++ { NET_IPSEC_REGRESS_PFKEY_LOSSAGE, "pfkey_lossage", ++ &sysctl_ipsec_regress_pfkey_lossage, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++#endif /* CONFIG_IPSEC_REGRESS */ ++ ++#endif /* CONFIG_IPSEC_DEBUG */ ++ { NET_IPSEC_ICMP, "icmp", &sysctl_ipsec_icmp, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_INBOUND_POLICY_CHECK, "inbound_policy_check", &sysctl_ipsec_inbound_policy_check, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ { NET_IPSEC_TOS, "tos", &sysctl_ipsec_tos, ++ sizeof(int), 0644, NULL, &proc_dointvec}, ++ {0} ++}; ++ ++static ctl_table ipsec_net_table[] = { ++ { NET_IPSEC, "ipsec", NULL, 0, 0555, ipsec_table }, ++ { 0 } ++}; ++ ++static ctl_table ipsec_root_table[] = { ++ { CTL_NET, "net", NULL, 0, 0555, ipsec_net_table }, ++ { 0 } ++}; ++ ++static struct ctl_table_header *ipsec_table_header; ++ ++int ipsec_sysctl_register(void) ++{ ++ ipsec_table_header = register_sysctl_table(ipsec_root_table, 0); ++ if (!ipsec_table_header) { ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++void ipsec_sysctl_unregister(void) ++{ ++ unregister_sysctl_table(ipsec_table_header); ++} ++ ++#endif /* CONFIG_SYSCTL */ ++ ++/* ++ * $Log: sysctl_net_ipsec.c,v $ ++ * Revision 1.16 2004/04/06 02:49:26 mcr ++ * pullup of algo code from alg-branch. ++ * ++ * Revision 1.15 2002/04/24 07:55:32 mcr ++ * #include patches and Makefiles for post-reorg compilation. ++ * ++ * Revision 1.14 2002/04/24 07:36:35 mcr ++ * Moved from ./klips/net/ipsec/sysctl_net_ipsec.c,v ++ * ++ * Revision 1.13 2002/01/12 02:58:32 mcr ++ * first regression test causes acquire messages to be lost ++ * 100% of the time. This is to help testing of pluto. ++ * ++ * Revision 1.12 2001/06/14 19:35:13 rgb ++ * Update copyright date. ++ * ++ * Revision 1.11 2001/02/26 19:58:13 rgb ++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs. ++ * ++ * Revision 1.10 2000/09/16 01:50:15 rgb ++ * Protect sysctl_ipsec_debug_ipcomp with compiler defines too so that the ++ * linker won't blame rj_delete() for missing symbols. ;-> Damn statics... ++ * ++ * Revision 1.9 2000/09/15 23:17:51 rgb ++ * Moved stuff around to compile with debug off. ++ * ++ * Revision 1.8 2000/09/15 11:37:02 rgb ++ * Merge in heavily modified Svenning Soerensen's ++ * IPCOMP zlib deflate code. ++ * ++ * Revision 1.7 2000/09/15 07:37:15 rgb ++ * Munged silly log comment that was causing a warning. ++ * ++ * Revision 1.6 2000/09/15 04:58:23 rgb ++ * Added tos runtime switch. ++ * Removed 'sysctl_ipsec_' prefix from /proc/sys/net/ipsec/ filenames. ++ * ++ * Revision 1.5 2000/09/12 03:25:28 rgb ++ * Filled in and implemented sysctl. ++ * ++ * Revision 1.4 1999/04/11 00:29:03 henry ++ * GPL boilerplate ++ * ++ * Revision 1.3 1999/04/06 04:54:29 rgb ++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes ++ * patch shell fixes. ++ * ++ */ +--- /dev/null Tue Mar 11 13:02:56 2003 ++++ linux/net/ipsec/version.c Mon Feb 9 13:51:03 2004 +@@ -0,0 +1,44 @@ ++/* ++ * return IPsec version information ++ * Copyright (C) 2001 Henry Spencer. ++ * ++ * This library is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU Library General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This library is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public ++ * License for more details. ++ * ++ * RCSID $Id: version.in.c,v 1.2 2004/04/14 05:09:46 ken Exp $ ++ */ ++ ++#ifdef __KERNEL__ ++#include ++#endif ++ ++#include "openswan.h" ++ ++#define V "cvs2002Mar12_01:19:03" /* substituted in by Makefile */ ++static const char openswan_number[] = V; ++static const char openswan_string[] = "Openswan " V; ++ ++/* ++ - ipsec_version_code - return IPsec version number/code, as string ++ */ ++const char * ++ipsec_version_code() ++{ ++ return openswan_number; ++} ++ ++/* ++ - ipsec_version_string - return full version string ++ */ ++const char * ++ipsec_version_string() ++{ ++ return openswan_string; ++} +RCSID $Id: af_inet.c.fs2_4.patch,v 1.6 2002/04/24 07:36:36 mcr Exp $ +--- ./net/ipv4/af_inet.c.preipsec Wed Apr 26 15:13:17 2000 ++++ ./net/ipv4/af_inet.c Fri Jun 30 15:01:27 2000 +@@ -1019,6 +1019,17 @@ + ip_mr_init(); + #endif + ++#if defined(CONFIG_IPSEC) ++ { ++ extern /* void */ int ipsec_init(void); ++ /* ++ * Initialise AF_INET ESP and AH protocol support including ++ * e-routing and SA tables ++ */ ++ ipsec_init(); ++ } ++#endif /* CONFIG_IPSEC */ ++ + /* + * Create all the /proc entries. + */ +--- /dev/null Fri May 10 13:59:54 2002 ++++ linux/net/ipsec/Makefile.ver Sun Jul 28 22:10:40 2002 +@@ -0,0 +1 @@ ++IPSECVERSION=cvs2002Mar12_01:19:03 +make[1]: Leaving directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0' diff --git a/packages/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff b/packages/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff index e69de29bb2..f848d2a224 100644 --- a/packages/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff +++ b/packages/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff @@ -0,0 +1,64 @@ +--- linux/drivers/net/au1000_eth.c.orig 2004-11-23 12:01:00.551663672 +0100 ++++ linux/drivers/net/au1000_eth.c 2004-11-23 12:08:36.795304096 +0100 +@@ -6,7 +6,9 @@ + * Copyright 2002 TimeSys Corp. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com +- * ++ * Bjoern Riemer 2004 ++ * riemer@fokus.fraunhofer.de or riemer@riemer-nt.de ++ * // fixed the link beat detection with ioctls (SIOCGMIIPHY) + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it +@@ -1383,6 +1385,10 @@ + aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed); + control = MAC_DISABLE_RX_OWN | MAC_RX_ENABLE | MAC_TX_ENABLE; + #ifndef CONFIG_CPU_LITTLE_ENDIAN ++ /*riemer: fix for startup without cable */ ++ if (!link) ++ dev->flags &= ~IFF_RUNNING; ++ + control |= MAC_BIG_ENDIAN; + #endif + if (link && (dev->if_port == IF_PORT_100BASEFX)) { +@@ -1841,17 +1847,35 @@ + + static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + { +- //u16 *data = (u16 *)&rq->ifr_data; ++/* ++// This structure is used in all SIOCxMIIxxx ioctl calls ++struct mii_ioctl_data { ++ 0 u16 phy_id; ++ 1 u16 reg_num; ++ 2 u16 val_in; ++ 3 u16 val_out; ++};*/ ++ u16 *data = (u16 *)&rq->ifr_data; ++ struct au1000_private *aup = (struct au1000_private *) dev->priv; ++ //struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data; + + /* fixme */ + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ +- //data[0] = PHY_ADDRESS; ++ case SIOCGMIIPHY: ++ if (!netif_running(dev)) ++ return -EINVAL; ++ data[0] = aup->phy_addr; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ +- //data[3] = mdio_read(ioaddr, data[0], data[1]); ++ case SIOCGMIIREG: ++ data[3] = mdio_read(dev, data[0], data[1]); ++ //data->val_out = mdio_read(dev,data->phy_id,data->reg_num); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ +- //mdio_write(ioaddr, data[0], data[1], data[2]); ++ case SIOCSMIIREG: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ mdio_write(dev, data[0], data[1],data[2]); + return 0; + default: + return -EOPNOTSUPP; diff --git a/packages/linux/linux-mtx-1-2.4.27/15-au1000-pci-fixup-non-coherent-pre-ac.diff b/packages/linux/linux-mtx-1-2.4.27/15-au1000-pci-fixup-non-coherent-pre-ac.diff index e69de29bb2..0a3896433a 100644 --- a/packages/linux/linux-mtx-1-2.4.27/15-au1000-pci-fixup-non-coherent-pre-ac.diff +++ b/packages/linux/linux-mtx-1-2.4.27/15-au1000-pci-fixup-non-coherent-pre-ac.diff @@ -0,0 +1,19 @@ +--- linux/arch/mips/au1000/common/pci_fixup.c.orig 2004-11-25 20:14:24.907902616 +0100 ++++ linux/arch/mips/au1000/common/pci_fixup.c 2004-11-25 20:27:08.842766864 +0100 +@@ -75,10 +75,13 @@ + + #ifdef CONFIG_NONCOHERENT_IO + /* +- * Set the NC bit in controller for pre-AC silicon ++ * Set the NC bit in controller for Au1500 pre-AC silicon + */ +- au_writel( 1<<16 | au_readl(Au1500_PCI_CFG), Au1500_PCI_CFG); +- printk("Non-coherent PCI accesses enabled\n"); ++ u32 prid = read_c0_prid(); ++ if ( (prid & 0xFF000000) == 0x01000000 && prid < 0x01030202) { ++ au_writel( 1<<16 | au_readl(Au1500_PCI_CFG), Au1500_PCI_CFG); ++ printk("Non-coherent PCI accesses enabled\n"); ++ } + #endif + + set_io_port_base(virt_io_addr); diff --git a/packages/linux/linux-mtx-1-2.4.27/16-i2c.patch b/packages/linux/linux-mtx-1-2.4.27/16-i2c.patch index e69de29bb2..cec737bdfe 100644 --- a/packages/linux/linux-mtx-1-2.4.27/16-i2c.patch +++ b/packages/linux/linux-mtx-1-2.4.27/16-i2c.patch @@ -0,0 +1,7482 @@ +--- linux-old/Documentation/Configure.help Mon Dec 13 16:57:33 2004 ++++ linux/Documentation/Configure.help Mon Dec 13 19:26:23 2004 +@@ -19347,6 +19347,16 @@ + . + The module will be called i2c-velleman.o. + ++Basic I2C on Parallel Port adapter ++CONFIG_I2C_PPORT ++ This supports directly connecting I2C devices to the parallel port. ++ See for more information. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-pport.o. ++ + I2C PCF 8584 interfaces + CONFIG_I2C_ALGOPCF + This allows you to use a range of I2C adapters called PCF adapters. +@@ -19368,6 +19378,15 @@ + . + The module will be called i2c-elektor.o. + ++PCF on the EPP Parallel Port ++CONFIG_I2C_PCFEPP ++ This supports the PCF8584 connected to the parallel port. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-pcf-epp.o. ++ + ITE I2C Algorithm + CONFIG_ITE_I2C_ALGO + This supports the use the ITE8172 I2C interface found on some MIPS +@@ -19405,6 +19424,51 @@ + Supports the SGI interfaces like the ones found on SGI Indy VINO + or SGI O2 MACE. + ++Motorola 8xx I2C algorithm ++CONFIG_I2C_ALGO8XX ++ This is the algorithm that allows you to use Motorola 8xx I2C adapters. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-algo-8xx.o. ++ ++Motorola 8xx I2C interface ++CONFIG_I2C_RPXLITE ++ This supports the Motorola 8xx I2C device. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-rpx.o. ++ ++IBM 405 I2C algorithm ++CONFIG_I2C_IBM_OCP_ALGO ++ This is the algorithm that allows you to use IBM 405 I2C adapters. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-algo-ibm_ocp.o. ++ ++IBM 405 I2C interface ++CONFIG_I2C_IBM_OCP_ADAP ++ This supports the IBM 405 I2C device. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-adap-ibm_ocp.o. ++ ++StrongARM SA-1110 interface ++CONFIG_I2C_FRODO ++ This supports the StrongARM SA-1110 Development Board. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read ++ . ++ The module will be called i2c-frodo.o. ++ + I2C device interface + CONFIG_I2C_CHARDEV + Say Y here to use i2c-* device files, usually found in the /dev +--- linux-old/Documentation/i2c/dev-interface Mon Dec 22 22:44:34 2003 ++++ linux/Documentation/i2c/dev-interface Mon Dec 13 19:26:23 2004 +@@ -89,6 +89,11 @@ + Selects ten bit addresses if select not equals 0, selects normal 7 bit + addresses if select equals 0. Default 0. + ++ioctl(file,I2C_PEC,long select) ++ Selects SMBus PEC (packet error checking) generation and verification ++ if select not equals 0, disables if select equals 0. Default 0. ++ Used only for SMBus transactions. ++ + ioctl(file,I2C_FUNCS,unsigned long *funcs) + Gets the adapter functionality and puts it in *funcs. + +--- linux-old/Documentation/i2c/i2c-pport Thu Jan 1 00:00:00 1970 ++++ linux/Documentation/i2c/i2c-pport Mon Dec 13 19:26:24 2004 +@@ -0,0 +1,67 @@ ++Parallel Port Adapters ++---------------------- ++If you are installing parallel port adapters it means you are probably messing ++around with wires and IC's and the like. If you have purchased a card that ++provides an external i2c/smbus this will require combined algorithm and ++adapter code in a single module. ++If you are doing it yourself by using the parallel port there ++are basically 2 options. ++ ++1) Using the parallel port and using the i2c-pport adapter module and the ++i2c-algo-bit algorithm module together to enable you to wire up your parallel ++port to act as an i2c/smbus. This provides a bus that will enable most ++sensors to work but doesn't support the entire i2c/smbus capability. ++ ++2) Using the parallel port to interface to a Philips PCF8584 parallel to i2c ++adapter chip. You will need to build a bit of a circuit to do this. This ++configuration needs the i2c-pcf-epp adapter module and the i2c-algo-pcf ++algorithm module. This support almost all of the i2c/smbus capabilities. ++ ++ ++i2c-pport Documentation ++----------------------- ++This is a primitive parallel port driver for the i2c bus, which exploits ++features of modern bidirectional parallel ports. ++ ++Bidirectional ports have particular bits connected in following way: ++ ++ | ++ /-----| R ++ --o| |-----| ++ read \-----| /------- Out pin ++ |/ ++ - -|\ ++ write V ++ | ++ --- ++ ++ ++It means when output is set to 1 we can read the port. Therefore ++we can use 2 pins of parallel port as SDA and SCL for i2c bus. It ++is not necessary to add any external - additional parts, we can ++read and write the same port simultaneously. ++ I only use register base+2 so it is possible to use all ++8 data bits of parallel port for other applications (I have ++connected EEPROM and LCD display). I do not use bit Enable Bi-directional ++ Port. The only disadvantage is we can only support 5V chips. ++ ++Layout: ++ ++Cannon 25 pin ++ ++SDA - connect to pin 14 (Auto Linefeed) ++SCL - connect to pin 16 (Initialize Printer) ++GND - connect to pin 18-25 +++5V - use external supply (I use 5V from 3.5" floppy connector) ++ ++no pullups requied ++ ++Module parameters: ++ ++base = 0xXXX ++XXX - 278 or 378 ++ ++That's all. ++ ++Daniel Smolik ++marvin@sitour.cz +--- linux-old/Documentation/i2c/i2c-protocol Mon Dec 22 22:44:34 2003 ++++ linux/Documentation/i2c/i2c-protocol Mon Dec 13 19:26:24 2004 +@@ -65,3 +65,12 @@ + need to emit an Rd instead of a Wr, or vice versa, you set this + flag. For example: + S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P ++ ++ Flags I2C_M_IGNORE_NAK ++ Normally message is interrupted immediately if there is [NA] from the ++ client. Setting this flag treats any [NA] as [A], and all of ++ message is sent. ++ These messages may still fail to SCL lo->hi timeout. ++ ++ Flags I2C_M_NO_RD_ACK ++ In a read message, master A/NA bit is skipped. +--- linux-old/Documentation/i2c/summary Tue Jan 20 15:10:28 2004 ++++ linux/Documentation/i2c/summary Mon Dec 13 19:26:24 2004 +@@ -59,16 +59,16 @@ + i2c-algo-8xx: An algorithm for CPM's I2C device in Motorola 8xx processors (NOT BUILT BY DEFAULT) + i2c-algo-bit: A bit-banging algorithm + i2c-algo-pcf: A PCF 8584 style algorithm +-i2c-algo-ppc405: An algorithm for the I2C device in IBM 405xx processors (NOT BUILT BY DEFAULT) ++i2c-algo-ibm_ocp: An algorithm for the I2C device in IBM 4xx processors (NOT BUILT BY DEFAULT) + + Adapter drivers + --------------- + + i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) + i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) +-i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (BROKEN - missing i2c-pcf-epp.h) ++i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (NOT mkpatched) + i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) +-i2c-ppc405: IBM 405xx processor I2C device (uses i2c-algo-ppc405) (NOT BUILT BY DEFAULT) ++i2c-adap-ibm_ocp: IBM 4xx processor I2C device (uses i2c-algo-ibm_ocp) (NOT BUILT BY DEFAULT) + i2c-pport: Primitive parallel port adapter (uses i2c-algo-bit) + i2c-rpx: RPX board Motorola 8xx I2C device (uses i2c-algo-8xx) (NOT BUILT BY DEFAULT) + i2c-velleman: Velleman K8000 parallel port adapter (uses i2c-algo-bit) +--- linux-old/Documentation/i2c/writing-clients Mon Dec 22 22:44:34 2003 ++++ linux/Documentation/i2c/writing-clients Mon Dec 13 19:26:25 2004 +@@ -24,24 +24,24 @@ + routines, a client structure specific information like the actual I2C + address. + +- struct i2c_driver foo_driver +- { +- /* name */ "Foo version 2.3 and later driver", +- /* id */ I2C_DRIVERID_FOO, +- /* flags */ I2C_DF_NOTIFY, +- /* attach_adapter */ &foo_attach_adapter, +- /* detach_client */ &foo_detach_client, +- /* command */ &foo_command, /* May be NULL */ +- /* inc_use */ &foo_inc_use, /* May be NULL */ +- /* dec_use */ &foo_dec_use /* May be NULL */ +- } ++static struct i2c_driver foo_driver = { ++ .owner = THIS_MODULE, ++ .name = "Foo version 2.3 driver", ++ .id = I2C_DRIVERID_FOO, /* from i2c-id.h, optional */ ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = &foo_attach_adapter, ++ .detach_client = &foo_detach_client, ++ .command = &foo_command /* may be NULL */ ++} + + The name can be chosen freely, and may be upto 40 characters long. Please + use something descriptive here. + +-The id should be a unique ID. The range 0xf000 to 0xffff is reserved for +-local use, and you can use one of those until you start distributing the +-driver. Before you do that, contact the i2c authors to get your own ID(s). ++If used, the id should be a unique ID. The range 0xf000 to 0xffff is ++reserved for local use, and you can use one of those until you start ++distributing the driver, at which time you should contact the i2c authors ++to get your own ID(s). Note that most of the time you don't need an ID ++at all so you can just omit it. + + Don't worry about the flags field; just put I2C_DF_NOTIFY into it. This + means that your driver will be notified when new adapters are found. +@@ -50,43 +50,8 @@ + All other fields are for call-back functions which will be explained + below. + +- +-Module usage count +-================== +- +-If your driver can also be compiled as a module, there are moments at +-which the module can not be removed from memory. For example, when you +-are doing a lengthy transaction, or when you create a /proc directory, +-and some process has entered that directory (this last case is the +-main reason why these call-backs were introduced). +- +-To increase or decrease the module usage count, you can use the +-MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module +-which needs to get its usage count changed; that is why each driver +-module has to implement its own callback. +- +- void foo_inc_use (struct i2c_client *client) +- { +- #ifdef MODULE +- MOD_INC_USE_COUNT; +- #endif +- } +- +- void foo_dec_use (struct i2c_client *client) +- { +- #ifdef MODULE +- MOD_DEC_USE_COUNT; +- #endif +- } +- +-Do not call these call-back functions directly; instead, use one of the +-following functions defined in i2c.h: +- void i2c_inc_use_client(struct i2c_client *); +- void i2c_dec_use_client(struct i2c_client *); +- +-You should *not* increase the module count just because a device is +-detected and a client created. This would make it impossible to remove +-an adapter driver! ++There use to be two additional fields in this structure, inc_use et dec_use, ++for module usage count, but these fields were obsoleted and removed. + + + Extra client data +--- linux-old/drivers/i2c/i2c-adap-ibm_ocp.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-adap-ibm_ocp.c Mon Dec 13 19:26:26 2004 +@@ -0,0 +1,346 @@ ++/* ++ ------------------------------------------------------------------------- ++ i2c-adap-ibm_ocp.c i2c-hw access for the IIC peripheral on the IBM PPC 405 ++ ------------------------------------------------------------------------- ++ ++ Ian DaSilva, MontaVista Software, Inc. ++ idasilva@mvista.com or source@mvista.com ++ ++ Copyright 2000 MontaVista Software Inc. ++ ++ Changes made to support the IIC peripheral on the IBM PPC 405 ++ ++ ++ ---------------------------------------------------------------------------- ++ This file was highly leveraged from i2c-elektor.c, which was created ++ by Simon G. Vogl and Hans Berglund: ++ ++ ++ Copyright (C) 1995-97 Simon G. Vogl ++ 1998-99 Hans Berglund ++ ++ With some changes from Kyösti Mälkki and even ++ Frodo Looijaard ++ ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ---------------------------------------------------------------------------- ++ ++ History: 01/20/12 - Armin ++ akuster@mvista.com ++ ported up to 2.4.16+ ++ ++ Version 02/03/25 - Armin ++ converted to ocp format ++ removed commented out or #if 0 code ++ ++ TODO: convert to ocp_register ++ add PM hooks ++ ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * This next section is configurable, and it is used to set the number ++ * of i2c controllers in the system. The default number of instances is 1, ++ * however, this should be changed to reflect your system's configuration. ++ */ ++ ++/* ++ * The STB03xxx, with a PPC405 core, has two i2c controllers. ++ */ ++//(sizeof(IIC_ADDR)/sizeof(struct iic_regs)) ++extern iic_t *IIC_ADDR[]; ++static struct iic_ibm iic_ibmocp_adaps[IIC_NUMS][5]; ++ ++static struct i2c_algo_iic_data *iic_ibmocp_data[IIC_NUMS]; ++static struct i2c_adapter *iic_ibmocp_ops[IIC_NUMS]; ++ ++static int i2c_debug=0; ++static wait_queue_head_t iic_wait[IIC_NUMS]; ++static int iic_pending; ++static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED; ++ ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) if (i2c_debug>=1) x ++#define DEB2(x) if (i2c_debug>=2) x ++#define DEB3(x) if (i2c_debug>=3) x ++#define DEBE(x) x /* error messages */ ++ ++/* ----- local functions ---------------------------------------------- */ ++ ++// ++// Description: Write a byte to IIC hardware ++// ++static void iic_ibmocp_setbyte(void *data, int ctl, int val) ++{ ++ // writeb resolves to a write to the specified memory location ++ // plus a call to eieio. eieio ensures that all instructions ++ // preceding it are completed before any further stores are ++ // completed. ++ // Delays at this level (to protect writes) are not needed here. ++ writeb(val, ctl); ++} ++ ++ ++// ++// Description: Read a byte from IIC hardware ++// ++static int iic_ibmocp_getbyte(void *data, int ctl) ++{ ++ int val; ++ ++ val = readb(ctl); ++ return (val); ++} ++ ++ ++// ++// Description: Return our slave address. This is the address ++// put on the I2C bus when another master on the bus wants to address us ++// as a slave ++// ++static int iic_ibmocp_getown(void *data) ++{ ++ return(((struct iic_ibm *)(data))->iic_own); ++} ++ ++ ++// ++// Description: Return the clock rate ++// ++static int iic_ibmocp_getclock(void *data) ++{ ++ return(((struct iic_ibm *)(data))->iic_clock); ++} ++ ++ ++ ++// ++// Description: Put this process to sleep. We will wake up when the ++// IIC controller interrupts. ++// ++static void iic_ibmocp_waitforpin(void *data) { ++ ++ int timeout = 2; ++ struct iic_ibm *priv_data = data; ++ ++ // ++ // If interrupts are enabled (which they are), then put the process to ++ // sleep. This process will be awakened by two events -- either the ++ // the IIC peripheral interrupts or the timeout expires. ++ // ++ if (priv_data->iic_irq > 0) { ++ spin_lock_irq(&irq_driver_lock); ++ if (iic_pending == 0) { ++ interruptible_sleep_on_timeout(&(iic_wait[priv_data->index]), timeout*HZ ); ++ } else ++ iic_pending = 0; ++ spin_unlock_irq(&irq_driver_lock); ++ } else { ++ // ++ // If interrupts are not enabled then delay for a reasonable amount ++ // of time and return. We expect that by time we return to the calling ++ // function that the IIC has finished our requested transaction and ++ // the status bit reflects this. ++ // ++ // udelay is probably not the best choice for this since it is ++ // the equivalent of a busy wait ++ // ++ udelay(100); ++ } ++ //printk("iic_ibmocp_waitforpin: exitting\n"); ++} ++ ++ ++// ++// Description: The registered interrupt handler ++// ++static void iic_ibmocp_handler(int this_irq, void *dev_id, struct pt_regs *regs) ++{ ++ int ret; ++ struct iic_regs *iic; ++ struct iic_ibm *priv_data = dev_id; ++ iic = (struct iic_regs *) priv_data->iic_base; ++ iic_pending = 1; ++ DEB2(printk("iic_ibmocp_handler: in interrupt handler\n")); ++ // Read status register ++ ret = readb((int) &(iic->sts)); ++ DEB2(printk("iic_ibmocp_handler: status = %x\n", ret)); ++ // Clear status register. See IBM PPC 405 reference manual for details ++ writeb(0x0a, (int) &(iic->sts)); ++ wake_up_interruptible(&(iic_wait[priv_data->index])); ++} ++ ++ ++// ++// Description: This function is very hardware dependent. First, we lock ++// the region of memory where out registers exist. Next, we request our ++// interrupt line and register its associated handler. Our IIC peripheral ++// uses interrupt number 2, as specified by the 405 reference manual. ++// ++static int iic_hw_resrc_init(int instance) ++{ ++ ++ DEB(printk("iic_hw_resrc_init: Physical Base address: 0x%x\n", (u32) IIC_ADDR[instance] )); ++ iic_ibmocp_adaps[instance]->iic_base = (u32)ioremap((unsigned long)IIC_ADDR[instance],PAGE_SIZE); ++ ++ DEB(printk("iic_hw_resrc_init: ioremapped base address: 0x%x\n", iic_ibmocp_adaps[instance]->iic_base)); ++ ++ if (iic_ibmocp_adaps[instance]->iic_irq > 0) { ++ ++ if (request_irq(iic_ibmocp_adaps[instance]->iic_irq, iic_ibmocp_handler, ++ 0, "IBM OCP IIC", iic_ibmocp_adaps[instance]) < 0) { ++ printk(KERN_ERR "iic_hw_resrc_init: Request irq%d failed\n", ++ iic_ibmocp_adaps[instance]->iic_irq); ++ iic_ibmocp_adaps[instance]->iic_irq = 0; ++ } else { ++ DEB3(printk("iic_hw_resrc_init: Enabled interrupt\n")); ++ } ++ } ++ return 0; ++} ++ ++ ++// ++// Description: Release irq and memory ++// ++static void iic_ibmocp_release(void) ++{ ++ int i; ++ ++ for(i=0; idata; ++ if (priv_data->iic_irq > 0) { ++ disable_irq(priv_data->iic_irq); ++ free_irq(priv_data->iic_irq, 0); ++ } ++ kfree(iic_ibmocp_data[i]); ++ kfree(iic_ibmocp_ops[i]); ++ } ++} ++ ++ ++// ++// Description: Called when the module is loaded. This function starts the ++// cascade of calls up through the heirarchy of i2c modules (i.e. up to the ++// algorithm layer and into to the core layer) ++// ++static int __init iic_ibmocp_init(void) ++{ ++ int i; ++ ++ printk(KERN_INFO "iic_ibmocp_init: IBM on-chip iic adapter module\n"); ++ ++ for(i=0; iiic_irq = IIC_IRQ(0); ++ break; ++ case 1: ++ iic_ibmocp_adaps[i]->iic_irq = IIC_IRQ(1); ++ break; ++ } ++ iic_ibmocp_adaps[i]->iic_clock = IIC_CLOCK; ++ iic_ibmocp_adaps[i]->iic_own = IIC_OWN; ++ iic_ibmocp_adaps[i]->index = i; ++ ++ DEB(printk("irq %x\n", iic_ibmocp_adaps[i]->iic_irq)); ++ DEB(printk("clock %x\n", iic_ibmocp_adaps[i]->iic_clock)); ++ DEB(printk("own %x\n", iic_ibmocp_adaps[i]->iic_own)); ++ DEB(printk("index %x\n", iic_ibmocp_adaps[i]->index)); ++ ++ ++ iic_ibmocp_data[i]->data = (struct iic_regs *)iic_ibmocp_adaps[i]; ++ iic_ibmocp_data[i]->setiic = iic_ibmocp_setbyte; ++ iic_ibmocp_data[i]->getiic = iic_ibmocp_getbyte; ++ iic_ibmocp_data[i]->getown = iic_ibmocp_getown; ++ iic_ibmocp_data[i]->getclock = iic_ibmocp_getclock; ++ iic_ibmocp_data[i]->waitforpin = iic_ibmocp_waitforpin; ++ iic_ibmocp_data[i]->udelay = 80; ++ iic_ibmocp_data[i]->mdelay = 80; ++ iic_ibmocp_data[i]->timeout = HZ; ++ ++ iic_ibmocp_ops[i] = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); ++ if(iic_ibmocp_ops[i] == NULL) { ++ return -ENOMEM; ++ } ++ memset(iic_ibmocp_ops[i], 0, sizeof(struct i2c_adapter)); ++ strcpy(iic_ibmocp_ops[i]->name, "IBM OCP IIC adapter"); ++ iic_ibmocp_ops[i]->owner = THIS_MODULE; ++ iic_ibmocp_ops[i]->id = I2C_HW_OCP; ++ iic_ibmocp_ops[i]->algo = NULL; ++ iic_ibmocp_ops[i]->algo_data = iic_ibmocp_data[i]; ++ ++ ++ init_waitqueue_head(&(iic_wait[i])); ++ if (iic_hw_resrc_init(i) == 0) { ++ if (i2c_ocp_add_bus(iic_ibmocp_ops[i]) < 0) ++ return -ENODEV; ++ } else { ++ return -ENODEV; ++ } ++ DEB(printk(KERN_INFO "iic_ibmocp_init: found device at %#x.\n\n", iic_ibmocp_adaps[i]->iic_base)); ++ } ++ return 0; ++} ++ ++ ++static void __exit iic_ibmocp_exit(void) ++{ ++ int i; ++ ++ for(i=0; i"); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for PPC 405 IIC bus adapter"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(base, "i"); ++MODULE_PARM(irq, "i"); ++MODULE_PARM(clock, "i"); ++MODULE_PARM(own, "i"); ++MODULE_PARM(i2c_debug,"i"); ++ ++ ++module_init(iic_ibmocp_init); ++module_exit(iic_ibmocp_exit); +--- linux-old/drivers/i2c/i2c-algo-8xx.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-algo-8xx.c Mon Dec 13 19:26:27 2004 +@@ -0,0 +1,616 @@ ++/* ++ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM ++ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). ++ * ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * moved into proper i2c interface; separated out platform specific ++ * parts into i2c-rpx.c ++ * Brad Parker (brad@heeltoe.com) ++ */ ++ ++// XXX todo ++// timeout sleep? ++ ++/* $Id: i2c-algo-8xx.c,v 1.14 2003/07/25 07:56:42 khali Exp $ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define CPM_MAX_READ 513 ++/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an older CPU(earlier than rev D4) */ ++static wait_queue_head_t iic_wait; ++static ushort r_tbase, r_rbase; ++ ++int cpm_debug = 0; ++ ++static void ++cpm_iic_interrupt(void *dev_id, struct pt_regs *regs) ++{ ++ volatile i2c8xx_t *i2c = (i2c8xx_t *)dev_id; ++ if (cpm_debug > 1) ++ printk("cpm_iic_interrupt(dev_id=%p)\n", dev_id); ++#if 0 ++ /* Chip errata, clear enable. This is not needed on rev D4 CPUs */ ++ /* This should probably be removed and replaced by I2C_CHIP_ERRATA stuff */ ++ /* Someone with a buggy CPU needs to confirm that */ ++ i2c->i2c_i2mod &= ~1; ++#endif ++ /* Clear interrupt. ++ */ ++ i2c->i2c_i2cer = 0xff; ++ ++ /* Get 'me going again. ++ */ ++ wake_up_interruptible(&iic_wait); ++} ++ ++static void ++cpm_iic_init(struct i2c_algo_8xx_data *cpm) ++{ ++ volatile iic_t *iip = cpm->iip; ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ unsigned char brg; ++ bd_t *bd = (bd_t *)__res; ++ ++ if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init()\n"); ++ ++ /* Initialize the parameter ram. ++ * We need to make sure many things are initialized to zero, ++ * especially in the case of a microcode patch. ++ */ ++ iip->iic_rstate = 0; ++ iip->iic_rdp = 0; ++ iip->iic_rbptr = 0; ++ iip->iic_rbc = 0; ++ iip->iic_rxtmp = 0; ++ iip->iic_tstate = 0; ++ iip->iic_tdp = 0; ++ iip->iic_tbptr = 0; ++ iip->iic_tbc = 0; ++ iip->iic_txtmp = 0; ++ ++ /* Set up the IIC parameters in the parameter ram. ++ */ ++ iip->iic_tbase = r_tbase = cpm->dp_addr; ++ iip->iic_rbase = r_rbase = cpm->dp_addr + sizeof(cbd_t)*2; ++ ++ iip->iic_tfcr = SMC_EB; ++ iip->iic_rfcr = SMC_EB; ++ ++ /* Set maximum receive size. ++ */ ++ iip->iic_mrblr = CPM_MAX_READ; ++ ++ /* Initialize Tx/Rx parameters. ++ */ ++ if (cpm->reloc == 0) { ++ volatile cpm8xx_t *cp = cpm->cp; ++ ++ cp->cp_cpcr = ++ mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG; ++ while (cp->cp_cpcr & CPM_CR_FLG); ++ } else { ++ iip->iic_rbptr = iip->iic_rbase; ++ iip->iic_tbptr = iip->iic_tbase; ++ iip->iic_rstate = 0; ++ iip->iic_tstate = 0; ++ } ++ ++ /* Select an arbitrary address. Just make sure it is unique. ++ */ ++ i2c->i2c_i2add = 0xfe; ++ ++ /* Make clock run at 60 KHz. ++ */ ++ brg = (unsigned char) (bd->bi_intfreq/(32*2*60000) -3); ++ i2c->i2c_i2brg = brg; ++ ++ i2c->i2c_i2mod = 0x00; ++ i2c->i2c_i2com = 0x01; /* Master mode */ ++ ++ /* Disable interrupts. ++ */ ++ i2c->i2c_i2cmr = 0; ++ i2c->i2c_i2cer = 0xff; ++ ++ init_waitqueue_head(&iic_wait); ++ ++ /* Install interrupt handler. ++ */ ++ if (cpm_debug) { ++ printk ("%s[%d] Install ISR for IRQ %d\n", ++ __func__,__LINE__, CPMVEC_I2C); ++ } ++ (*cpm->setisr)(CPMVEC_I2C, cpm_iic_interrupt, (void *)i2c); ++} ++ ++ ++static int ++cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm) ++{ ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ ++ /* Shut down IIC. ++ */ ++ i2c->i2c_i2mod &= ~1; ++ i2c->i2c_i2cmr = 0; ++ i2c->i2c_i2cer = 0xff; ++ ++ return(0); ++} ++ ++static void ++cpm_reset_iic_params(volatile iic_t *iip) ++{ ++ iip->iic_tbase = r_tbase; ++ iip->iic_rbase = r_rbase; ++ ++ iip->iic_tfcr = SMC_EB; ++ iip->iic_rfcr = SMC_EB; ++ ++ iip->iic_mrblr = CPM_MAX_READ; ++ ++ iip->iic_rstate = 0; ++ iip->iic_rdp = 0; ++ iip->iic_rbptr = iip->iic_rbase; ++ iip->iic_rbc = 0; ++ iip->iic_rxtmp = 0; ++ iip->iic_tstate = 0; ++ iip->iic_tdp = 0; ++ iip->iic_tbptr = iip->iic_tbase; ++ iip->iic_tbc = 0; ++ iip->iic_txtmp = 0; ++} ++ ++#define BD_SC_NAK ((ushort)0x0004) /* NAK - did not respond */ ++#define BD_SC_OV ((ushort)0x0002) /* OV - receive overrun */ ++#define CPM_CR_CLOSE_RXBD ((ushort)0x0007) ++ ++static void force_close(struct i2c_algo_8xx_data *cpm) ++{ ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ if (cpm->reloc == 0) { /* micro code disabled */ ++ volatile cpm8xx_t *cp = cpm->cp; ++ ++ if (cpm_debug) printk("force_close()\n"); ++ cp->cp_cpcr = ++ mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) | ++ CPM_CR_FLG; ++ ++ while (cp->cp_cpcr & CPM_CR_FLG); ++ } ++ i2c->i2c_i2cmr = 0x00; /* Disable all interrupts */ ++ i2c->i2c_i2cer = 0xff; ++} ++ ++ ++/* Read from IIC... ++ * abyte = address byte, with r/w flag already set ++ */ ++static int ++cpm_iic_read(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf, int count) ++{ ++ volatile iic_t *iip = cpm->iip; ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ volatile cpm8xx_t *cp = cpm->cp; ++ volatile cbd_t *tbdf, *rbdf; ++ u_char *tb; ++ unsigned long flags, tmo; ++ ++ if (count >= CPM_MAX_READ) ++ return -EINVAL; ++ ++ /* check for and use a microcode relocation patch */ ++ if (cpm->reloc) { ++ cpm_reset_iic_params(iip); ++ } ++ ++ tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; ++ rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase]; ++ ++ /* To read, we need an empty buffer of the proper length. ++ * All that is used is the first byte for address, the remainder ++ * is just used for timing (and doesn't really have to exist). ++ */ ++ tb = cpm->temp; ++ tb = (u_char *)(((uint)tb + 15) & ~15); ++ tb[0] = abyte; /* Device address byte w/rw flag */ ++ ++ flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1)); ++ ++ if (cpm_debug) printk("cpm_iic_read(abyte=0x%x)\n", abyte); ++ ++ tbdf->cbd_bufaddr = __pa(tb); ++ tbdf->cbd_datlen = count + 1; ++ tbdf->cbd_sc = ++ BD_SC_READY | BD_SC_LAST | ++ BD_SC_WRAP | BD_IIC_START; ++ ++ iip->iic_mrblr = count +1; /* prevent excessive read, +1 ++ is needed otherwise will the ++ RXB interrupt come too early */ ++ ++ /* flush will invalidate too. */ ++ flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count)); ++ ++ rbdf->cbd_datlen = 0; ++ rbdf->cbd_bufaddr = __pa(buf); ++ rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT; ++ if(count > 16){ ++ /* Chip bug, set enable here */ ++ local_irq_save(flags); ++ i2c->i2c_i2cmr = 0x13; /* Enable some interupts */ ++ i2c->i2c_i2cer = 0xff; ++ i2c->i2c_i2mod |= 1; /* Enable */ ++ i2c->i2c_i2com |= 0x80; /* Begin transmission */ ++ ++ /* Wait for IIC transfer */ ++ tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ); ++ local_irq_restore(flags); ++ } else { /* busy wait for small transfers, its faster */ ++ i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */ ++ i2c->i2c_i2cer = 0xff; ++ i2c->i2c_i2mod |= 1; /* Enable */ ++ i2c->i2c_i2com |= 0x80; /* Begin transmission */ ++ tmo = jiffies + 1*HZ; ++ while(!(i2c->i2c_i2cer & 0x11 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */ ++ } ++ ++ if (signal_pending(current) || !tmo){ ++ force_close(cpm); ++ if(cpm_debug) ++ printk("IIC read: timeout!\n"); ++ return -EIO; ++ } ++#ifdef I2C_CHIP_ERRATA ++ /* Chip errata, clear enable. This is not needed on rev D4 CPUs. ++ Disabling I2C too early may cause too short stop condition */ ++ udelay(4); ++ i2c->i2c_i2mod &= ~1; ++#endif ++ if (cpm_debug) { ++ printk("tx sc %04x, rx sc %04x\n", ++ tbdf->cbd_sc, rbdf->cbd_sc); ++ } ++ ++ if (tbdf->cbd_sc & BD_SC_READY) { ++ printk("IIC read; complete but tbuf ready\n"); ++ force_close(cpm); ++ printk("tx sc %04x, rx sc %04x\n", ++ tbdf->cbd_sc, rbdf->cbd_sc); ++ } ++ ++ if (tbdf->cbd_sc & BD_SC_NAK) { ++ if (cpm_debug) ++ printk("IIC read; no ack\n"); ++ return -EREMOTEIO; ++ } ++ ++ if (rbdf->cbd_sc & BD_SC_EMPTY) { ++ /* force_close(cpm); */ ++ if (cpm_debug){ ++ printk("IIC read; complete but rbuf empty\n"); ++ printk("tx sc %04x, rx sc %04x\n", ++ tbdf->cbd_sc, rbdf->cbd_sc); ++ } ++ return -EREMOTEIO; ++ } ++ ++ if (rbdf->cbd_sc & BD_SC_OV) { ++ if (cpm_debug) ++ printk("IIC read; Overrun\n"); ++ return -EREMOTEIO;; ++ } ++ ++ if (cpm_debug) printk("read %d bytes\n", rbdf->cbd_datlen); ++ ++ if (rbdf->cbd_datlen < count) { ++ if (cpm_debug) ++ printk("IIC read; short, wanted %d got %d\n", ++ count, rbdf->cbd_datlen); ++ return 0; ++ } ++ ++ return count; ++} ++ ++/* Write to IIC... ++ * addr = address byte, with r/w flag already set ++ */ ++static int ++cpm_iic_write(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf,int count) ++{ ++ volatile iic_t *iip = cpm->iip; ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ volatile cpm8xx_t *cp = cpm->cp; ++ volatile cbd_t *tbdf; ++ u_char *tb; ++ unsigned long flags, tmo; ++ ++ /* check for and use a microcode relocation patch */ ++ if (cpm->reloc) { ++ cpm_reset_iic_params(iip); ++ } ++ tb = cpm->temp; ++ tb = (u_char *)(((uint)tb + 15) & ~15); ++ *tb = abyte; /* Device address byte w/rw flag */ ++ ++ flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1)); ++ flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count)); ++ ++ if (cpm_debug) printk("cpm_iic_write(abyte=0x%x)\n", abyte); ++ ++ /* set up 2 descriptors */ ++ tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; ++ ++ tbdf[0].cbd_bufaddr = __pa(tb); ++ tbdf[0].cbd_datlen = 1; ++ tbdf[0].cbd_sc = BD_SC_READY | BD_IIC_START; ++ ++ tbdf[1].cbd_bufaddr = __pa(buf); ++ tbdf[1].cbd_datlen = count; ++ tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP; ++ ++ if(count > 16){ ++ /* Chip bug, set enable here */ ++ local_irq_save(flags); ++ i2c->i2c_i2cmr = 0x13; /* Enable some interupts */ ++ i2c->i2c_i2cer = 0xff; ++ i2c->i2c_i2mod |= 1; /* Enable */ ++ i2c->i2c_i2com |= 0x80; /* Begin transmission */ ++ ++ /* Wait for IIC transfer */ ++ tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ); ++ local_irq_restore(flags); ++ } else { /* busy wait for small transfers, its faster */ ++ i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */ ++ i2c->i2c_i2cer = 0xff; ++ i2c->i2c_i2mod |= 1; /* Enable */ ++ i2c->i2c_i2com |= 0x80; /* Begin transmission */ ++ tmo = jiffies + 1*HZ; ++ while(!(i2c->i2c_i2cer & 0x12 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */ ++ } ++ ++ if (signal_pending(current) || !tmo){ ++ force_close(cpm); ++ if(cpm_debug && !tmo) ++ printk("IIC write: timeout!\n"); ++ return -EIO; ++ } ++ ++#if I2C_CHIP_ERRATA ++ /* Chip errata, clear enable. This is not needed on rev D4 CPUs. ++ Disabling I2C too early may cause too short stop condition */ ++ udelay(4); ++ i2c->i2c_i2mod &= ~1; ++#endif ++ if (cpm_debug) { ++ printk("tx0 sc %04x, tx1 sc %04x\n", ++ tbdf[0].cbd_sc, tbdf[1].cbd_sc); ++ } ++ ++ if (tbdf->cbd_sc & BD_SC_NAK) { ++ if (cpm_debug) ++ printk("IIC write; no ack\n"); ++ return 0; ++ } ++ ++ if (tbdf->cbd_sc & BD_SC_READY) { ++ if (cpm_debug) ++ printk("IIC write; complete but tbuf ready\n"); ++ return 0; ++ } ++ ++ return count; ++} ++ ++/* See if an IIC address exists.. ++ * addr = 7 bit address, unshifted ++ */ ++static int ++cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr) ++{ ++ volatile iic_t *iip = cpm->iip; ++ volatile i2c8xx_t *i2c = cpm->i2c; ++ volatile cpm8xx_t *cp = cpm->cp; ++ volatile cbd_t *tbdf, *rbdf; ++ u_char *tb; ++ unsigned long flags, len, tmo; ++ ++ if (cpm_debug > 1) ++ printk("cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr); ++ ++ /* check for and use a microcode relocation patch */ ++ if (cpm->reloc) { ++ cpm_reset_iic_params(iip); ++ } ++ ++ if (cpm_debug && addr == 0) { ++ printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr); ++ printk("iic_tbase %d, r_tbase %d\n", iip->iic_tbase, r_tbase); ++ } ++ ++ tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase]; ++ rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase]; ++ ++ tb = cpm->temp; ++ tb = (u_char *)(((uint)tb + 15) & ~15); ++ ++ /* do a simple read */ ++ tb[0] = (addr << 1) | 1; /* device address (+ read) */ ++ len = 2; ++ ++ flush_dcache_range((unsigned long) tb, (unsigned long) (tb+2)); ++ ++ tbdf->cbd_bufaddr = __pa(tb); ++ tbdf->cbd_datlen = len; ++ tbdf->cbd_sc = ++ BD_SC_READY | BD_SC_LAST | ++ BD_SC_WRAP | BD_IIC_START; ++ ++ rbdf->cbd_datlen = 0; ++ rbdf->cbd_bufaddr = __pa(tb+2); ++ rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT; ++ ++ local_irq_save(flags); ++ i2c->i2c_i2cmr = 0x13; /* Enable some interupts */ ++ i2c->i2c_i2cer = 0xff; ++ i2c->i2c_i2mod |= 1; /* Enable */ ++ i2c->i2c_i2com |= 0x80; /* Begin transmission */ ++ ++ if (cpm_debug > 1) printk("about to sleep\n"); ++ ++ /* wait for IIC transfer */ ++ tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ); ++ local_irq_restore(flags); ++ ++#ifdef I2C_CHIP_ERRATA ++ /* Chip errata, clear enable. This is not needed on rev D4 CPUs. ++ Disabling I2C too early may cause too short stop condition */ ++ udelay(4); ++ i2c->i2c_i2mod &= ~1; ++#endif ++ ++ if (signal_pending(current) || !tmo){ ++ force_close(cpm); ++ if(cpm_debug && !tmo) ++ printk("IIC tryaddress: timeout!\n"); ++ return -EIO; ++ } ++ ++ if (cpm_debug > 1) printk("back from sleep\n"); ++ ++ if (tbdf->cbd_sc & BD_SC_NAK) { ++ if (cpm_debug > 1) printk("IIC try; no ack\n"); ++ return 0; ++ } ++ ++ if (tbdf->cbd_sc & BD_SC_READY) { ++ printk("IIC try; complete but tbuf ready\n"); ++ } ++ ++ return 1; ++} ++ ++static int cpm_xfer(struct i2c_adapter *adap, ++ struct i2c_msg msgs[], ++ int num) ++{ ++ struct i2c_algo_8xx_data *cpm = adap->algo_data; ++ struct i2c_msg *pmsg; ++ int i, ret; ++ u_char addr; ++ ++ for (i = 0; i < num; i++) { ++ pmsg = &msgs[i]; ++ ++ if (cpm_debug) ++ printk("i2c-algo-8xx.o: " ++ "#%d addr=0x%x flags=0x%x len=%d\n buf=%lx\n", ++ i, pmsg->addr, pmsg->flags, pmsg->len, (unsigned long)pmsg->buf); ++ ++ addr = pmsg->addr << 1; ++ if (pmsg->flags & I2C_M_RD ) ++ addr |= 1; ++ if (pmsg->flags & I2C_M_REV_DIR_ADDR ) ++ addr ^= 1; ++ ++ if (!(pmsg->flags & I2C_M_NOSTART)) { ++ } ++ if (pmsg->flags & I2C_M_RD ) { ++ /* read bytes into buffer*/ ++ ret = cpm_iic_read(cpm, addr, pmsg->buf, pmsg->len); ++ if (cpm_debug) ++ printk("i2c-algo-8xx.o: read %d bytes\n", ret); ++ if (ret < pmsg->len ) { ++ return (ret<0)? ret : -EREMOTEIO; ++ } ++ } else { ++ /* write bytes from buffer */ ++ ret = cpm_iic_write(cpm, addr, pmsg->buf, pmsg->len); ++ if (cpm_debug) ++ printk("i2c-algo-8xx.o: wrote %d\n", ret); ++ if (ret < pmsg->len ) { ++ return (ret<0) ? ret : -EREMOTEIO; ++ } ++ } ++ } ++ return (num); ++} ++ ++static u32 cpm_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | ++ I2C_FUNC_PROTOCOL_MANGLING; ++} ++ ++/* -----exported algorithm data: ------------------------------------- */ ++ ++static struct i2c_algorithm cpm_algo = { ++ .owner = THIS_MODULE, ++ .name = "MPC8xx CPM algorithm", ++ .id = I2C_ALGO_MPC8XX, ++ .master_xfer = cpm_xfer, ++ .functionality = cpm_func, ++}; ++ ++/* ++ * registering functions to load algorithms at runtime ++ */ ++int i2c_8xx_add_bus(struct i2c_adapter *adap) ++{ ++ int i; ++ struct i2c_algo_8xx_data *cpm = adap->algo_data; ++ ++ if (cpm_debug) ++ printk("i2c-algo-8xx.o: hw routines for %s registered.\n", ++ adap->name); ++ ++ /* register new adapter to i2c module... */ ++ ++ adap->id |= cpm_algo.id; ++ adap->algo = &cpm_algo; ++ ++ i2c_add_adapter(adap); ++ cpm_iic_init(cpm); ++} ++ ++ ++int i2c_8xx_del_bus(struct i2c_adapter *adap) ++{ ++ struct i2c_algo_8xx_data *cpm = adap->algo_data; ++ ++ cpm_iic_shutdown(cpm); ++ ++ return i2c_del_adapter(adap); ++} ++ ++EXPORT_SYMBOL(i2c_8xx_add_bus); ++EXPORT_SYMBOL(i2c_8xx_del_bus); ++ ++MODULE_AUTHOR("Brad Parker "); ++MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm"); ++MODULE_LICENSE("GPL"); +--- linux-old/include/linux/i2c-algo-8xx.h Thu Jan 1 00:00:00 1970 ++++ linux/include/linux/i2c-algo-8xx.h Mon Dec 13 19:26:27 2004 +@@ -0,0 +1,43 @@ ++/* ------------------------------------------------------------------------- */ ++/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM */ ++/* ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* $Id: i2c-algo-8xx.h,v 1.7 2003/08/01 20:56:38 khali Exp $ */ ++ ++#ifndef _LINUX_I2C_ALGO_8XX_H ++#define _LINUX_I2C_ALGO_8XX_H ++ ++#include "asm/commproc.h" ++ ++struct i2c_algo_8xx_data { ++ uint dp_addr; ++ int reloc; ++ volatile i2c8xx_t *i2c; ++ volatile iic_t *iip; ++ volatile cpm8xx_t *cp; ++ ++ int (*setisr) (int irq, ++ void (*func)(void *, void *), ++ void *data); ++ ++ u_char temp[513]; ++}; ++ ++int i2c_8xx_add_bus(struct i2c_adapter *); ++int i2c_8xx_del_bus(struct i2c_adapter *); ++ ++#endif /* _LINUX_I2C_ALGO_8XX_H */ +--- linux-old/drivers/i2c/i2c-algo-bit.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-algo-bit.c Mon Dec 13 19:26:27 2004 +@@ -18,24 +18,22 @@ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* ------------------------------------------------------------------------- */ + +-/* With some changes from Kyösti Mälkki and even +- Frodo Looijaard */ ++/* With some changes from Frodo Looijaard , Kyösti Mälkki ++ and Jean Delvare */ + +-/* $Id: i2c-algo-bit.c,v 1.30 2001/07/29 02:44:25 mds Exp $ */ ++/* $Id: i2c-algo-bit.c,v 1.50 2003/12/22 20:03:39 khali Exp $ */ + + #include + #include + #include + #include + #include +-#include +-#include + #include + #include +- + #include + #include + ++ + /* ----- global defines ----------------------------------------------- */ + #define DEB(x) if (i2c_debug>=1) x; + #define DEB2(x) if (i2c_debug>=2) x; +@@ -43,27 +41,13 @@ + #define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +-/* debugging - slow down transfer to have a look at the data .. */ +-/* I use this with two leds&resistors, each one connected to sda,scl */ +-/* respectively. This makes sure that the algorithm works. Some chips */ +-/* might not like this, as they have an internal timeout of some mils */ +-/* +-#define SLO_IO jif=jiffies;while(time_before_eq(jiffies, jif+i2c_table[minor].veryslow))\ +- if (need_resched) schedule(); +-*/ +- + + /* ----- global variables --------------------------------------------- */ + +-#ifdef SLO_IO +- int jif; +-#endif +- + /* module parameters: + */ + static int i2c_debug; + static int bit_test; /* see if the line-setting functions work */ +-static int bit_scan; /* have a look at what's hanging 'round */ + + /* --- setting states on the bus with the right timing: --------------- */ + +@@ -88,9 +72,6 @@ + { + setscl(adap,0); + udelay(adap->udelay); +-#ifdef SLO_IO +- SLO_IO +-#endif + } + + /* +@@ -99,33 +80,35 @@ + */ + static inline int sclhi(struct i2c_algo_bit_data *adap) + { +- int start=jiffies; ++ int start; + + setscl(adap,1); + +- udelay(adap->udelay); +- + /* Not all adapters have scl sense line... */ +- if (adap->getscl == NULL ) ++ if (adap->getscl == NULL ) { ++ udelay(adap->udelay); + return 0; ++ } + +- while (! getscl(adap) ) { ++ start=jiffies; ++ while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ +- setscl(adap,1); + if (time_after_eq(jiffies, start+adap->timeout)) { + return -ETIMEDOUT; + } ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + if (current->need_resched) + schedule(); ++#else ++ cond_resched(); ++#endif + } + DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start)); +-#ifdef SLO_IO +- SLO_IO +-#endif ++ udelay(adap->udelay); + return 0; + } + +@@ -144,7 +127,7 @@ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); +- setscl(adap,1); ++ sclhi(adap); + udelay(adap->udelay); + + sdalo(adap); +@@ -178,7 +161,6 @@ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ +- DEB2(printk(KERN_DEBUG " i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); +@@ -186,6 +168,7 @@ + DEBPROTO(printk(KERN_DEBUG "%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ ++ DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at bit #%d\n", c&0xff, i)); + return -ETIMEDOUT; + }; + /* do arbitration here: +@@ -196,11 +179,12 @@ + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ ++ DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at ack\n", c&0xff)); + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ +- DEB2(printk(KERN_DEBUG " i2c_outb: getsda() = 0x%2.2x\n", ~ack )); ++ DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x , getsda() = %d\n", c & 0xff, ack)); + + DEBPROTO( printk(KERN_DEBUG "[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(KERN_DEBUG " A ");} else printk(KERN_DEBUG " NA ") ); +@@ -219,11 +203,10 @@ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ +- DEB2(printk(KERN_DEBUG "i2c_inb.\n")); +- + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ ++ DEB2(printk(KERN_DEBUG " i2c_inb: timeout at bit #%d\n", 7-i)); + return -ETIMEDOUT; + }; + indata *= 2; +@@ -232,6 +215,8 @@ + scllo(adap); + } + /* assert: scl is low */ ++ DEB2(printk(KERN_DEBUG "i2c_inb: 0x%02x\n", indata & 0xff)); ++ + DEBPROTO(printk(KERN_DEBUG " 0x%02x", indata & 0xff)); + return (int) (indata & 0xff); + } +@@ -242,71 +227,75 @@ + */ + static int test_bus(struct i2c_algo_bit_data *adap, char* name) { + int scl,sda; ++ ++ if (adap->getscl==NULL) ++ printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, " ++ "SCL is not readable.\n"); ++ + sda=getsda(adap); +- if (adap->getscl==NULL) { +- printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n"); +- return 0; +- } +- scl=getscl(adap); +- printk("i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n", +- name,getscl(adap),getsda(adap)); ++ scl=(adap->getscl==NULL?1:getscl(adap)); ++ printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda); + if (!scl || !sda ) { +- printk("i2c-algo-bit.o: %s seems to be busy.\n",name); ++ printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name); + goto bailout; + } ++ + sdalo(adap); +- printk("i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap), +- getsda(adap)); +- if ( 0 != getsda(adap) ) { +- printk("i2c-algo-bit.o: %s SDA stuck high!\n",name); +- sdahi(adap); ++ sda=getsda(adap); ++ scl=(adap->getscl==NULL?1:getscl(adap)); ++ printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda); ++ if ( 0 != sda ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n"); + goto bailout; + } +- if ( 0 == getscl(adap) ) { +- printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n", +- name); ++ if ( 0 == scl ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low " ++ "while pulling SDA low!\n"); + goto bailout; + } ++ + sdahi(adap); +- printk("i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap), +- getsda(adap)); +- if ( 0 == getsda(adap) ) { +- printk("i2c-algo-bit.o: %s SDA stuck low!\n",name); +- sdahi(adap); ++ sda=getsda(adap); ++ scl=(adap->getscl==NULL?1:getscl(adap)); ++ printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda); ++ if ( 0 == sda ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n"); + goto bailout; + } +- if ( 0 == getscl(adap) ) { +- printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n", +- name); +- goto bailout; ++ if ( 0 == scl ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low " ++ "while pulling SDA high!\n"); ++ goto bailout; + } ++ + scllo(adap); +- printk("i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap), +- getsda(adap)); +- if ( 0 != getscl(adap) ) { +- printk("i2c-algo-bit.o: %s SCL stuck high!\n",name); +- sclhi(adap); ++ sda=getsda(adap); ++ scl=(adap->getscl==NULL?0:getscl(adap)); ++ printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda); ++ if ( 0 != scl ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n"); + goto bailout; + } +- if ( 0 == getsda(adap) ) { +- printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n", +- name); ++ if ( 0 == sda ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low " ++ "while pulling SCL low!\n"); + goto bailout; + } ++ + sclhi(adap); +- printk("i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap), +- getsda(adap)); +- if ( 0 == getscl(adap) ) { +- printk("i2c-algo-bit.o: %s SCL stuck low!\n",name); +- sclhi(adap); ++ sda=getsda(adap); ++ scl=(adap->getscl==NULL?1:getscl(adap)); ++ printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda); ++ if ( 0 == scl ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n"); + goto bailout; + } +- if ( 0 == getsda(adap) ) { +- printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n", +- name); ++ if ( 0 == sda ) { ++ printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low " ++ "while pulling SCL high!\n"); + goto bailout; + } +- printk("i2c-algo-bit.o: %s passed test.\n",name); ++ printk(KERN_INFO "i2c-algo-bit.o: %s passed test.\n",name); + return 0; + bailout: + sdahi(adap); +@@ -340,16 +329,21 @@ + i2c_start(adap); + udelay(adap->udelay); + } +- DEB2(if (i) printk(KERN_DEBUG "i2c-algo-bit.o: needed %d retries for %d\n", +- i,addr)); ++ DEB2(if (i) ++ printk(KERN_DEBUG "i2c-algo-bit.o: Used %d tries to %s client at 0x%02x : %s\n", ++ i+1, addr & 1 ? "read" : "write", addr>>1, ++ ret==1 ? "success" : ret==0 ? "no ack" : "failed, timeout?" ) ++ ); + return ret; + } + +-static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) ++static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) + { + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; +- const char *temp = buf; ++ const char *temp = msg->buf; ++ int count = msg->len; ++ unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + int retval; + int wrcount=0; + +@@ -358,7 +352,7 @@ + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: %s sendbytes: writing %2.2X\n", + i2c_adap->name, c&0xff)); + retval = i2c_outb(i2c_adap,c); +- if (retval>0) { ++ if ((retval>0) || (nak_ok && (retval==0))) { /* ok or ignored NAK */ + count--; + temp++; + wrcount++; +@@ -377,12 +371,18 @@ + return wrcount; + } + +-static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count) ++static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) + { +- char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; ++ char *temp = msg->buf; ++ int count = msg->len; ++ int recv_len = 0; ++ ++ /* Receive [Count] for I2C_SMBUS_BLOCK_DATA or I2C_SMBUS_BLOCK_PROC_CALL protocol */ ++ if (msg->flags & I2C_M_RECV_LEN) ++ recv_len = 1; + + while (count > 0) { + inval = i2c_inb(i2c_adap); +@@ -395,6 +395,20 @@ + break; + } + ++ if (recv_len) { ++ recv_len = 0; ++ /* [Count] should be between 1 and 31 (I2C_SMBUS_BLOCK_MAX - 1). */ ++ if (inval > 0 && inval < I2C_SMBUS_BLOCK_MAX) { ++ count = inval + 1; /* plus one for [Count] itself */ ++ msg->len = count; ++ if (msg->flags & I2C_M_RECV_PEC) ++ count++; /* plus one for PEC */ ++ } else { ++ printk(KERN_ERR "i2c-algo-bit.o: readbytes: bad block count (%d).\n", inval); ++ break; ++ } ++ } ++ + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); +@@ -419,31 +433,34 @@ + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: +- * 0 everything went okay, the chip ack'ed ++ * 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set + * -x an error occurred (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +-static inline int bit_doAddress(struct i2c_adapter *i2c_adap, +- struct i2c_msg *msg, int retries) ++static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) + { + unsigned short flags = msg->flags; ++ unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; +- int ret; ++ int ret, retries; ++ ++ retries = nak_ok ? 0 : i2c_adap->retries; ++ + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk(KERN_DEBUG "addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); +- if (ret!=1) { ++ if ((ret != 1) && !nak_ok) { + printk(KERN_ERR "died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); +- if (ret != 1) { ++ if ((ret != 1) && !nak_ok) { + /* the chip did not ack / xmission error occurred */ + printk(KERN_ERR "died at 2nd address code.\n"); + return -EREMOTEIO; +@@ -453,7 +470,7 @@ + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); +- if (ret!=1) { ++ if ((ret!=1) && !nak_ok) { + printk(KERN_ERR "died at extended address code.\n"); + return -EREMOTEIO; + } +@@ -465,10 +482,10 @@ + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); +- if (ret!=1) { ++ if ((ret!=1) && !nak_ok) + return -EREMOTEIO; +- } + } ++ + return 0; + } + +@@ -479,16 +496,18 @@ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + int i,ret; ++ unsigned short nak_ok; + + i2c_start(adap); + for (i=0;iflags & I2C_M_IGNORE_NAK; + if (!(pmsg->flags & I2C_M_NOSTART)) { + if (i) { + i2c_repstart(adap); + } +- ret = bit_doAddress(i2c_adap,pmsg,i2c_adap->retries); +- if (ret != 0) { ++ ret = bit_doAddress(i2c_adap, pmsg); ++ if ((ret != 0) && !nak_ok) { + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.2x msg #%d\n", + msgs[i].addr,i)); + return (ret<0) ? ret : -EREMOTEIO; +@@ -496,14 +515,14 @@ + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ +- ret = readbytes(i2c_adap,pmsg->buf,pmsg->len); ++ ret = readbytes(i2c_adap, pmsg); + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: read %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ +- ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len); ++ ret = sendbytes(i2c_adap, pmsg); + DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; +@@ -514,30 +533,25 @@ + return num; + } + +-static int algo_control(struct i2c_adapter *adapter, +- unsigned int cmd, unsigned long arg) +-{ +- return 0; +-} +- +-static u32 bit_func(struct i2c_adapter *adap) ++static u32 bit_func(struct i2c_adapter *i2c_adap) + { + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | +- I2C_FUNC_PROTOCOL_MANGLING; ++ I2C_FUNC_PROTOCOL_MANGLING | ++ I2C_FUNC_SMBUS_BLOCK_PROC_CALL | ++ I2C_FUNC_SMBUS_READ_BLOCK_DATA | ++ I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC | ++ I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC; + } + + + /* -----exported algorithm data: ------------------------------------- */ + + static struct i2c_algorithm i2c_bit_algo = { +- "Bit-shift algorithm", +- I2C_ALGO_BIT, +- bit_xfer, +- NULL, +- NULL, /* slave_xmit */ +- NULL, /* slave_recv */ +- algo_control, /* ioctl */ +- bit_func, /* functionality */ ++ .owner = THIS_MODULE, ++ .name = "Bit-shift algorithm", ++ .id = I2C_ALGO_BIT, ++ .master_xfer = bit_xfer, ++ .functionality = bit_func, + }; + + /* +@@ -545,7 +559,6 @@ + */ + int i2c_bit_add_bus(struct i2c_adapter *adap) + { +- int i; + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + + if (bit_test) { +@@ -565,78 +578,26 @@ + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +- /* scan bus */ +- if (bit_scan) { +- int ack; +- printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n", +- adap->name); +- for (i = 0x00; i < 0xff; i+=2) { +- i2c_start(bit_adap); +- ack = i2c_outb(adap,i); +- i2c_stop(bit_adap); +- if (ack>0) { +- printk("(%02x)",i>>1); +- } else +- printk("."); +- } +- printk("\n"); +- } +- +-#ifdef MODULE +- MOD_INC_USE_COUNT; +-#endif + i2c_add_adapter(adap); +- + return 0; + } + + + int i2c_bit_del_bus(struct i2c_adapter *adap) + { +- int res; +- +- if ((res = i2c_del_adapter(adap)) < 0) +- return res; +- +- DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); +- +-#ifdef MODULE +- MOD_DEC_USE_COUNT; +-#endif +- return 0; +-} +- +-int __init i2c_algo_bit_init (void) +-{ +- printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n"); +- return 0; ++ return i2c_del_adapter(adap); + } + +- +- + EXPORT_SYMBOL(i2c_bit_add_bus); + EXPORT_SYMBOL(i2c_bit_del_bus); + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + MODULE_LICENSE("GPL"); + + MODULE_PARM(bit_test, "i"); +-MODULE_PARM(bit_scan, "i"); + MODULE_PARM(i2c_debug,"i"); + + MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +-MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); + MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); +- +-int init_module(void) +-{ +- return i2c_algo_bit_init(); +-} +- +-void cleanup_module(void) +-{ +-} +-#endif +--- linux-old/include/linux/i2c-algo-bit.h Sat Feb 5 06:47:38 2000 ++++ linux/include/linux/i2c-algo-bit.h Mon Dec 13 19:26:28 2004 +@@ -21,12 +21,10 @@ + /* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +-/* $Id: i2c-algo-bit.h,v 1.7 1999/12/21 23:45:58 frodo Exp $ */ ++/* $Id: i2c-algo-bit.h,v 1.11 2003/07/25 07:56:42 khali Exp $ */ + +-#ifndef I2C_ALGO_BIT_H +-#define I2C_ALGO_BIT_H 1 +- +-#include ++#ifndef _LINUX_I2C_ALGO_BIT_H ++#define _LINUX_I2C_ALGO_BIT_H + + /* --- Defines for bit-adapters --------------------------------------- */ + /* +@@ -42,9 +40,10 @@ + int (*getscl) (void *data); + + /* local settings */ +- int udelay; +- int mdelay; +- int timeout; ++ int udelay; /* half-clock-cycle time in microsecs */ ++ /* i.e. clock is (500 / udelay) KHz */ ++ int mdelay; /* in millisecs, unused */ ++ int timeout; /* in jiffies */ + }; + + #define I2C_BIT_ADAP_MAX 16 +@@ -52,4 +51,4 @@ + int i2c_bit_add_bus(struct i2c_adapter *); + int i2c_bit_del_bus(struct i2c_adapter *); + +-#endif /* I2C_ALGO_BIT_H */ ++#endif /* _LINUX_I2C_ALGO_BIT_H */ +--- linux-old/drivers/i2c/i2c-algo-ibm_ocp.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-algo-ibm_ocp.c Mon Dec 13 19:26:29 2004 +@@ -0,0 +1,901 @@ ++/* ++ ------------------------------------------------------------------------- ++ i2c-algo-ibm_ocp.c i2c driver algorithms for IBM PPC 405 adapters ++ ------------------------------------------------------------------------- ++ ++ Ian DaSilva, MontaVista Software, Inc. ++ idasilva@mvista.com or source@mvista.com ++ ++ Copyright 2000 MontaVista Software Inc. ++ ++ Changes made to support the IIC peripheral on the IBM PPC 405 ++ ++ ++ --------------------------------------------------------------------------- ++ This file was highly leveraged from i2c-algo-pcf.c, which was created ++ by Simon G. Vogl and Hans Berglund: ++ ++ ++ Copyright (C) 1995-1997 Simon G. Vogl ++ 1998-2000 Hans Berglund ++ ++ With some changes from Kyösti Mälkki and ++ Frodo Looijaard ,and also from Martin Bailey ++ ++ ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ --------------------------------------------------------------------------- ++ ++ History: 01/20/12 - Armin ++ akuster@mvista.com ++ ported up to 2.4.16+ ++ ++ Version 02/03/25 - Armin ++ converted to ocp format ++ removed commented out or #if 0 code ++ added Gérard Basler's fix to iic_combined_transaction() such that it ++ returns the number of successfully completed transfers . ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) if (i2c_debug>=1) x ++#define DEB2(x) if (i2c_debug>=2) x ++#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ ++#define DEBPROTO(x) if (i2c_debug>=9) x; ++ /* debug the protocol by showing transferred bits */ ++#define DEF_TIMEOUT 5 ++ ++ ++/* ----- global variables --------------------------------------------- */ ++ ++ ++/* module parameters: ++ */ ++static int i2c_debug=0; ++ ++/* --- setting states on the bus with the right timing: --------------- */ ++ ++#define iic_outb(adap, reg, val) adap->setiic(adap->data, (int) &(reg), val) ++#define iic_inb(adap, reg) adap->getiic(adap->data, (int) &(reg)) ++ ++#define IICO_I2C_SDAHIGH 0x0780 ++#define IICO_I2C_SDALOW 0x0781 ++#define IICO_I2C_SCLHIGH 0x0782 ++#define IICO_I2C_SCLLOW 0x0783 ++#define IICO_I2C_LINEREAD 0x0784 ++ ++#define IIC_SINGLE_XFER 0 ++#define IIC_COMBINED_XFER 1 ++ ++#define IIC_ERR_LOST_ARB -2 ++#define IIC_ERR_INCOMPLETE_XFR -3 ++#define IIC_ERR_NACK -1 ++ ++/* --- other auxiliary functions -------------------------------------- */ ++ ++ ++// ++// Description: Puts this process to sleep for a period equal to timeout ++// ++static inline void iic_sleep(unsigned long timeout) ++{ ++ schedule_timeout( timeout * HZ); ++} ++ ++ ++// ++// Description: This performs the IBM PPC 405 IIC initialization sequence ++// as described in the PPC405GP data book. ++// ++static int iic_init (struct i2c_algo_iic_data *adap) ++{ ++ struct iic_regs *iic; ++ struct iic_ibm *adap_priv_data = adap->data; ++ unsigned short retval; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ /* Clear master low master address */ ++ iic_outb(adap,iic->lmadr, 0); ++ ++ /* Clear high master address */ ++ iic_outb(adap,iic->hmadr, 0); ++ ++ /* Clear low slave address */ ++ iic_outb(adap,iic->lsadr, 0); ++ ++ /* Clear high slave address */ ++ iic_outb(adap,iic->hsadr, 0); ++ ++ /* Clear status */ ++ iic_outb(adap,iic->sts, 0x0a); ++ ++ /* Clear extended status */ ++ iic_outb(adap,iic->extsts, 0x8f); ++ ++ /* Set clock division */ ++ iic_outb(adap,iic->clkdiv, 0x04); ++ ++ retval = iic_inb(adap, iic->clkdiv); ++ DEB(printk("iic_init: CLKDIV register = %x\n", retval)); ++ ++ /* Enable interrupts on Requested Master Transfer Complete */ ++ iic_outb(adap,iic->intmsk, 0x01); ++ ++ /* Clear transfer count */ ++ iic_outb(adap,iic->xfrcnt, 0x0); ++ ++ /* Clear extended control and status */ ++ iic_outb(adap,iic->xtcntlss, 0xf0); ++ ++ /* Set mode control (flush master data buf, enable hold SCL, exit */ ++ /* unknown state. */ ++ iic_outb(adap,iic->mdcntl, 0x47); ++ ++ /* Clear control register */ ++ iic_outb(adap,iic->cntl, 0x0); ++ ++ DEB2(printk(KERN_DEBUG "iic_init: Initialized IIC on PPC 405\n")); ++ return 0; ++} ++ ++ ++// ++// Description: After we issue a transaction on the IIC bus, this function ++// is called. It puts this process to sleep until we get an interrupt from ++// from the controller telling us that the transaction we requested in complete. ++// ++static int wait_for_pin(struct i2c_algo_iic_data *adap, int *status) ++{ ++ ++ int timeout = DEF_TIMEOUT; ++ int retval; ++ struct iic_regs *iic; ++ struct iic_ibm *adap_priv_data = adap->data; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ ++ *status = iic_inb(adap, iic->sts); ++#ifndef STUB_I2C ++ ++ while (timeout-- && (*status & 0x01)) { ++ adap->waitforpin(adap->data); ++ *status = iic_inb(adap, iic->sts); ++ } ++#endif ++ if (timeout <= 0) { ++ /* Issue stop signal on the bus, and force an interrupt */ ++ retval = iic_inb(adap, iic->cntl); ++ iic_outb(adap, iic->cntl, retval | 0x80); ++ /* Clear status register */ ++ iic_outb(adap, iic->sts, 0x0a); ++ /* Exit unknown bus state */ ++ retval = iic_inb(adap, iic->mdcntl); ++ iic_outb(adap, iic->mdcntl, (retval | 0x02)); ++ ++ // Check the status of the controller. Does it still see a ++ // pending transfer, even though we've tried to stop any ++ // ongoing transaction? ++ retval = iic_inb(adap, iic->sts); ++ retval = retval & 0x01; ++ if(retval) { ++ // The iic controller is hosed. It is not responding to any ++ // of our commands. We have already tried to force it into ++ // a known state, but it has not worked. Our only choice now ++ // is a soft reset, which will clear all registers, and force ++ // us to re-initialize the controller. ++ /* Soft reset */ ++ iic_outb(adap, iic->xtcntlss, 0x01); ++ udelay(500); ++ iic_init(adap); ++ /* Is the pending transfer bit in the sts reg finally cleared? */ ++ retval = iic_inb(adap, iic->sts); ++ retval = retval & 0x01; ++ if(retval) { ++ printk(KERN_CRIT "The IIC Controller is hosed. A processor reset is required\n"); ++ } ++ // For some reason, even though the interrupt bit in this ++ // register was set during iic_init, it didn't take. We ++ // need to set it again. Don't ask me why....this is just what ++ // I saw when testing timeouts. ++ iic_outb(adap, iic->intmsk, 0x01); ++ } ++ return(-1); ++ } ++ else ++ return(0); ++} ++ ++ ++//------------------------------------ ++// Utility functions ++// ++ ++ ++// ++// Description: Look at the status register to see if there was an error ++// in the requested transaction. If there is, look at the extended status ++// register and determine the exact cause. ++// ++int analyze_status(struct i2c_algo_iic_data *adap, int *error_code) ++{ ++ int ret; ++ struct iic_regs *iic; ++ struct iic_ibm *adap_priv_data = adap->data; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ ++ ret = iic_inb(adap, iic->sts); ++ if(ret & 0x04) { ++ // Error occurred ++ ret = iic_inb(adap, iic->extsts); ++ if(ret & 0x04) { ++ // Lost arbitration ++ *error_code = IIC_ERR_LOST_ARB; ++ } ++ if(ret & 0x02) { ++ // Incomplete transfer ++ *error_code = IIC_ERR_INCOMPLETE_XFR; ++ } ++ if(ret & 0x01) { ++ // Master transfer aborted by a NACK during the transfer of the ++ // address byte ++ *error_code = IIC_ERR_NACK; ++ } ++ return -1; ++ } ++ return 0; ++} ++ ++ ++// ++// Description: This function is called by the upper layers to do the ++// grunt work for a master send transaction ++// ++static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, ++ int count, int xfer_flag) ++{ ++ struct iic_regs *iic; ++ struct i2c_algo_iic_data *adap = i2c_adap->algo_data; ++ struct iic_ibm *adap_priv_data = adap->data; ++ int wrcount, status, timeout; ++ int loops, remainder, i, j; ++ int ret, error_code; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ ++ if( count == 0 ) return 0; ++ wrcount = 0; ++ loops = count / 4; ++ remainder = count % 4; ++ ++ if((loops > 1) && (remainder == 0)) { ++ for(i=0; i<(loops-1); i++) { ++ // ++ // Write four bytes to master data buffer ++ // ++ for(j=0; j<4; j++) { ++ iic_outb(adap, iic->mdbuf, ++ buf[wrcount++]); ++ } ++ // ++ // Issue command to IICO device to begin transmission ++ // ++ iic_outb(adap, iic->cntl, 0x35); ++ // ++ // Wait for transmission to complete. When it does, ++ //loop to the top of the for statement and write the ++ // next four bytes. ++ // ++ timeout = wait_for_pin(adap, &status); ++ if(timeout < 0) { ++ // ++ // Error handling ++ // ++ //printk(KERN_ERR "Error: write timeout\n"); ++ return wrcount; ++ } ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) { ++ // Return the number of bytes transferred ++ ret = iic_inb(adap, iic->xfrcnt); ++ ret = ret & 0x07; ++ return (wrcount-4+ret); ++ } ++ else return error_code; ++ } ++ } ++ } ++ else if((loops >= 1) && (remainder > 0)){ ++ //printk(KERN_DEBUG "iic_sendbytes: (loops >= 1)\n"); ++ for(i=0; imdbuf, ++ buf[wrcount++]); ++ } ++ // ++ // Issue command to IICO device to begin transmission ++ // ++ iic_outb(adap, iic->cntl, 0x35); ++ // ++ // Wait for transmission to complete. When it does, ++ //loop to the top of the for statement and write the ++ // next four bytes. ++ // ++ timeout = wait_for_pin(adap, &status); ++ if(timeout < 0) { ++ // ++ // Error handling ++ // ++ //printk(KERN_ERR "Error: write timeout\n"); ++ return wrcount; ++ } ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) { ++ // Return the number of bytes transferred ++ ret = iic_inb(adap, iic->xfrcnt); ++ ret = ret & 0x07; ++ return (wrcount-4+ret); ++ } ++ else return error_code; ++ } ++ } ++ } ++ ++ //printk(KERN_DEBUG "iic_sendbytes: expedite write\n"); ++ if(remainder == 0) remainder = 4; ++ // remainder = remainder - 1; ++ // ++ // Write the remaining bytes (less than or equal to 4) ++ // ++ for(i=0; imdbuf, buf[wrcount++]); ++ //printk(KERN_DEBUG "iic_sendbytes: data transferred = %x, wrcount = %d\n", buf[wrcount-1], (wrcount-1)); ++ } ++ //printk(KERN_DEBUG "iic_sendbytes: Issuing write\n"); ++ ++ if(xfer_flag == IIC_COMBINED_XFER) { ++ iic_outb(adap, iic->cntl, (0x09 | ((remainder-1) << 4))); ++ } ++ else { ++ iic_outb(adap, iic->cntl, (0x01 | ((remainder-1) << 4))); ++ } ++ DEB2(printk(KERN_DEBUG "iic_sendbytes: Waiting for interrupt\n")); ++ timeout = wait_for_pin(adap, &status); ++ if(timeout < 0) { ++ // ++ // Error handling ++ // ++ //printk(KERN_ERR "Error: write timeout\n"); ++ return wrcount; ++ } ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) { ++ // Return the number of bytes transferred ++ ret = iic_inb(adap, iic->xfrcnt); ++ ret = ret & 0x07; ++ return (wrcount-4+ret); ++ } ++ else return error_code; ++ } ++ DEB2(printk(KERN_DEBUG "iic_sendbytes: Got interrupt\n")); ++ return wrcount; ++} ++ ++ ++// ++// Description: Called by the upper layers to do the grunt work for ++// a master read transaction. ++// ++static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, int xfer_type) ++{ ++ struct iic_regs *iic; ++ int rdcount=0, i, status, timeout; ++ struct i2c_algo_iic_data *adap = i2c_adap->algo_data; ++ struct iic_ibm *adap_priv_data = adap->data; ++ int loops, remainder, j; ++ int ret, error_code; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ if(count == 0) return 0; ++ loops = count / 4; ++ remainder = count % 4; ++ ++ //printk(KERN_DEBUG "iic_readbytes: loops = %d, remainder = %d\n", loops, remainder); ++ ++ if((loops > 1) && (remainder == 0)) { ++ //printk(KERN_DEBUG "iic_readbytes: (loops > 1) && (remainder == 0)\n"); ++ for(i=0; i<(loops-1); i++) { ++ // ++ // Issue command to begin master read (4 bytes maximum) ++ // ++ //printk(KERN_DEBUG "--->Issued read command\n"); ++ iic_outb(adap, iic->cntl, 0x37); ++ // ++ // Wait for transmission to complete. When it does, ++ // loop to the top of the for statement and write the ++ // next four bytes. ++ // ++ //printk(KERN_DEBUG "--->Waiting for interrupt\n"); ++ timeout = wait_for_pin(adap, &status); ++ if(timeout < 0) { ++ // Error Handler ++ //printk(KERN_ERR "Error: read timed out\n"); ++ return rdcount; ++ } ++ //printk(KERN_DEBUG "--->Got interrupt\n"); ++ ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) ++ return rdcount; ++ else ++ return error_code; ++ } ++ ++ for(j=0; j<4; j++) { ++ // Wait for data to shuffle to top of data buffer ++ // This value needs to optimized. ++ udelay(1); ++ buf[rdcount] = iic_inb(adap, iic->mdbuf); ++ rdcount++; ++ //printk(KERN_DEBUG "--->Read one byte\n"); ++ } ++ } ++ } ++ ++ else if((loops >= 1) && (remainder > 0)){ ++ //printk(KERN_DEBUG "iic_readbytes: (loops >=1) && (remainder > 0)\n"); ++ for(i=0; iIssued read command\n"); ++ iic_outb(adap, iic->cntl, 0x37); ++ // ++ // Wait for transmission to complete. When it does, ++ // loop to the top of the for statement and write the ++ // next four bytes. ++ // ++ //printk(KERN_DEBUG "--->Waiting for interrupt\n"); ++ timeout = wait_for_pin(adap, &status); ++ if(timeout < 0) { ++ // Error Handler ++ //printk(KERN_ERR "Error: read timed out\n"); ++ return rdcount; ++ } ++ //printk(KERN_DEBUG "--->Got interrupt\n"); ++ ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) ++ return rdcount; ++ else ++ return error_code; ++ } ++ ++ for(j=0; j<4; j++) { ++ // Wait for data to shuffle to top of data buffer ++ // This value needs to optimized. ++ udelay(1); ++ buf[rdcount] = iic_inb(adap, iic->mdbuf); ++ rdcount++; ++ //printk(KERN_DEBUG "--->Read one byte\n"); ++ } ++ } ++ } ++ ++ //printk(KERN_DEBUG "iic_readbytes: expedite read\n"); ++ if(remainder == 0) remainder = 4; ++ DEB2(printk(KERN_DEBUG "iic_readbytes: writing %x to IICO_CNTL\n", (0x03 | ((remainder-1) << 4)))); ++ ++ if(xfer_type == IIC_COMBINED_XFER) { ++ iic_outb(adap, iic->cntl, (0x0b | ((remainder-1) << 4))); ++ } ++ else { ++ iic_outb(adap, iic->cntl, (0x03 | ((remainder-1) << 4))); ++ } ++ DEB2(printk(KERN_DEBUG "iic_readbytes: Wait for pin\n")); ++ timeout = wait_for_pin(adap, &status); ++ DEB2(printk(KERN_DEBUG "iic_readbytes: Got the interrupt\n")); ++ if(timeout < 0) { ++ // Error Handler ++ //printk(KERN_ERR "Error: read timed out\n"); ++ return rdcount; ++ } ++ ++ ret = analyze_status(adap, &error_code); ++ if(ret < 0) { ++ if(error_code == IIC_ERR_INCOMPLETE_XFR) ++ return rdcount; ++ else ++ return error_code; ++ } ++ ++ //printk(KERN_DEBUG "iic_readbyte: Begin reading data buffer\n"); ++ for(i=0; imdbuf); ++ // printk(KERN_DEBUG "iic_readbytes: Character read = %x\n", buf[rdcount]); ++ rdcount++; ++ } ++ ++ return rdcount; ++} ++ ++ ++// ++// Description: This function implements combined transactions. Combined ++// transactions consist of combinations of reading and writing blocks of data. ++// Each transfer (i.e. a read or a write) is separated by a repeated start ++// condition. ++// ++static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) ++{ ++ int i; ++ struct i2c_msg *pmsg; ++ int ret; ++ ++ DEB2(printk(KERN_DEBUG "Beginning combined transaction\n")); ++ for(i=0; i < num; i++) { ++ pmsg = &msgs[i]; ++ if(pmsg->flags & I2C_M_RD) { ++ ++ // Last read or write segment needs to be terminated with a stop ++ if(i < num-1) { ++ DEB2(printk(KERN_DEBUG "This one is a read\n")); ++ } ++ else { ++ DEB2(printk(KERN_DEBUG "Doing the last read\n")); ++ } ++ ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER); ++ ++ if (ret != pmsg->len) { ++ DEB2(printk("i2c-algo-ppc405.o: fail: " ++ "only read %d bytes.\n",ret)); ++ return i; ++ } ++ else { ++ DEB2(printk("i2c-algo-ppc405.o: read %d bytes.\n",ret)); ++ } ++ } ++ else if(!(pmsg->flags & I2C_M_RD)) { ++ ++ // Last read or write segment needs to be terminated with a stop ++ if(i < num-1) { ++ DEB2(printk(KERN_DEBUG "This one is a write\n")); ++ } ++ else { ++ DEB2(printk(KERN_DEBUG "Doing the last write\n")); ++ } ++ ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER); ++ ++ if (ret != pmsg->len) { ++ DEB2(printk("i2c-algo-ppc405.o: fail: " ++ "only wrote %d bytes.\n",ret)); ++ return i; ++ } ++ else { ++ DEB2(printk("i2c-algo-ppc405.o: wrote %d bytes.\n",ret)); ++ } ++ } ++ } ++ ++ return num; ++} ++ ++ ++// ++// Description: Whenever we initiate a transaction, the first byte clocked ++// onto the bus after the start condition is the address (7 bit) of the ++// device we want to talk to. This function manipulates the address specified ++// so that it makes sense to the hardware when written to the IIC peripheral. ++// ++// Note: 10 bit addresses are not supported in this driver, although they are ++// supported by the hardware. This functionality needs to be implemented. ++// ++static inline int iic_doAddress(struct i2c_algo_iic_data *adap, ++ struct i2c_msg *msg, int retries) ++{ ++ struct iic_regs *iic; ++ unsigned short flags = msg->flags; ++ unsigned char addr; ++ struct iic_ibm *adap_priv_data = adap->data; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++// ++// The following segment for 10 bit addresses needs to be ported ++// ++/* Ten bit addresses not supported right now ++ if ( (flags & I2C_M_TEN) ) { ++ // a ten bit address ++ addr = 0xf0 | (( msg->addr >> 7) & 0x03); ++ DEB2(printk(KERN_DEBUG "addr0: %d\n",addr)); ++ // try extended address code... ++ ret = try_address(adap, addr, retries); ++ if (ret!=1) { ++ printk(KERN_ERR "iic_doAddress: died at extended address code.\n"); ++ return -EREMOTEIO; ++ } ++ // the remaining 8 bit address ++ iic_outb(adap,msg->addr & 0x7f); ++ // Status check comes here ++ if (ret != 1) { ++ printk(KERN_ERR "iic_doAddress: died at 2nd address code.\n"); ++ return -EREMOTEIO; ++ } ++ if ( flags & I2C_M_RD ) { ++ i2c_repstart(adap); ++ // okay, now switch into reading mode ++ addr |= 0x01; ++ ret = try_address(adap, addr, retries); ++ if (ret!=1) { ++ printk(KERN_ERR "iic_doAddress: died at extended address code.\n"); ++ return -EREMOTEIO; ++ } ++ } ++ } else ----------> // normal 7 bit address ++ ++Ten bit addresses not supported yet */ ++ ++ addr = ( msg->addr << 1 ); ++ if (flags & I2C_M_RD ) ++ addr |= 1; ++ if (flags & I2C_M_REV_DIR_ADDR ) ++ addr ^= 1; ++ // ++ // Write to the low slave address ++ // ++ iic_outb(adap, iic->lmadr, addr); ++ // ++ // Write zero to the high slave register since we are ++ // only using 7 bit addresses ++ // ++ iic_outb(adap, iic->hmadr, 0); ++ ++ return 0; ++} ++ ++ ++// ++// Description: Prepares the controller for a transaction (clearing status ++// registers, data buffers, etc), and then calls either iic_readbytes or ++// iic_sendbytes to do the actual transaction. ++// ++static int iic_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msgs[], ++ int num) ++{ ++ struct iic_regs *iic; ++ struct i2c_algo_iic_data *adap = i2c_adap->algo_data; ++ struct iic_ibm *adap_priv_data = adap->data; ++ struct i2c_msg *pmsg; ++ int i = 0; ++ int ret; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ pmsg = &msgs[i]; ++ ++ // ++ // Clear status register ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: iic_xfer: Clearing status register\n")); ++ iic_outb(adap, iic->sts, 0x0a); ++ ++ // ++ // Wait for any pending transfers to complete ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: Waiting for any pending transfers to complete\n")); ++ while((ret = iic_inb(adap, iic->sts)) == 0x01) { ++ ; ++ } ++ ++ // ++ // Flush master data buf ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: Clearing master data buffer\n")); ++ ret = iic_inb(adap, iic->mdcntl); ++ iic_outb(adap, iic->mdcntl, ret | 0x40); ++ ++ // ++ // Load slave address ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: Loading slave address\n")); ++ ret = iic_doAddress(adap, pmsg, i2c_adap->retries); ++ ++ // ++ // Check to see if the bus is busy ++ // ++ ret = iic_inb(adap, iic->extsts); ++ // Mask off the irrelevent bits ++ ret = ret & 0x70; ++ // When the bus is free, the BCS bits in the EXTSTS register are 0b100 ++ if(ret != 0x40) return IIC_ERR_LOST_ARB; ++ ++ // ++ // Combined transaction (read and write) ++ // ++ if(num > 1) { ++ DEB2(printk(KERN_DEBUG "iic_xfer: Call combined transaction\n")); ++ ret = iic_combined_transaction(i2c_adap, msgs, num); ++ } ++ // ++ // Read only ++ // ++ else if((num == 1) && (pmsg->flags & I2C_M_RD)) { ++ // ++ // Tell device to begin reading data from the master data ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's read\n")); ++ ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); ++ } ++ // ++ // Write only ++ // ++ else if((num == 1 ) && (!(pmsg->flags & I2C_M_RD))) { ++ // ++ // Write data to master data buffers and tell our device ++ // to begin transmitting ++ // ++ DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's write\n")); ++ ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); ++ } ++ ++ return ret; ++} ++ ++ ++// ++// Description: Implements device specific ioctls. Higher level ioctls can ++// be found in i2c-core.c and are typical of any i2c controller (specifying ++// slave address, timeouts, etc). These ioctls take advantage of any hardware ++// features built into the controller for which this algorithm-adapter set ++// was written. These ioctls allow you to take control of the data and clock ++// lines on the IBM PPC 405 IIC controller and set the either high or low, ++// similar to a GPIO pin. ++// ++static int algo_control(struct i2c_adapter *adapter, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct iic_regs *iic; ++ struct i2c_algo_iic_data *adap = adapter->algo_data; ++ struct iic_ibm *adap_priv_data = adap->data; ++ int ret=0; ++ int lines; ++ iic = (struct iic_regs *) adap_priv_data->iic_base; ++ ++ lines = iic_inb(adap, iic->directcntl); ++ ++ if (cmd == IICO_I2C_SDAHIGH) { ++ lines = lines & 0x01; ++ if( lines ) lines = 0x04; ++ else lines = 0; ++ iic_outb(adap, iic->directcntl,(0x08|lines)); ++ } ++ else if (cmd == IICO_I2C_SDALOW) { ++ lines = lines & 0x01; ++ if( lines ) lines = 0x04; ++ else lines = 0; ++ iic_outb(adap, iic->directcntl,(0x00|lines)); ++ } ++ else if (cmd == IICO_I2C_SCLHIGH) { ++ lines = lines & 0x02; ++ if( lines ) lines = 0x08; ++ else lines = 0; ++ iic_outb(adap, iic->directcntl,(0x04|lines)); ++ } ++ else if (cmd == IICO_I2C_SCLLOW) { ++ lines = lines & 0x02; ++ if( lines ) lines = 0x08; ++ else lines = 0; ++ iic_outb(adap, iic->directcntl,(0x00|lines)); ++ } ++ else if (cmd == IICO_I2C_LINEREAD) { ++ ret = lines; ++ } ++ return ret; ++} ++ ++ ++static u32 iic_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | ++ I2C_FUNC_PROTOCOL_MANGLING; ++} ++ ++ ++/* -----exported algorithm data: ------------------------------------- */ ++ ++static struct i2c_algorithm iic_algo = { ++ .owner = THIS_MODULE, ++ .name = "IBM on-chip IIC algorithm", ++ .id = I2C_ALGO_OCP, ++ .master_xfer = iic_xfer, ++ .algo_control = algo_control, ++ .functionality = iic_func, ++}; ++ ++/* ++ * registering functions to load algorithms at runtime ++ */ ++ ++ ++// ++// Description: Register bus structure ++// ++int i2c_ocp_add_bus(struct i2c_adapter *adap) ++{ ++ struct i2c_algo_iic_data *iic_adap = adap->algo_data; ++ ++ DEB2(printk(KERN_DEBUG "i2c-algo-iic.o: hw routines for %s registered.\n", ++ adap->name)); ++ ++ /* register new adapter to i2c module... */ ++ ++ adap->id |= iic_algo.id; ++ adap->algo = &iic_algo; ++ ++ adap->timeout = 100; /* default values, should */ ++ adap->retries = 3; /* be replaced by defines */ ++ ++ iic_init(iic_adap); ++ i2c_add_adapter(adap); ++ return 0; ++} ++ ++ ++// ++// Done ++// ++int i2c_ocp_del_bus(struct i2c_adapter *adap) ++{ ++ return i2c_del_adapter(adap); ++} ++ ++ ++EXPORT_SYMBOL(i2c_ocp_add_bus); ++EXPORT_SYMBOL(i2c_ocp_del_bus); ++ ++// ++// The MODULE_* macros resolve to nothing if MODULES is not defined ++// when this file is compiled. ++// ++MODULE_AUTHOR("MontaVista Software "); ++MODULE_DESCRIPTION("PPC 405 iic algorithm"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(i2c_debug,"i"); ++ ++MODULE_PARM_DESC(i2c_debug, ++ "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); ++ +--- linux-old/include/linux/i2c-algo-ibm_ocp.h Thu Jan 1 00:00:00 1970 ++++ linux/include/linux/i2c-algo-ibm_ocp.h Mon Dec 13 19:26:29 2004 +@@ -0,0 +1,52 @@ ++/* ------------------------------------------------------------------------- */ ++/* i2c-algo-ibm_ocp.h i2c driver algorithms for IBM PPC 405 IIC adapters */ ++/* ------------------------------------------------------------------------- */ ++/* Copyright (C) 1995-97 Simon G. Vogl ++ 1998-99 Hans Berglund ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* With some changes from Kyösti Mälkki and even ++ Frodo Looijaard */ ++ ++/* Modifications by MontaVista Software, August 2000 ++ Changes made to support the IIC peripheral on the IBM PPC 405 */ ++ ++#ifndef _LINUX_I2C_ALGO_IBM_OCP_H ++#define _LINUX_I2C_ALGO_IBM_OCP_H ++ ++struct i2c_algo_iic_data { ++ struct iic_regs *data; /* private data for lolevel routines */ ++ void (*setiic) (void *data, int ctl, int val); ++ int (*getiic) (void *data, int ctl); ++ int (*getown) (void *data); ++ int (*getclock) (void *data); ++ void (*waitforpin) (void *data); ++ ++ /* local settings */ ++ int udelay; ++ int mdelay; ++ int timeout; ++}; ++ ++ ++#define I2C_IIC_ADAP_MAX 16 ++ ++ ++int i2c_ocp_add_bus(struct i2c_adapter *); ++int i2c_ocp_del_bus(struct i2c_adapter *); ++ ++#endif /* _LINUX_I2C_ALGO_IBM_OCP_H */ +--- linux-old/drivers/i2c/i2c-algo-pcf.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-algo-pcf.c Mon Dec 13 19:26:29 2004 +@@ -32,14 +32,11 @@ + #include + #include + #include +-#include +-#include + #include + #include +- + #include + #include +-#include "i2c-pcf8584.h" ++ + + /* ----- global defines ----------------------------------------------- */ + #define DEB(x) if (i2c_debug>=1) x +@@ -52,7 +49,6 @@ + /* module parameters: + */ + static int i2c_debug=0; +-static int pcf_scan=0; /* have a look at what's hanging 'round */ + + /* --- setting states on the bus with the right timing: --------------- */ + +@@ -149,8 +145,7 @@ + set_pcf(adap, 1, I2C_PCF_PIN); + /* check to see S1 now used as R/W ctrl - + PCF8584 does that when ESO is zero */ +- /* PCF also resets PIN bit */ +- if ((temp = get_pcf(adap, 1)) != (0)) { ++ if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp)); + return -ENXIO; /* definetly not PCF8584 */ + } +@@ -166,7 +161,7 @@ + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + /* check to see S2 now selected */ +- if ((temp = get_pcf(adap, 1)) != I2C_PCF_ES1) { ++ if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp)); + return -ENXIO; + } +@@ -427,12 +422,6 @@ + return (i); + } + +-static int algo_control(struct i2c_adapter *adapter, +- unsigned int cmd, unsigned long arg) +-{ +- return 0; +-} +- + static u32 pcf_func(struct i2c_adapter *adap) + { + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | +@@ -442,14 +431,11 @@ + /* -----exported algorithm data: ------------------------------------- */ + + static struct i2c_algorithm pcf_algo = { +- "PCF8584 algorithm", +- I2C_ALGO_PCF, +- pcf_xfer, +- NULL, +- NULL, /* slave_xmit */ +- NULL, /* slave_recv */ +- algo_control, /* ioctl */ +- pcf_func, /* functionality */ ++ .owner = THIS_MODULE, ++ .name = "PCF8584 algorithm", ++ .id = I2C_ALGO_PCF, ++ .master_xfer = pcf_xfer, ++ .functionality = pcf_func, + }; + + /* +@@ -457,7 +443,7 @@ + */ + int i2c_pcf_add_bus(struct i2c_adapter *adap) + { +- int i, status; ++ int i; + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: hw routines for %s registered.\n", +@@ -475,81 +461,23 @@ + return i; + } + +-#ifdef MODULE +- MOD_INC_USE_COUNT; +-#endif +- + i2c_add_adapter(adap); +- +- /* scan bus */ +- if (pcf_scan) { +- printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", +- adap->name); +- for (i = 0x00; i < 0xff; i+=2) { +- if (wait_for_bb(pcf_adap)) { +- printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s - TIMEOUTed.\n", +- adap->name); +- break; +- } +- i2c_outb(pcf_adap, i); +- i2c_start(pcf_adap); +- if ((wait_for_pin(pcf_adap, &status) >= 0) && +- ((status & I2C_PCF_LRB) == 0)) { +- printk("(%02x)",i>>1); +- } else { +- printk("."); +- } +- i2c_stop(pcf_adap); +- udelay(pcf_adap->udelay); +- } +- printk("\n"); +- } + return 0; + } + + + int i2c_pcf_del_bus(struct i2c_adapter *adap) + { +- int res; +- if ((res = i2c_del_adapter(adap)) < 0) +- return res; +- DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); +- +-#ifdef MODULE +- MOD_DEC_USE_COUNT; +-#endif +- return 0; +-} +- +-int __init i2c_algo_pcf_init (void) +-{ +- printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); +- return 0; ++ return i2c_del_adapter(adap); + } + +- + EXPORT_SYMBOL(i2c_pcf_add_bus); + EXPORT_SYMBOL(i2c_pcf_del_bus); + +-#ifdef MODULE + MODULE_AUTHOR("Hans Berglund "); + MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + MODULE_LICENSE("GPL"); + +-MODULE_PARM(pcf_scan, "i"); + MODULE_PARM(i2c_debug,"i"); +- +-MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); + MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); +- +- +-int init_module(void) +-{ +- return i2c_algo_pcf_init(); +-} +- +-void cleanup_module(void) +-{ +-} +-#endif +--- linux-old/include/linux/i2c-algo-pcf.h Thu Mar 23 02:26:01 2000 ++++ linux/include/linux/i2c-algo-pcf.h Mon Dec 13 19:26:30 2004 +@@ -22,13 +22,12 @@ + /* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +-/* $Id: i2c-algo-pcf.h,v 1.7 2000/02/27 23:02:45 frodo Exp $ */ ++/* $Id: i2c-algo-pcf.h,v 1.9 2003/07/25 07:56:42 khali Exp $ */ + +-#ifndef I2C_ALGO_PCF_H +-#define I2C_ALGO_PCF_H 1 ++#ifndef _LINUX_I2C_ALGO_PCF_H ++#define _LINUX_I2C_ALGO_PCF_H + +-/* --- Defines for pcf-adapters --------------------------------------- */ +-#include ++#include + + struct i2c_algo_pcf_data { + void *data; /* private data for lolevel routines */ +@@ -49,4 +48,4 @@ + int i2c_pcf_add_bus(struct i2c_adapter *); + int i2c_pcf_del_bus(struct i2c_adapter *); + +-#endif /* I2C_ALGO_PCF_H */ ++#endif /* _LINUX_I2C_ALGO_PCF_H */ +--- linux-old/drivers/i2c/i2c-core.c Wed Jul 7 00:38:02 2004 ++++ linux/drivers/i2c/i2c-core.c Mon Dec 13 19:26:31 2004 +@@ -18,56 +18,33 @@ + /* ------------------------------------------------------------------------- */ + + /* With some changes from Kyösti Mälkki . +- All SMBus-related things are written by Frodo Looijaard */ ++ All SMBus-related things are written by Frodo Looijaard ++ SMBus 2.0 support by Mark Studebaker */ + +-/* $Id: i2c-core.c,v 1.64 2001/08/13 01:35:56 mds Exp $ */ ++/* i2c-core.c,v 1.91.2.2 2003/01/21 10:00:19 kmalkki Exp */ + + #include + #include + #include + #include + #include +-#include +- +-#include +- +-/* ----- compatibility stuff ----------------------------------------------- */ +- + #include +- ++#include + #include + + /* ----- global defines ---------------------------------------------------- */ + +-/* exclusive access to the bus */ +-#define I2C_LOCK(adap) down(&adap->lock) +-#define I2C_UNLOCK(adap) up(&adap->lock) +- +-#define ADAP_LOCK() down(&adap_lock) +-#define ADAP_UNLOCK() up(&adap_lock) +- +-#define DRV_LOCK() down(&driver_lock) +-#define DRV_UNLOCK() up(&driver_lock) +- + #define DEB(x) if (i2c_debug>=1) x; + #define DEB2(x) if (i2c_debug>=2) x; + + /* ----- global variables -------------------------------------------------- */ + +-/**** lock for writing to global variables: the adapter & driver list */ +-struct semaphore adap_lock; +-struct semaphore driver_lock; +- +-/**** adapter list */ ++DECLARE_MUTEX(core_lists); + static struct i2c_adapter *adapters[I2C_ADAP_MAX]; +-static int adap_count; +- +-/**** drivers list */ + static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +-static int driver_count; + + /**** debug level */ +-static int i2c_debug=1; ++static int i2c_debug; + + /* --------------------------------------------------- + * /proc entry declarations +@@ -75,10 +52,6 @@ + */ + + #ifdef CONFIG_PROC_FS +- +-static int i2cproc_init(void); +-static int i2cproc_cleanup(void); +- + static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); + static int read_bus_i2c(char *buf, char **start, off_t offset, int len, +@@ -87,15 +60,11 @@ + /* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ + static struct file_operations i2cproc_operations = { +- read: i2cproc_bus_read, ++ .read = i2cproc_bus_read, + }; + +-static int i2cproc_initialized = 0; +- +-#else /* undef CONFIG_PROC_FS */ +- +-#define i2cproc_init() 0 +-#define i2cproc_cleanup() 0 ++static int i2cproc_register(struct i2c_adapter *adap, int bus); ++static void i2cproc_remove(int bus); + + #endif /* CONFIG_PROC_FS */ + +@@ -112,9 +81,9 @@ + */ + int i2c_add_adapter(struct i2c_adapter *adap) + { +- int i,j,res; ++ int i,j,res = 0; + +- ADAP_LOCK(); ++ down(&core_lists); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; +@@ -125,68 +94,39 @@ + res = -ENOMEM; + goto ERROR0; + } ++ ++#ifdef CONFIG_PROC_FS ++ res = i2cproc_register(adap, i); ++ if (res<0) ++ goto ERROR0; ++#endif /* def CONFIG_PROC_FS */ + + adapters[i] = adap; +- adap_count++; +- ADAP_UNLOCK(); + + /* init data types */ +- init_MUTEX(&adap->lock); +- +-#ifdef CONFIG_PROC_FS +- +- if (i2cproc_initialized) { +- char name[8]; +- struct proc_dir_entry *proc_entry; +- +- sprintf(name,"i2c-%d", i); +- +- proc_entry = create_proc_entry(name,0,proc_bus); +- if (! proc_entry) { +- printk("i2c-core.o: Could not create /proc/bus/%s\n", +- name); +- res = -ENOENT; +- goto ERROR1; +- } +- +- proc_entry->proc_fops = &i2cproc_operations; +- proc_entry->owner = THIS_MODULE; +- adap->inode = proc_entry->low_ino; +- } +- +-#endif /* def CONFIG_PROC_FS */ ++ init_MUTEX(&adap->bus); ++ init_MUTEX(&adap->list); + + /* inform drivers of new adapters */ +- DRV_LOCK(); + for (j=0;jflags&(I2C_DF_NOTIFY|I2C_DF_DUMMY))) + /* We ignore the return code; if it fails, too bad */ + drivers[j]->attach_adapter(adap); +- DRV_UNLOCK(); + + DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n", + adap->name,i)); +- +- return 0; +- +- +-ERROR1: +- ADAP_LOCK(); +- adapters[i] = NULL; +- adap_count--; + ERROR0: +- ADAP_UNLOCK(); ++ up(&core_lists); + return res; + } + + + int i2c_del_adapter(struct i2c_adapter *adap) + { +- int i,j,res; +- +- ADAP_LOCK(); ++ int i,j,res = 0; + ++ down(&core_lists); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; +@@ -202,20 +142,17 @@ + * *detach* it! Of course, each dummy driver should know about + * this or hell will break loose... + */ +- DRV_LOCK(); + for (j = 0; j < I2C_DRIVER_MAX; j++) + if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY)) + if ((res = drivers[j]->attach_adapter(adap))) { + printk(KERN_WARNING "i2c-core.o: can't detach adapter %s " + "while detaching driver %s: driver not " + "detached!",adap->name,drivers[j]->name); +- goto ERROR1; ++ goto ERROR0; + } +- DRV_UNLOCK(); +- + + /* detach any active clients. This must be done first, because +- * it can fail; in which case we give upp. */ ++ * it can fail; in which case we give up. */ + for (j=0;jclients[j]; + if (client!=NULL) +@@ -231,26 +168,15 @@ + goto ERROR0; + } + } ++ + #ifdef CONFIG_PROC_FS +- if (i2cproc_initialized) { +- char name[8]; +- sprintf(name,"i2c-%d", i); +- remove_proc_entry(name,proc_bus); +- } ++ i2cproc_remove(i); + #endif /* def CONFIG_PROC_FS */ + + adapters[i] = NULL; +- adap_count--; +- +- ADAP_UNLOCK(); + DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); +- return 0; +- + ERROR0: +- ADAP_UNLOCK(); +- return res; +-ERROR1: +- DRV_UNLOCK(); ++ up(&core_lists); + return res; + } + +@@ -264,7 +190,8 @@ + int i2c_add_driver(struct i2c_driver *driver) + { + int i; +- DRV_LOCK(); ++ ++ down(&core_lists); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; +@@ -273,19 +200,12 @@ + " i2c-core.o: register_driver(%s) " + "- enlarge I2C_DRIVER_MAX.\n", + driver->name); +- DRV_UNLOCK(); ++ up(&core_lists); + return -ENOMEM; + } +- + drivers[i] = driver; +- driver_count++; +- +- DRV_UNLOCK(); /* driver was successfully added */ +- + DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); + +- ADAP_LOCK(); +- + /* now look for instances of driver on our adapters + */ + if (driver->flags& (I2C_DF_NOTIFY|I2C_DF_DUMMY)) { +@@ -294,15 +214,15 @@ + /* Ignore errors */ + driver->attach_adapter(adapters[i]); + } +- ADAP_UNLOCK(); ++ up(&core_lists); + return 0; + } + + int i2c_del_driver(struct i2c_driver *driver) + { +- int i,j,k,res; ++ int i,j,k,res = 0; + +- DRV_LOCK(); ++ down(&core_lists); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; +@@ -310,7 +230,7 @@ + printk(KERN_WARNING " i2c-core.o: unregister_driver: " + "[%s] not found\n", + driver->name); +- DRV_UNLOCK(); ++ up(&core_lists); + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still +@@ -322,7 +242,6 @@ + * invalid operation might (will!) result, when using stale client + * pointers. + */ +- ADAP_LOCK(); /* should be moved inside the if statement... */ + for (k=0;kname, + adap->name); +- ADAP_UNLOCK(); +- return res; ++ goto ERROR0; + } + } else { + for (j=0;jname, + client->addr, + adap->name); +- ADAP_UNLOCK(); +- return res; ++ goto ERROR0; + } + } + } + } + } +- ADAP_UNLOCK(); + drivers[i] = NULL; +- driver_count--; +- DRV_UNLOCK(); +- + DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name)); +- return 0; ++ ++ERROR0: ++ up(&core_lists); ++ return res; + } + +-int i2c_check_addr (struct i2c_adapter *adapter, int addr) ++static int __i2c_check_addr (struct i2c_adapter *adapter, int addr) + { + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; ++ + return 0; + } + ++int i2c_check_addr (struct i2c_adapter *adapter, int addr) ++{ ++ int rval; ++ ++ down(&adapter->list); ++ rval = __i2c_check_addr(adapter, addr); ++ up(&adapter->list); ++ ++ return rval; ++} ++ + int i2c_attach_client(struct i2c_client *client) + { + struct i2c_adapter *adapter = client->adapter; +@@ -398,6 +326,7 @@ + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; + ++ down(&adapter->list); + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; +@@ -405,11 +334,11 @@ + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); ++ up(&adapter->list); + return -ENOMEM; + } +- + adapter->clients[i] = client; +- adapter->client_count++; ++ up(&adapter->list); + + if (adapter->client_register) + if (adapter->client_register(client)) +@@ -431,16 +360,6 @@ + struct i2c_adapter *adapter = client->adapter; + int i,res; + +- for (i = 0; i < I2C_CLIENT_MAX; i++) +- if (client == adapter->clients[i]) +- break; +- if (I2C_CLIENT_MAX == i) { +- printk(KERN_WARNING " i2c-core.o: unregister_client " +- "[%s] not found\n", +- client->name); +- return -ENODEV; +- } +- + if( (client->flags & I2C_CLIENT_ALLOW_USE) && + (client->usage_count>0)) + return -EBUSY; +@@ -452,33 +371,41 @@ + return res; + } + ++ down(&adapter->list); ++ for (i = 0; i < I2C_CLIENT_MAX; i++) ++ if (client == adapter->clients[i]) ++ break; ++ if (I2C_CLIENT_MAX == i) { ++ printk(KERN_WARNING " i2c-core.o: unregister_client " ++ "[%s] not found\n", ++ client->name); ++ up(&adapter->list); ++ return -ENODEV; ++ } + adapter->clients[i] = NULL; +- adapter->client_count--; ++ up(&adapter->list); + + DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] unregistered.\n",client->name)); + return 0; + } + +-void i2c_inc_use_client(struct i2c_client *client) ++static void i2c_inc_use_client(struct i2c_client *client) + { +- +- if (client->driver->inc_use != NULL) +- client->driver->inc_use(client); +- +- if (client->adapter->inc_use != NULL) +- client->adapter->inc_use(client->adapter); ++ if(client->driver->owner) ++ __MOD_INC_USE_COUNT(client->driver->owner); ++ if(client->adapter->owner) ++ __MOD_INC_USE_COUNT(client->adapter->owner); + } + +-void i2c_dec_use_client(struct i2c_client *client) ++static void i2c_dec_use_client(struct i2c_client *client) + { +- +- if (client->driver->dec_use != NULL) +- client->driver->dec_use(client); +- +- if (client->adapter->dec_use != NULL) +- client->adapter->dec_use(client->adapter); ++ if(client->driver->owner) ++ __MOD_DEC_USE_COUNT(client->driver->owner); ++ if(client->adapter->owner) ++ __MOD_DEC_USE_COUNT(client->adapter->owner); + } + ++#if 0 /* just forget about this for now --km */ + struct i2c_client *i2c_get_client(int driver_id, int adapter_id, + struct i2c_client *prev) + { +@@ -545,18 +472,17 @@ + + return 0; + } ++#endif + + int i2c_use_client(struct i2c_client *client) + { +- if(client->flags & I2C_CLIENT_ALLOW_USE) { +- if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) ++ if (client->flags & I2C_CLIENT_ALLOW_USE) { ++ if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) ++ client->usage_count++; ++ else if (client->usage_count > 0) ++ return -EBUSY; ++ else + client->usage_count++; +- else { +- if(client->usage_count > 0) +- return -EBUSY; +- else +- client->usage_count++; +- } + } + + i2c_inc_use_client(client); +@@ -589,12 +515,13 @@ + #ifdef CONFIG_PROC_FS + + /* This function generates the output for /proc/bus/i2c */ +-int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, ++static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) + { + int i; + int nr = 0; + /* Note that it is safe to write a `little' beyond len. Yes, really. */ ++ down(&core_lists); + for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) + if (adapters[i]) { + nr += sprintf(buf+nr, "i2c-%d\t", i); +@@ -611,6 +538,7 @@ + adapters[i]->name, + adapters[i]->algo->name); + } ++ up(&core_lists); + return nr; + } + +@@ -621,98 +549,125 @@ + struct inode * inode = file->f_dentry->d_inode; + char *kbuf; + struct i2c_client *client; ++ struct i2c_adapter *adap; + int i,j,k,order_nr,len=0; + size_t len_total; + int order[I2C_CLIENT_MAX]; ++#define OUTPUT_LENGTH_PER_LINE 70 + +- if (count > 4000) +- return -EINVAL; + len_total = file->f_pos + count; +- /* Too bad if this gets longer (unlikely) */ +- if (len_total > 4000) +- len_total = 4000; +- for (i = 0; i < I2C_ADAP_MAX; i++) +- if (adapters[i]->inode == inode->i_ino) { +- /* We need a bit of slack in the kernel buffer; this makes the +- sprintf safe. */ +- if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) +- return -ENOMEM; +- /* Order will hold the indexes of the clients +- sorted by address */ +- order_nr=0; +- for (j = 0; j < I2C_CLIENT_MAX; j++) { +- if ((client = adapters[i]->clients[j]) && +- (client->driver->id != I2C_DRIVERID_I2CDEV)) { +- for(k = order_nr; +- (k > 0) && +- adapters[i]->clients[order[k-1]]-> +- addr > client->addr; +- k--) +- order[k] = order[k-1]; +- order[k] = j; +- order_nr++; +- } +- } +- +- +- for (j = 0; (j < order_nr) && (len < len_total); j++) { +- client = adapters[i]->clients[order[j]]; +- len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", +- client->addr, +- client->name, +- client->driver->name); +- } +- len = len - file->f_pos; +- if (len > count) +- len = count; +- if (len < 0) +- len = 0; +- if (copy_to_user (buf,kbuf+file->f_pos, len)) { +- kfree(kbuf); +- return -EFAULT; +- } +- file->f_pos += len; +- kfree(kbuf); +- return len; +- } +- return -ENOENT; ++ if (len_total > (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE) ) ++ /* adjust to maximum file size */ ++ len_total = (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE); ++ ++ down(&core_lists); ++ /* adap = file->private_data; ?? --km */ ++ for (i = 0; i < I2C_ADAP_MAX; i++) { ++ adap = adapters[i]; ++ if (adap && (adap->inode == inode->i_ino)) ++ break; ++ } ++ if ( I2C_ADAP_MAX == i ) { ++ up(&core_lists); ++ return -ENOENT; ++ } ++ ++ /* We need a bit of slack in the kernel buffer; this makes the ++ sprintf safe. */ ++ if (! (kbuf = kmalloc(len_total + ++ OUTPUT_LENGTH_PER_LINE, ++ GFP_KERNEL))) ++ return -ENOMEM; ++ ++ /* Order will hold the indexes of the clients ++ sorted by address */ ++ order_nr=0; ++ down(&adap->list); ++ for (j = 0; j < I2C_CLIENT_MAX; j++) { ++ if ((client = adap->clients[j]) && ++ (client->driver->id != I2C_DRIVERID_I2CDEV)) { ++ for(k = order_nr; ++ (k > 0) && ++ adap->clients[order[k-1]]-> ++ addr > client->addr; ++ k--) ++ order[k] = order[k-1]; ++ order[k] = j; ++ order_nr++; ++ } ++ } ++ ++ ++ for (j = 0; (j < order_nr) && (len < len_total); j++) { ++ client = adap->clients[order[j]]; ++ len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", ++ client->addr, ++ client->name, ++ client->driver->name); ++ } ++ up(&adap->list); ++ up(&core_lists); ++ ++ len = len - file->f_pos; ++ if (len > count) ++ len = count; ++ if (len < 0) ++ len = 0; ++ if (copy_to_user (buf,kbuf+file->f_pos, len)) { ++ kfree(kbuf); ++ return -EFAULT; ++ } ++ file->f_pos += len; ++ kfree(kbuf); ++ return len; ++} ++ ++static int i2cproc_register(struct i2c_adapter *adap, int bus) ++{ ++ char name[8]; ++ struct proc_dir_entry *proc_entry; ++ ++ sprintf(name,"i2c-%d", bus); ++ proc_entry = create_proc_entry(name,0,proc_bus); ++ if (! proc_entry) { ++ printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/%s\n", ++ name); ++ return -ENOENT; ++ } ++ ++ proc_entry->proc_fops = &i2cproc_operations; ++ proc_entry->owner = adap->owner; ++ adap->inode = proc_entry->low_ino; ++ return 0; + } + +-int i2cproc_init(void) ++static void i2cproc_remove(int bus) + { ++ char name[8]; ++ sprintf(name,"i2c-%d", bus); ++ remove_proc_entry(name, proc_bus); ++} + ++static int __init i2cproc_init(void) ++{ + struct proc_dir_entry *proc_bus_i2c; + +- i2cproc_initialized = 0; +- +- if (! proc_bus) { +- printk("i2c-core.o: /proc/bus/ does not exist"); +- i2cproc_cleanup(); +- return -ENOENT; +- } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); +- i2cproc_cleanup(); + return -ENOENT; + } ++ + proc_bus_i2c->read_proc = &read_bus_i2c; + proc_bus_i2c->owner = THIS_MODULE; +- i2cproc_initialized += 2; + return 0; + } + +-int i2cproc_cleanup(void) ++static void __exit i2cproc_cleanup(void) + { +- +- if (i2cproc_initialized >= 1) { +- remove_proc_entry("i2c",proc_bus); +- i2cproc_initialized -= 2; +- } +- return 0; ++ remove_proc_entry("i2c",proc_bus); + } + +- + #endif /* def CONFIG_PROC_FS */ + + /* ---------------------------------------------------- +@@ -728,9 +683,9 @@ + DEB2(printk(KERN_DEBUG "i2c-core.o: master_xfer: %s with %d msgs.\n", + adap->name,num)); + +- I2C_LOCK(adap); ++ down(&adap->bus); + ret = adap->algo->master_xfer(adap,msgs,num); +- I2C_UNLOCK(adap); ++ up(&adap->bus); + + return ret; + } else { +@@ -755,9 +710,9 @@ + DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); + +- I2C_LOCK(adap); ++ down(&adap->bus); + ret = adap->algo->master_xfer(adap,&msg,1); +- I2C_UNLOCK(adap); ++ up(&adap->bus); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. +@@ -785,9 +740,9 @@ + DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); + +- I2C_LOCK(adap); ++ down(&adap->bus); + ret = adap->algo->master_xfer(adap,&msg,1); +- I2C_UNLOCK(adap); ++ up(&adap->bus); + + DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); +@@ -965,6 +920,123 @@ + + /* The SMBus parts */ + ++#define POLY (0x1070U << 3) ++static u8 ++crc8(u16 data) ++{ ++ int i; ++ ++ for(i = 0; i < 8; i++) { ++ if (data & 0x8000) ++ data = data ^ POLY; ++ data = data << 1; ++ } ++ return (u8)(data >> 8); ++} ++ ++/* CRC over count bytes in the first array plus the bytes in the rest ++ array if it is non-null. rest[0] is the (length of rest) - 1 ++ and is included. */ ++u8 i2c_smbus_partial_pec(u8 crc, int count, u8 *first, u8 *rest) ++{ ++ int i; ++ ++ for(i = 0; i < count; i++) ++ crc = crc8((crc ^ first[i]) << 8); ++ if(rest != NULL) ++ for(i = 0; i <= rest[0]; i++) ++ crc = crc8((crc ^ rest[i]) << 8); ++ return crc; ++} ++ ++u8 i2c_smbus_pec(int count, u8 *first, u8 *rest) ++{ ++ return i2c_smbus_partial_pec(0, count, first, rest); ++} ++ ++/* Returns new "size" (transaction type) ++ Note that we convert byte to byte_data and byte_data to word_data ++ rather than invent new xxx_PEC transactions. */ ++int i2c_smbus_add_pec(u16 addr, u8 command, int size, ++ union i2c_smbus_data *data) ++{ ++ u8 buf[3]; ++ ++ buf[0] = addr << 1; ++ buf[1] = command; ++ switch(size) { ++ case I2C_SMBUS_BYTE: ++ data->byte = i2c_smbus_pec(2, buf, NULL); ++ size = I2C_SMBUS_BYTE_DATA; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ buf[2] = data->byte; ++ data->word = buf[2] || ++ (i2c_smbus_pec(3, buf, NULL) << 8); ++ size = I2C_SMBUS_WORD_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ /* unsupported */ ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ data->block[data->block[0] + 1] = ++ i2c_smbus_pec(2, buf, data->block); ++ size = I2C_SMBUS_BLOCK_DATA_PEC; ++ break; ++ } ++ return size; ++} ++ ++int i2c_smbus_check_pec(u16 addr, u8 command, int size, u8 partial, ++ union i2c_smbus_data *data) ++{ ++ u8 buf[3], rpec, cpec; ++ ++ buf[1] = command; ++ switch(size) { ++ case I2C_SMBUS_BYTE_DATA: ++ buf[0] = (addr << 1) | 1; ++ cpec = i2c_smbus_pec(2, buf, NULL); ++ rpec = data->byte; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ buf[0] = (addr << 1) | 1; ++ buf[2] = data->word & 0xff; ++ cpec = i2c_smbus_pec(3, buf, NULL); ++ rpec = data->word >> 8; ++ break; ++ case I2C_SMBUS_WORD_DATA_PEC: ++ /* unsupported */ ++ cpec = rpec = 0; ++ break; ++ case I2C_SMBUS_PROC_CALL_PEC: ++ /* unsupported */ ++ cpec = rpec = 0; ++ break; ++ case I2C_SMBUS_BLOCK_DATA_PEC: ++ buf[0] = (addr << 1); ++ buf[2] = (addr << 1) | 1; ++ cpec = i2c_smbus_pec(3, buf, data->block); ++ rpec = data->block[data->block[0] + 1]; ++ break; ++ case I2C_SMBUS_BLOCK_PROC_CALL_PEC: ++ buf[0] = (addr << 1) | 1; ++ rpec = i2c_smbus_partial_pec(partial, 1, ++ buf, data->block); ++ cpec = data->block[data->block[0] + 1]; ++ break; ++ default: ++ cpec = rpec = 0; ++ break; ++ } ++ if(rpec != cpec) { ++ DEB(printk(KERN_DEBUG "i2c-core.o: Bad PEC 0x%02x vs. 0x%02x\n", ++ rpec, cpec)); ++ return -1; ++ } ++ return 0; ++} ++ + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) + { + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, +@@ -983,8 +1055,9 @@ + + extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) + { ++ union i2c_smbus_data data; /* only for PEC */ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, +- I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); ++ I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,&data); + } + + extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) +@@ -1072,6 +1145,43 @@ + I2C_SMBUS_BLOCK_DATA,&data); + } + ++/* Returns the number of read bytes */ ++extern s32 i2c_smbus_block_process_call(struct i2c_client * client, ++ u8 command, u8 length, u8 *values) ++{ ++ union i2c_smbus_data data; ++ int i; ++ if (length > I2C_SMBUS_BLOCK_MAX - 1) ++ return -1; ++ data.block[0] = length; ++ for (i = 1; i <= length; i++) ++ data.block[i] = values[i-1]; ++ if(i2c_smbus_xfer(client->adapter,client->addr,client->flags, ++ I2C_SMBUS_WRITE, command, ++ I2C_SMBUS_BLOCK_PROC_CALL, &data)) ++ return -1; ++ for (i = 1; i <= data.block[0]; i++) ++ values[i-1] = data.block[i]; ++ return data.block[0]; ++} ++ ++/* Returns the number of read bytes */ ++extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, ++ u8 command, u8 *values) ++{ ++ union i2c_smbus_data data; ++ int i; ++ if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, ++ I2C_SMBUS_READ,command, ++ I2C_SMBUS_I2C_BLOCK_DATA,&data)) ++ return -1; ++ else { ++ for (i = 1; i <= data.block[0]; i++) ++ values[i-1] = data.block[i]; ++ return data.block[0]; ++ } ++} ++ + extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, + u8 command, u8 length, u8 *values) + { +@@ -1098,13 +1208,13 @@ + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ +- unsigned char msgbuf0[34]; +- unsigned char msgbuf1[34]; ++ unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+2]; ++ unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int num = read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; +- int i; ++ int i, len; + + msgbuf0[0] = command; + switch(size) { +@@ -1140,16 +1250,30 @@ + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ ++ read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + break; + case I2C_SMBUS_BLOCK_DATA: ++ case I2C_SMBUS_BLOCK_DATA_PEC: + if (read_write == I2C_SMBUS_READ) { +- printk(KERN_ERR "i2c-core.o: Block read not supported " +- "under I2C emulation!\n"); +- return -1; ++ /* I2C_FUNC_SMBUS_EMUL doesn't include I2C_FUNC_SMBUS_READ_BLOCK_DATA */ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { ++ printk(KERN_ERR "i2c-core.o: Block read not supported " ++ "under I2C emulation!\n"); ++ return -1; ++ } ++ /* set send message */ ++ msg[0].len = 1; ++ /* set recv message */ ++ msg[1].flags |= I2C_M_RECV_LEN; ++ msg[1].len = I2C_SMBUS_BLOCK_MAX + 1; ++ if (size == I2C_SMBUS_BLOCK_DATA_PEC) { ++ msg[1].len++; ++ msg[1].flags |= I2C_M_RECV_PEC; ++ } + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { +@@ -1158,10 +1282,57 @@ + data->block[0]); + return -1; + } ++ if(size == I2C_SMBUS_BLOCK_DATA_PEC) ++ (msg[0].len)++; + for (i = 1; i <= msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + } + break; ++ case I2C_SMBUS_BLOCK_PROC_CALL: ++ case I2C_SMBUS_BLOCK_PROC_CALL_PEC: ++ /* I2C_FUNC_SMBUS_EMUL doesn't include I2C_FUNC_SMBUS_BLOCK_PROC_CALL */ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_PROC_CALL)) { ++ printk(KERN_ERR "i2c-core.o: adapter doesn't support block process call!\n"); ++ return -1; ++ } ++ ++ /* Another special case */ ++ num = 2; ++ read_write = I2C_SMBUS_READ; ++ ++ /* set send message */ ++ msg[0].len = data->block[0] + 2; ++ if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { ++ printk(KERN_ERR "i2c-core.o: smbus_access called with " ++ "invalid block write size (%d)\n", data->block[0]); ++ return -1; ++ } ++ for (i = 1; i <= msg[0].len; i++) ++ msgbuf0[i] = data->block[i-1]; ++ ++ /* set recv message */ ++ msg[1].flags |= I2C_M_RECV_LEN; ++ msg[1].len = I2C_SMBUS_BLOCK_MAX + 1; ++ if (size == I2C_SMBUS_BLOCK_PROC_CALL_PEC) { ++ msg[1].len++; ++ msg[1].flags |= I2C_M_RECV_PEC; ++ } ++ break; ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ if (read_write == I2C_SMBUS_READ) { ++ msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX; ++ } else { ++ msg[0].len = data->block[0] + 1; ++ if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) { ++ printk("i2c-core.o: i2c_smbus_xfer_emulated called with " ++ "invalid block write size (%d)\n", ++ data->block[0]); ++ return -1; ++ } ++ for (i = 1; i <= data->block[0]; i++) ++ msgbuf0[i] = data->block[i]; ++ } ++ break; + default: + printk(KERN_ERR "i2c-core.o: smbus_access called with invalid size (%d)\n", + size); +@@ -1183,25 +1354,72 @@ + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ /* fixed at 32 for now */ ++ data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX; ++ for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++) ++ data->block[i+1] = msgbuf1[i]; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ case I2C_SMBUS_BLOCK_PROC_CALL: ++ case I2C_SMBUS_BLOCK_DATA_PEC: ++ case I2C_SMBUS_BLOCK_PROC_CALL_PEC: ++ len = msgbuf1[0] + 1; ++ if(size == I2C_SMBUS_BLOCK_DATA_PEC || ++ size == I2C_SMBUS_BLOCK_PROC_CALL_PEC) ++ len++; ++ for (i = 0; i < len; i++) ++ data->block[i] = msgbuf1[i]; ++ break; + } + return 0; + } + + +-s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, ++s32 i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) + { + s32 res; +- flags = flags & I2C_M_TEN; +- if (adapter->algo->smbus_xfer) { +- I2C_LOCK(adapter); +- res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, ++ int swpec = 0; ++ u8 partial = 0; ++ ++ flags &= I2C_M_TEN | I2C_CLIENT_PEC; ++ if((flags & I2C_CLIENT_PEC) && ++ !(i2c_check_functionality(adap, I2C_FUNC_SMBUS_HWPEC_CALC))) { ++ swpec = 1; ++ if(read_write == I2C_SMBUS_READ && ++ size == I2C_SMBUS_BLOCK_DATA) ++ size = I2C_SMBUS_BLOCK_DATA_PEC; ++ else if(size == I2C_SMBUS_PROC_CALL) ++ size = I2C_SMBUS_PROC_CALL_PEC; ++ else if(size == I2C_SMBUS_BLOCK_PROC_CALL) { ++ i2c_smbus_add_pec(addr, command, ++ I2C_SMBUS_BLOCK_DATA, data); ++ partial = data->block[data->block[0] + 1]; ++ size = I2C_SMBUS_BLOCK_PROC_CALL_PEC; ++ } else if(read_write == I2C_SMBUS_WRITE && ++ size != I2C_SMBUS_QUICK && ++ size != I2C_SMBUS_I2C_BLOCK_DATA) ++ size = i2c_smbus_add_pec(addr, command, size, data); ++ } ++ ++ if (adap->algo->smbus_xfer) { ++ down(&adap->bus); ++ res = adap->algo->smbus_xfer(adap,addr,flags,read_write, + command,size,data); +- I2C_UNLOCK(adapter); ++ up(&adap->bus); + } else +- res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, ++ res = i2c_smbus_xfer_emulated(adap,addr,flags,read_write, + command,size,data); ++ ++ if(res >= 0 && swpec && ++ size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA && ++ (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC || ++ size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) { ++ if(i2c_smbus_check_pec(addr, command, size, partial, data)) ++ return -1; ++ } + return res; + } + +@@ -1228,143 +1446,37 @@ + printk(KERN_INFO "i2c-core.o: i2c core module version %s (%s)\n", I2C_VERSION, I2C_DATE); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); +- adap_count=0; +- driver_count=0; + +- init_MUTEX(&adap_lock); +- init_MUTEX(&driver_lock); +- +- i2cproc_init(); +- ++#ifdef CONFIG_PROC_FS ++ return i2cproc_init(); ++#else + return 0; +-} +- +-#ifndef MODULE +-#ifdef CONFIG_I2C_CHARDEV +- extern int i2c_dev_init(void); +-#endif +-#ifdef CONFIG_I2C_ALGOBIT +- extern int i2c_algo_bit_init(void); +-#endif +-#ifdef CONFIG_I2C_PHILIPSPAR +- extern int i2c_bitlp_init(void); +-#endif +-#ifdef CONFIG_I2C_ELV +- extern int i2c_bitelv_init(void); +-#endif +-#ifdef CONFIG_I2C_VELLEMAN +- extern int i2c_bitvelle_init(void); +-#endif +-#ifdef CONFIG_I2C_BITVIA +- extern int i2c_bitvia_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGOPCF +- extern int i2c_algo_pcf_init(void); +-#endif +-#ifdef CONFIG_I2C_ELEKTOR +- extern int i2c_pcfisa_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGO8XX +- extern int i2c_algo_8xx_init(void); +-#endif +-#ifdef CONFIG_I2C_RPXLITE +- extern int i2c_rpx_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGO_SIBYTE +- extern int i2c_algo_sibyte_init(void); +- extern int i2c_sibyte_init(void); +-#endif +-#ifdef CONFIG_I2C_MAX1617 +- extern int i2c_max1617_init(void); +-#endif +-#ifdef CONFIG_I2C_ALGO_AU1550 +- extern int i2c_pb1550_init(void); + #endif ++} + +-#ifdef CONFIG_I2C_PROC +- extern int sensors_init(void); ++static void __exit i2c_exit(void) ++{ ++#ifdef CONFIG_PROC_FS ++ i2cproc_cleanup(); + #endif ++} + +-/* This is needed for automatic patch generation: sensors code starts here */ +-/* This is needed for automatic patch generation: sensors code ends here */ +- ++/* leave this in for now simply to make patching easier so we don't have ++ to remove the call in drivers/char/mem.c */ + int __init i2c_init_all(void) + { +- /* --------------------- global ----- */ +- i2c_init(); +- +-#ifdef CONFIG_I2C_CHARDEV +- i2c_dev_init(); +-#endif +- /* --------------------- bit -------- */ +-#ifdef CONFIG_I2C_ALGOBIT +- i2c_algo_bit_init(); +-#endif +-#ifdef CONFIG_I2C_PHILIPSPAR +- i2c_bitlp_init(); +-#endif +-#ifdef CONFIG_I2C_ELV +- i2c_bitelv_init(); +-#endif +-#ifdef CONFIG_I2C_VELLEMAN +- i2c_bitvelle_init(); +-#endif +- +- /* --------------------- pcf -------- */ +-#ifdef CONFIG_I2C_ALGOPCF +- i2c_algo_pcf_init(); +-#endif +-#ifdef CONFIG_I2C_ELEKTOR +- i2c_pcfisa_init(); +-#endif +- +- /* --------------------- 8xx -------- */ +-#ifdef CONFIG_I2C_ALGO8XX +- i2c_algo_8xx_init(); +-#endif +-#ifdef CONFIG_I2C_RPXLITE +- i2c_rpx_init(); +-#endif +- +- /* --------------------- SiByte -------- */ +-#ifdef CONFIG_I2C_ALGO_SIBYTE +- i2c_algo_sibyte_init(); +- i2c_sibyte_init(); +-#endif +-#ifdef CONFIG_I2C_MAX1617 +- i2c_max1617_init(); +-#endif +- +-#ifdef CONFIG_I2C_ALGO_AU1550 +- i2c_pb1550_init(); +-#endif +- +- /* -------------- proc interface ---- */ +-#ifdef CONFIG_I2C_PROC +- sensors_init(); +-#endif +-/* This is needed for automatic patch generation: sensors code starts here */ +-/* This is needed for automatic patch generation: sensors code ends here */ +- + return 0; + } + +-#endif +- +- +- + EXPORT_SYMBOL(i2c_add_adapter); + EXPORT_SYMBOL(i2c_del_adapter); + EXPORT_SYMBOL(i2c_add_driver); + EXPORT_SYMBOL(i2c_del_driver); + EXPORT_SYMBOL(i2c_attach_client); + EXPORT_SYMBOL(i2c_detach_client); +-EXPORT_SYMBOL(i2c_inc_use_client); +-EXPORT_SYMBOL(i2c_dec_use_client); ++#if 0 + EXPORT_SYMBOL(i2c_get_client); ++#endif + EXPORT_SYMBOL(i2c_use_client); + EXPORT_SYMBOL(i2c_release_client); + EXPORT_SYMBOL(i2c_check_addr); +@@ -1388,11 +1500,12 @@ + EXPORT_SYMBOL(i2c_smbus_process_call); + EXPORT_SYMBOL(i2c_smbus_read_block_data); + EXPORT_SYMBOL(i2c_smbus_write_block_data); ++EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); ++EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + + EXPORT_SYMBOL(i2c_get_functionality); + EXPORT_SYMBOL(i2c_check_functionality); + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus main module"); + MODULE_LICENSE("GPL"); +@@ -1400,13 +1513,5 @@ + MODULE_PARM(i2c_debug, "i"); + MODULE_PARM_DESC(i2c_debug,"debug level"); + +-int init_module(void) +-{ +- return i2c_init(); +-} +- +-void cleanup_module(void) +-{ +- i2cproc_cleanup(); +-} +-#endif ++module_init(i2c_init); ++module_exit(i2c_exit); +--- linux-old/drivers/i2c/i2c-dev.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-dev.c Mon Dec 13 19:26:32 2004 +@@ -28,9 +28,8 @@ + /* The devfs code is contributed by Philipp Matthias Hahn + */ + +-/* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */ ++/* $Id: i2c-dev.c,v 1.57 2003/12/22 20:03:39 khali Exp $ */ + +-#include + #include + #include + #include +@@ -39,21 +38,14 @@ + #ifdef CONFIG_DEVFS_FS + #include + #endif +- +- +-/* If you want debugging uncomment: */ +-/* #define DEBUG */ +- + #include +-#include +- + #include + #include ++#include ++ ++/* If you want debugging uncomment: */ ++/* #define DEBUG */ + +-#ifdef MODULE +-extern int init_module(void); +-extern int cleanup_module(void); +-#endif /* def MODULE */ + + /* struct file_operations changed too often in the 2.1 series for nice code */ + +@@ -73,22 +65,14 @@ + static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +-#ifdef MODULE +-static +-#else +-extern +-#endif +- int __init i2c_dev_init(void); +-static int i2cdev_cleanup(void); +- + static struct file_operations i2cdev_fops = { +- owner: THIS_MODULE, +- llseek: no_llseek, +- read: i2cdev_read, +- write: i2cdev_write, +- ioctl: i2cdev_ioctl, +- open: i2cdev_open, +- release: i2cdev_release, ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .read = i2cdev_read, ++ .write = i2cdev_write, ++ .ioctl = i2cdev_ioctl, ++ .open = i2cdev_open, ++ .release = i2cdev_release, + }; + + #define I2CDEV_ADAPS_MAX I2C_ADAP_MAX +@@ -99,28 +83,22 @@ + #endif + + static struct i2c_driver i2cdev_driver = { +- name: "i2c-dev dummy driver", +- id: I2C_DRIVERID_I2CDEV, +- flags: I2C_DF_DUMMY, +- attach_adapter: i2cdev_attach_adapter, +- detach_client: i2cdev_detach_client, +- command: i2cdev_command, +-/* inc_use: NULL, +- dec_use: NULL, */ ++ .owner = THIS_MODULE, /* not really used */ ++ .name = "i2c-dev dummy driver", ++ .id = I2C_DRIVERID_I2CDEV, ++ .flags = I2C_DF_DUMMY, ++ .attach_adapter = i2cdev_attach_adapter, ++ .detach_client = i2cdev_detach_client, ++ .command = i2cdev_command, + }; + + static struct i2c_client i2cdev_client_template = { +- name: "I2C /dev entry", +- id: 1, +- flags: 0, +- addr: -1, +-/* adapter: NULL, */ +- driver: &i2cdev_driver, +-/* data: NULL */ ++ .name = "I2C /dev entry", ++ .id = 1, ++ .addr = -1, ++ .driver = &i2cdev_driver, + }; + +-static int i2cdev_initialized; +- + static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset) + { +@@ -142,7 +120,7 @@ + return -ENOMEM; + + #ifdef DEBUG +- printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev), ++ printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",minor(inode->i_rdev), + count); + #endif + +@@ -177,7 +155,7 @@ + } + + #ifdef DEBUG +- printk(KERN_DEBUG "i2c-dev.o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev), ++ printk(KERN_DEBUG "i2c-dev.o: i2c-%d writing %d bytes.\n",minor(inode->i_rdev), + count); + #endif + ret = i2c_master_send(client,tmp,count); +@@ -199,7 +177,7 @@ + + #ifdef DEBUG + printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", +- MINOR(inode->i_rdev),cmd, arg); ++ minor(inode->i_rdev),cmd, arg); + #endif /* DEBUG */ + + switch ( cmd ) { +@@ -218,6 +196,12 @@ + else + client->flags &= ~I2C_M_TEN; + return 0; ++ case I2C_PEC: ++ if (arg) ++ client->flags |= I2C_CLIENT_PEC; ++ else ++ client->flags &= ~I2C_CLIENT_PEC; ++ return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return (copy_to_user((unsigned long *)arg,&funcs, +@@ -318,7 +302,8 @@ + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA) && +- (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA)) { ++ (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && ++ (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { + #ifdef DEBUG + printk(KERN_DEBUG "i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); +@@ -361,10 +346,11 @@ + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); +- else /* size == I2C_SMBUS_BLOCK_DATA */ ++ else /* size == smbus block, i2c block, or block proc. call */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || ++ (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data_arg.data, datasize)) + return -EFAULT; +@@ -373,6 +359,7 @@ + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || ++ (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data_arg.data, &temp, datasize)) + return -EFAULT; +@@ -387,7 +374,7 @@ + + int i2cdev_open (struct inode *inode, struct file *file) + { +- unsigned int minor = MINOR(inode->i_rdev); ++ unsigned int minor = minor(inode->i_rdev); + struct i2c_client *client; + + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { +@@ -403,11 +390,13 @@ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); ++ ++ /* registered with adapter, passed as client to user */ + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; + +- if (i2cdev_adaps[minor]->inc_use) +- i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); ++ if(client->adapter->owner) ++ __MOD_INC_USE_COUNT(client->adapter->owner); + + #ifdef DEBUG + printk(KERN_DEBUG "i2c-dev.o: opened i2c-%d\n",minor); +@@ -417,16 +406,19 @@ + + static int i2cdev_release (struct inode *inode, struct file *file) + { +- unsigned int minor = MINOR(inode->i_rdev); +- kfree(file->private_data); +- file->private_data=NULL; ++ struct i2c_client *client; ++#ifdef DEBUG ++ unsigned int minor = minor(inode->i_rdev); ++#endif ++ ++ client = file->private_data; ++ file->private_data = NULL; ++ if(client->adapter->owner) ++ __MOD_DEC_USE_COUNT(client->adapter->owner); ++ kfree(client); + #ifdef DEBUG + printk(KERN_DEBUG "i2c-dev.o: Closed: i2c-%d\n", minor); + #endif +- lock_kernel(); +- if (i2cdev_adaps[minor]->dec_use) +- i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); +- unlock_kernel(); + return 0; + } + +@@ -451,7 +443,7 @@ + devfs_i2c[i] = devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, I2C_MAJOR, i, + S_IFCHR | S_IRUSR | S_IWUSR, +- &i2cdev_fops, NULL); ++ &i2cdev_fops, adap); + #endif + printk(KERN_DEBUG "i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { +@@ -479,13 +471,12 @@ + return -1; + } + +-int __init i2c_dev_init(void) ++static int __init i2c_dev_init(void) + { + int res; + + printk(KERN_INFO "i2c-dev.o: i2c /dev entries driver module version %s (%s)\n", I2C_VERSION, I2C_DATE); + +- i2cdev_initialized = 0; + #ifdef CONFIG_DEVFS_FS + if (devfs_register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops)) { + #else +@@ -498,63 +489,31 @@ + #ifdef CONFIG_DEVFS_FS + devfs_handle = devfs_mk_dir(NULL, "i2c", NULL); + #endif +- i2cdev_initialized ++; +- + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n"); +- i2cdev_cleanup(); ++#ifdef CONFIG_DEVFS_FS ++ devfs_unregister(devfs_handle); ++#endif ++ unregister_chrdev(I2C_MAJOR,"i2c"); + return res; + } +- i2cdev_initialized ++; + return 0; + } + +-int i2cdev_cleanup(void) ++static void __exit i2c_dev_exit(void) + { +- int res; +- +- if (i2cdev_initialized >= 2) { +- if ((res = i2c_del_driver(&i2cdev_driver))) { +- printk("i2c-dev.o: Driver deregistration failed, " +- "module not removed.\n"); +- return res; +- } +- i2cdev_initialized --; +- } +- +- if (i2cdev_initialized >= 1) { ++ i2c_del_driver(&i2cdev_driver); + #ifdef CONFIG_DEVFS_FS +- devfs_unregister(devfs_handle); +- if ((res = devfs_unregister_chrdev(I2C_MAJOR, "i2c"))) { +-#else +- if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { ++ devfs_unregister(devfs_handle); + #endif +- printk("i2c-dev.o: unable to release major %d for i2c bus\n", +- I2C_MAJOR); +- return res; +- } +- i2cdev_initialized --; +- } +- return 0; ++ unregister_chrdev(I2C_MAJOR,"i2c"); + } + + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE +- + MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); + MODULE_DESCRIPTION("I2C /dev entries driver"); + MODULE_LICENSE("GPL"); + +-int init_module(void) +-{ +- return i2c_dev_init(); +-} +- +-int cleanup_module(void) +-{ +- return i2cdev_cleanup(); +-} +- +-#endif /* def MODULE */ +- ++module_init(i2c_dev_init); ++module_exit(i2c_dev_exit); +--- linux-old/include/linux/i2c-dev.h Sat Jul 5 03:23:47 2003 ++++ linux/include/linux/i2c-dev.h Mon Dec 13 19:26:32 2004 +@@ -19,14 +19,16 @@ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +-/* $Id: i2c-dev.h,v 1.9 2001/08/15 03:04:58 mds Exp $ */ +- +-#ifndef I2C_DEV_H +-#define I2C_DEV_H ++/* $Id: i2c-dev.h,v 1.14 2003/07/25 07:56:42 khali Exp $ */ + ++#ifndef _LINUX_I2C_DEV_H ++#define _LINUX_I2C_DEV_H + + #include +-#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18) ++#define minor(d) MINOR(d) ++#endif + + /* Some IOCTL commands are defined in */ + /* Note: 10-bit addresses are NOT supported! */ +@@ -45,137 +47,4 @@ + __u32 nmsgs; /* number of i2c_msgs */ + }; + +-#ifndef __KERNEL__ +- +-#include +- +-static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, +- int size, union i2c_smbus_data *data) +-{ +- struct i2c_smbus_ioctl_data args; +- +- args.read_write = read_write; +- args.command = command; +- args.size = size; +- args.data = data; +- return ioctl(file,I2C_SMBUS,&args); +-} +- +- +-static inline __s32 i2c_smbus_write_quick(int file, __u8 value) +-{ +- return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); +-} +- +-static inline __s32 i2c_smbus_read_byte(int file) +-{ +- union i2c_smbus_data data; +- if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) +- return -1; +- else +- return 0x0FF & data.byte; +-} +- +-static inline __s32 i2c_smbus_write_byte(int file, __u8 value) +-{ +- return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, +- I2C_SMBUS_BYTE,NULL); +-} +- +-static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +-{ +- union i2c_smbus_data data; +- if (i2c_smbus_access(file,I2C_SMBUS_READ,command, +- I2C_SMBUS_BYTE_DATA,&data)) +- return -1; +- else +- return 0x0FF & data.byte; +-} +- +-static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, +- __u8 value) +-{ +- union i2c_smbus_data data; +- data.byte = value; +- return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, +- I2C_SMBUS_BYTE_DATA, &data); +-} +- +-static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +-{ +- union i2c_smbus_data data; +- if (i2c_smbus_access(file,I2C_SMBUS_READ,command, +- I2C_SMBUS_WORD_DATA,&data)) +- return -1; +- else +- return 0x0FFFF & data.word; +-} +- +-static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, +- __u16 value) +-{ +- union i2c_smbus_data data; +- data.word = value; +- return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, +- I2C_SMBUS_WORD_DATA, &data); +-} +- +-static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) +-{ +- union i2c_smbus_data data; +- data.word = value; +- if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, +- I2C_SMBUS_PROC_CALL,&data)) +- return -1; +- else +- return 0x0FFFF & data.word; +-} +- +- +-/* Returns the number of read bytes */ +-static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, +- __u8 *values) +-{ +- union i2c_smbus_data data; +- int i; +- if (i2c_smbus_access(file,I2C_SMBUS_READ,command, +- I2C_SMBUS_BLOCK_DATA,&data)) +- return -1; +- else { +- for (i = 1; i <= data.block[0]; i++) +- values[i-1] = data.block[i]; +- return data.block[0]; +- } +-} +- +-static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, +- __u8 length, __u8 *values) +-{ +- union i2c_smbus_data data; +- int i; +- if (length > 32) +- length = 32; +- for (i = 1; i <= length; i++) +- data.block[i] = values[i-1]; +- data.block[0] = length; +- return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, +- I2C_SMBUS_BLOCK_DATA, &data); +-} +- +-static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, +- __u8 length, __u8 *values) +-{ +- union i2c_smbus_data data; +- int i; +- if (length > 32) +- length = 32; +- for (i = 1; i <= length; i++) +- data.block[i] = values[i-1]; +- data.block[0] = length; +- return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, +- I2C_SMBUS_I2C_BLOCK_DATA, &data); +-} +- +-#endif /* ndef __KERNEL__ */ +- +-#endif ++#endif /* _LINUX_I2C_DEV_H */ +--- linux-old/drivers/i2c/i2c-elektor.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-elektor.c Mon Dec 13 19:26:33 2004 +@@ -31,23 +31,22 @@ + #include + #include + #include ++#include + #include +-#include +-#include +- ++#include + #include + #include +-#include +-#include "i2c-pcf8584.h" ++#include ++#include + + #define DEFAULT_BASE 0x330 + +-static int base = 0; +-static int irq = 0; ++static int base; ++static int irq; + static int clock = 0x1c; + static int own = 0x55; +-static int mmapped = 0; +-static int i2c_debug = 0; ++static int mmapped; ++static int i2c_debug; + + /* vdovikin: removed static struct i2c_pcf_isa gpi; code - + this module in real supports only one device, due to missing arguments +@@ -56,6 +55,7 @@ + + static wait_queue_head_t pcf_wait; + static int pcf_pending; ++static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED; + + /* ----- global defines ----------------------------------------------- */ + #define DEB(x) if (i2c_debug>=1) x +@@ -63,13 +63,24 @@ + #define DEB3(x) if (i2c_debug>=3) x + #define DEBE(x) x /* error messages */ + ++ ++/* compatibility */ ++#ifndef isa_readb ++#define isa_readb readb ++#endif ++ ++#ifndef isa_writeb ++#define isa_writeb writeb ++#endif ++ + /* ----- local functions ---------------------------------------------- */ + + static void pcf_isa_setbyte(void *data, int ctl, int val) + { + int address = ctl ? (base + 1) : base; + +- if (ctl && irq) { ++ /* enable irq if any specified for serial operation */ ++ if (ctl && irq && (val & I2C_PCF_ESO)) { + val |= I2C_PCF_ENI; + } + +@@ -81,10 +92,10 @@ + break; + case 2: /* double mapped I/O needed for UP2000 board, + I don't know why this... */ +- writeb(val, address); ++ isa_writeb(val, address); + /* fall */ + case 1: /* memory mapped I/O */ +- writeb(val, address); ++ isa_writeb(val, address); + break; + } + } +@@ -92,7 +103,7 @@ + static int pcf_isa_getbyte(void *data, int ctl) + { + int address = ctl ? (base + 1) : base; +- int val = mmapped ? readb(address) : inb(address); ++ int val = mmapped ? isa_readb(address) : inb(address); + + DEB3(printk(KERN_DEBUG "i2c-elektor.o: Read 0x%X 0x%02X\n", address, val)); + +@@ -115,12 +126,12 @@ + int timeout = 2; + + if (irq > 0) { +- cli(); ++ spin_lock_irq(&irq_driver_lock); + if (pcf_pending == 0) { + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); + } else + pcf_pending = 0; +- sti(); ++ spin_unlock_irq(&irq_driver_lock); + } else { + udelay(100); + } +@@ -136,13 +147,11 @@ + static int pcf_isa_init(void) + { + if (!mmapped) { +- if (check_region(base, 2) < 0 ) { ++ if (!request_region(base, 2, "i2c (isa bus adapter)")) { + printk(KERN_ERR + "i2c-elektor.o: requested I/O region (0x%X:2) " + "is in use.\n", base); + return -ENODEV; +- } else { +- request_region(base, 2, "i2c (isa bus adapter)"); + } + } + if (irq > 0) { +@@ -156,70 +165,29 @@ + } + + +-static void __exit pcf_isa_exit(void) +-{ +- if (irq > 0) { +- disable_irq(irq); +- free_irq(irq, 0); +- } +- if (!mmapped) { +- release_region(base , 2); +- } +-} +- +- +-static int pcf_isa_reg(struct i2c_client *client) +-{ +- return 0; +-} +- +- +-static int pcf_isa_unreg(struct i2c_client *client) +-{ +- return 0; +-} +- +-static void pcf_isa_inc_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_INC_USE_COUNT; +-#endif +-} +- +-static void pcf_isa_dec_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_DEC_USE_COUNT; +-#endif +-} +- +- + /* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + static struct i2c_algo_pcf_data pcf_isa_data = { +- NULL, +- pcf_isa_setbyte, +- pcf_isa_getbyte, +- pcf_isa_getown, +- pcf_isa_getclock, +- pcf_isa_waitforpin, +- 10, 10, 100, /* waits, timeout */ ++ .setpcf = pcf_isa_setbyte, ++ .getpcf = pcf_isa_getbyte, ++ .getown = pcf_isa_getown, ++ .getclock = pcf_isa_getclock, ++ .waitforpin = pcf_isa_waitforpin, ++ .udelay = 10, ++ .mdelay = 10, ++ .timeout = HZ, + }; + + static struct i2c_adapter pcf_isa_ops = { +- "PCF8584 ISA adapter", +- I2C_HW_P_ELEK, +- NULL, +- &pcf_isa_data, +- pcf_isa_inc_use, +- pcf_isa_dec_use, +- pcf_isa_reg, +- pcf_isa_unreg, ++ .owner = THIS_MODULE, ++ .name = "PCF8584 ISA adapter", ++ .id = I2C_HW_P_ELEK, ++ .algo_data = &pcf_isa_data, + }; + +-int __init i2c_pcfisa_init(void) ++static int __init i2c_pcfisa_init(void) + { + #ifdef __alpha__ + /* check to see we have memory mapped PCF8584 connected to the +@@ -277,22 +245,41 @@ + } + + init_waitqueue_head(&pcf_wait); +- if (pcf_isa_init() == 0) { +- if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) +- return -ENODEV; +- } else { ++ if (pcf_isa_init()) + return -ENODEV; +- } ++ if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) ++ goto fail; + + printk(KERN_DEBUG "i2c-elektor.o: found device at %#x.\n", base); + + return 0; ++ ++ fail: ++ if (irq > 0) { ++ disable_irq(irq); ++ free_irq(irq, 0); ++ } ++ ++ if (!mmapped) ++ release_region(base , 2); ++ return -ENODEV; + } + ++static void __exit i2c_pcfisa_exit(void) ++{ ++ i2c_pcf_del_bus(&pcf_isa_ops); ++ ++ if (irq > 0) { ++ disable_irq(irq); ++ free_irq(irq, 0); ++ } ++ ++ if (!mmapped) ++ release_region(base , 2); ++} + + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Hans Berglund "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + MODULE_LICENSE("GPL"); +@@ -304,15 +291,5 @@ + MODULE_PARM(mmapped, "i"); + MODULE_PARM(i2c_debug, "i"); + +-int init_module(void) +-{ +- return i2c_pcfisa_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_pcf_del_bus(&pcf_isa_ops); +- pcf_isa_exit(); +-} +- +-#endif ++module_init(i2c_pcfisa_init); ++module_exit(i2c_pcfisa_exit); +--- linux-old/include/linux/i2c-elektor.h Tue Nov 6 00:55:29 2001 ++++ linux/include/linux/i2c-elektor.h Mon Dec 13 19:26:33 2004 +@@ -1,47 +0,0 @@ +-/* ------------------------------------------------------------------------- */ +-/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +-/* ------------------------------------------------------------------------- */ +-/* Copyright (C) 1995-97 Simon G. Vogl +- 1998-99 Hans Berglund +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +-/* ------------------------------------------------------------------------- */ +- +-/* With some changes from Kyösti Mälkki and even +- Frodo Looijaard */ +- +-/* $Id: i2c-elektor.h,v 1.5 2001/06/05 01:46:33 mds Exp $ */ +- +-#ifndef I2C_PCF_ELEKTOR_H +-#define I2C_PCF_ELEKTOR_H 1 +- +-/* +- * This struct contains the hw-dependent functions of PCF8584 adapters to +- * manipulate the registers, and to init any hw-specific features. +- * vdovikin: removed: this module in real supports only one device, +- * due to missing arguments in some functions, called from the algo-pcf module. +- * Sometimes it's need to be rewriten - +- * but for now just remove this for simpler reading */ +- +-/* +-struct i2c_pcf_isa { +- int pi_base; +- int pi_irq; +- int pi_clock; +- int pi_own; +-}; +-*/ +- +-#endif /* PCF_ELEKTOR_H */ +--- linux-old/drivers/i2c/i2c-elv.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-elv.c Mon Dec 13 19:26:33 2004 +@@ -21,21 +21,18 @@ + /* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +-/* $Id: i2c-elv.c,v 1.17 2001/07/29 02:44:25 mds Exp $ */ ++/* $Id: i2c-elv.c,v 1.29 2003/12/22 20:03:11 khali Exp $ */ + + #include + #include + #include + #include + #include +- +-#include +- + #include +-#include + #include + #include + #include ++#include + + #define DEFAULT_BASE 0x378 + static int base=0; +@@ -89,58 +86,31 @@ + + static int bit_elv_init(void) + { +- if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { +- return -ENODEV; +- } else { +- /* test for ELV adap. */ +- if (inb(base+1) & 0x80) { /* BUSY should be high */ +- DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Busy was low.\n")); +- return -ENODEV; +- } else { +- outb(0x0c,base+2); /* SLCT auf low */ +- udelay(400); +- if ( !(inb(base+1) && 0x10) ) { +- outb(0x04,base+2); +- DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Select was high.\n")); +- return -ENODEV; +- } +- } +- request_region(base,(base == 0x3bc)? 3 : 8, +- "i2c (ELV adapter)"); +- PortData = 0; +- bit_elv_setsda((void*)base,1); +- bit_elv_setscl((void*)base,1); ++ if (!request_region(base, (base == 0x3bc) ? 3 : 8, ++ "i2c (ELV adapter)")) ++ return -ENODEV; ++ ++ if (inb(base+1) & 0x80) { /* BUSY should be high */ ++ DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Busy was low.\n")); ++ goto fail; ++ } ++ ++ outb(0x0c,base+2); /* SLCT auf low */ ++ udelay(400); ++ if (!(inb(base+1) && 0x10)) { ++ outb(0x04,base+2); ++ DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Select was high.\n")); ++ goto fail; + } +- return 0; +-} +- +-static void __exit bit_elv_exit(void) +-{ +- release_region( base , (base == 0x3bc)? 3 : 8 ); +-} +- +-static int bit_elv_reg(struct i2c_client *client) +-{ +- return 0; +-} + +-static int bit_elv_unreg(struct i2c_client *client) +-{ ++ PortData = 0; ++ bit_elv_setsda((void*)base,1); ++ bit_elv_setscl((void*)base,1); + return 0; +-} + +-static void bit_elv_inc_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_INC_USE_COUNT; +-#endif +-} +- +-static void bit_elv_dec_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_DEC_USE_COUNT; +-#endif ++fail: ++ release_region(base , (base == 0x3bc) ? 3 : 8); ++ return -ENODEV; + } + + /* ------------------------------------------------------------------------ +@@ -148,26 +118,23 @@ + * This is only done when more than one hardware adapter is supported. + */ + static struct i2c_algo_bit_data bit_elv_data = { +- NULL, +- bit_elv_setsda, +- bit_elv_setscl, +- bit_elv_getsda, +- bit_elv_getscl, +- 80, 80, 100, /* waits, timeout */ ++ .setsda = bit_elv_setsda, ++ .setscl = bit_elv_setscl, ++ .getsda = bit_elv_getsda, ++ .getscl = bit_elv_getscl, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ + }; + + static struct i2c_adapter bit_elv_ops = { +- "ELV Parallel port adaptor", +- I2C_HW_B_ELV, +- NULL, +- &bit_elv_data, +- bit_elv_inc_use, +- bit_elv_dec_use, +- bit_elv_reg, +- bit_elv_unreg, ++ .owner = THIS_MODULE, ++ .name = "ELV Parallel port adaptor", ++ .id = I2C_HW_B_ELV, ++ .algo_data = &bit_elv_data, + }; + +-int __init i2c_bitelv_init(void) ++static int __init i2c_bitelv_init(void) + { + printk(KERN_INFO "i2c-elv.o: i2c ELV parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + if (base==0) { +@@ -193,25 +160,19 @@ + return 0; + } + ++static void __exit i2c_bitelv_exit(void) ++{ ++ i2c_bit_del_bus(&bit_elv_ops); ++ release_region(base, (base == 0x3bc) ? 3 : 8); ++} + + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter"); + MODULE_LICENSE("GPL"); + + MODULE_PARM(base, "i"); + +-int init_module(void) +-{ +- return i2c_bitelv_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bit_del_bus(&bit_elv_ops); +- bit_elv_exit(); +-} +- +-#endif ++module_init(i2c_bitelv_init); ++module_exit(i2c_bitelv_exit); +--- linux-old/drivers/i2c/i2c-frodo.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-frodo.c Mon Dec 13 19:26:33 2004 +@@ -0,0 +1,83 @@ ++ ++/* ++ * linux/drivers/i2c/i2c-frodo.c ++ * ++ * Author: Abraham van der Merwe ++ * ++ * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110 ++ * Development board (Frodo). ++ * ++ * This source code is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static void frodo_setsda (void *data,int state) ++{ ++ if (state) ++ FRODO_CPLD_I2C |= FRODO_I2C_SDA_OUT; ++ else ++ FRODO_CPLD_I2C &= ~FRODO_I2C_SDA_OUT; ++} ++ ++static void frodo_setscl (void *data,int state) ++{ ++ if (state) ++ FRODO_CPLD_I2C |= FRODO_I2C_SCL_OUT; ++ else ++ FRODO_CPLD_I2C &= ~FRODO_I2C_SCL_OUT; ++} ++ ++static int frodo_getsda (void *data) ++{ ++ return ((FRODO_CPLD_I2C & FRODO_I2C_SDA_IN) != 0); ++} ++ ++static int frodo_getscl (void *data) ++{ ++ return ((FRODO_CPLD_I2C & FRODO_I2C_SCL_IN) != 0); ++} ++ ++static struct i2c_algo_bit_data bit_frodo_data = { ++ .setsda = frodo_setsda, ++ .setscl = frodo_setscl, ++ .getsda = frodo_getsda, ++ .getscl = frodo_getscl, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ ++}; ++ ++static struct i2c_adapter frodo_ops = { ++ .owner = THIS_MODULE, ++ .name = "Frodo adapter driver", ++ .id = I2C_HW_B_FRODO, ++ .algo_data = &bit_frodo_data, ++}; ++ ++static int __init i2c_frodo_init (void) ++{ ++ return i2c_bit_add_bus(&frodo_ops); ++} ++ ++static void __exit i2c_frodo_exit (void) ++{ ++ i2c_bit_del_bus(&frodo_ops); ++} ++ ++MODULE_AUTHOR ("Abraham van der Merwe "); ++MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo"); ++MODULE_LICENSE ("GPL"); ++ ++module_init (i2c_frodo_init); ++module_exit (i2c_frodo_exit); ++ +--- linux-old/include/linux/i2c-id.h Wed Jul 7 00:38:02 2004 ++++ linux/include/linux/i2c-id.h Mon Dec 13 19:26:33 2004 +@@ -20,10 +20,11 @@ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* ------------------------------------------------------------------------- */ + +-/* $Id: i2c-id.h,v 1.35 2001/08/12 17:22:20 mds Exp $ */ ++/* $Id: i2c-id.h,v 1.92 2004/07/27 18:15:06 mmh Exp $ */ ++ ++#ifndef LINUX_I2C_ID_H ++#define LINUX_I2C_ID_H + +-#ifndef I2C_ID_H +-#define I2C_ID_H + /* + * This file is part of the i2c-bus package and contains the identifier + * values for drivers, adapters and other folk populating these serial +@@ -90,10 +91,25 @@ + #define I2C_DRIVERID_DRP3510 43 /* ADR decoder (Astra Radio) */ + #define I2C_DRIVERID_SP5055 44 /* Satellite tuner */ + #define I2C_DRIVERID_STV0030 45 /* Multipurpose switch */ ++#define I2C_DRIVERID_SAA7108 46 /* video decoder, image scaler */ ++#define I2C_DRIVERID_DS1307 47 /* DS1307 real time clock */ + #define I2C_DRIVERID_ADV7175 48 /* ADV 7175/7176 video encoder */ +-#define I2C_DRIVERID_MAX1617 56 /* temp sensor */ +-#define I2C_DRIVERID_SAA7191 57 /* video decoder */ +-#define I2C_DRIVERID_INDYCAM 58 /* SGI IndyCam */ ++#define I2C_DRIVERID_ZR36067 49 /* Zoran 36067 video encoder */ ++#define I2C_DRIVERID_ZR36120 50 /* Zoran 36120 video encoder */ ++#define I2C_DRIVERID_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */ ++#define I2C_DRIVERID_STM41T00 52 /* real time clock */ ++#define I2C_DRIVERID_UDA1342 53 /* UDA1342 audio codec */ ++#define I2C_DRIVERID_ADV7170 54 /* video encoder */ ++#define I2C_DRIVERID_RADEON 55 /* I2C bus on Radeon boards */ ++#define I2C_DRIVERID_MAX1617 56 /* temp sensor */ ++#define I2C_DRIVERID_SAA7191 57 /* video encoder */ ++#define I2C_DRIVERID_INDYCAM 58 /* SGI IndyCam */ ++#define I2C_DRIVERID_BT832 59 /* CMOS camera video processor */ ++#define I2C_DRIVERID_TDA9887 60 /* TDA988x IF-PLL demodulator */ ++#define I2C_DRIVERID_OVCAMCHIP 61 /* OmniVision CMOS image sens. */ ++#define I2C_DRIVERID_TDA7313 62 /* TDA7313 audio processor */ ++#define I2C_DRIVERID_MAX6900 63 /* MAX6900 real-time clock */ ++ + + #define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */ + #define I2C_DRIVERID_EXP1 0xF1 +@@ -102,6 +118,8 @@ + + #define I2C_DRIVERID_I2CDEV 900 + #define I2C_DRIVERID_I2CPROC 901 ++#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ ++#define I2C_DRIVERID_ALERT 903 /* SMBus Alert Responder Client */ + + /* IDs -- Use DRIVERIDs 1000-1999 for sensors. + These were originally in sensors.h in the lm_sensors package */ +@@ -131,6 +149,28 @@ + #define I2C_DRIVERID_ADM1024 1025 + #define I2C_DRIVERID_IT87 1026 + #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */ ++#define I2C_DRIVERID_FSCPOS 1028 ++#define I2C_DRIVERID_FSCSCY 1029 ++#define I2C_DRIVERID_PCF8591 1030 ++#define I2C_DRIVERID_SMSC47M1 1031 ++#define I2C_DRIVERID_VT1211 1032 ++#define I2C_DRIVERID_LM92 1033 ++#define I2C_DRIVERID_VT8231 1034 ++#define I2C_DRIVERID_SMARTBATT 1035 ++#define I2C_DRIVERID_BMCSENSORS 1036 ++#define I2C_DRIVERID_FS451 1037 ++#define I2C_DRIVERID_W83627HF 1038 ++#define I2C_DRIVERID_LM85 1039 ++#define I2C_DRIVERID_LM83 1040 ++#define I2C_DRIVERID_SAA1064 1041 ++#define I2C_DRIVERID_LM90 1042 ++#define I2C_DRIVERID_ASB100 1043 ++#define I2C_DRIVERID_MAX6650 1044 ++#define I2C_DRIVERID_XEONTEMP 1045 ++#define I2C_DRIVERID_FSCHER 1046 ++#define I2C_DRIVERID_W83L785TS 1047 ++#define I2C_DRIVERID_ADM1026 1048 ++#define I2C_DRIVERID_LM93 1049 + + /* + * ---- Adapter types ---------------------------------------------------- +@@ -147,16 +187,21 @@ + #define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */ + #define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */ + #define I2C_ALGO_ACB 0x070000 /* ACCESS.bus algorithm */ +- ++#define I2C_ALGO_IIC 0x080000 /* ITE IIC bus */ ++#define I2C_ALGO_SAA7134 0x090000 ++#define I2C_ALGO_MPC824X 0x0a0000 /* Motorola 8240 / 8245 */ ++#define I2C_ALGO_IPMI 0x0b0000 /* IPMI dummy adapter */ ++#define I2C_ALGO_IPMB 0x0c0000 /* IPMB adapter */ ++#define I2C_ALGO_MPC107 0x0d0000 + #define I2C_ALGO_EC 0x100000 /* ACPI embedded controller */ + + #define I2C_ALGO_MPC8XX 0x110000 /* MPC8xx PowerPC I2C algorithm */ +- +-#define I2C_ALGO_SIBYTE 0x120000 /* Broadcom SiByte SOCs */ +- +-#define I2C_ALGO_SGI 0x130000 /* SGI algorithm */ +- +-#define I2C_ALGO_AU1550 0x140000 /* Alchemy Au1550 PSC */ ++#define I2C_ALGO_OCP 0x120000 /* IBM or otherwise On-chip I2C algorithm */ ++#define I2C_ALGO_BITHS 0x130000 /* enhanced bit style adapters */ ++#define I2C_ALGO_OCP_IOP3XX 0x140000 /* XSCALE IOP3XX On-chip I2C alg */ ++#define I2C_ALGO_SIBYTE 0x150000 /* Broadcom SiByte SOCs */ ++#define I2C_ALGO_SGI 0x160000 /* SGI algorithm */ ++#define I2C_ALGO_USB 0x170000 /* USB algorithm */ + + #define I2C_ALGO_EXP 0x800000 /* experimental */ + +@@ -184,21 +229,46 @@ + #define I2C_HW_B_I810 0x0a /* Intel I810 */ + #define I2C_HW_B_VOO 0x0b /* 3dfx Voodoo 3 / Banshee */ + #define I2C_HW_B_PPORT 0x0c /* Primitive parallel port adapter */ ++#define I2C_HW_B_SAVG 0x0d /* Savage 4 */ ++#define I2C_HW_B_SCX200 0x0e /* Nat'l Semi SCx200 I2C */ + #define I2C_HW_B_RIVA 0x10 /* Riva based graphics cards */ + #define I2C_HW_B_IOC 0x11 /* IOC bit-wiggling */ + #define I2C_HW_B_TSUNA 0x12 /* DEC Tsunami chipset */ ++#define I2C_HW_B_FRODO 0x13 /* 2d3D, Inc. SA-1110 Development Board */ ++#define I2C_HW_B_OMAHA 0x14 /* Omaha I2C interface (ARM) */ ++#define I2C_HW_B_GUIDE 0x15 /* Guide bit-basher */ ++#define I2C_HW_B_IXP2000 0x16 /* GPIO on IXP2000 systems */ ++#define I2C_HW_B_IXP425 0x17 /* GPIO on IXP425 systems */ ++#define I2C_HW_B_S3VIA 0x18 /* S3Via ProSavage adapter */ ++#define I2C_HW_B_ZR36067 0x19 /* Zoran-36057/36067 based boards */ ++#define I2C_HW_B_PCILYNX 0x1a /* TI PCILynx I2C adapter */ + + /* --- PCF 8584 based algorithms */ + #define I2C_HW_P_LP 0x00 /* Parallel port interface */ + #define I2C_HW_P_ISA 0x01 /* generic ISA Bus inteface card */ + #define I2C_HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + ++/* --- USB based adapters */ ++#define I2C_HW_USB_USBVISION 0x00 ++ + /* --- ACPI Embedded controller algorithms */ + #define I2C_HW_ACPI_EC 0x00 + ++/* --- MPC824x PowerPC adapters */ ++#define I2C_HW_MPC824X 0x00 /* Motorola 8240 / 8245 */ ++ + /* --- MPC8xx PowerPC adapters */ + #define I2C_HW_MPC8XX_EPON 0x00 /* Eponymous MPC8xx I2C adapter */ + ++/* --- ITE based algorithms */ ++#define I2C_HW_I_IIC 0x00 /* controller on the ITE */ ++ ++/* --- PowerPC on-chip adapters */ ++#define I2C_HW_OCP 0x00 /* IBM on-chip I2C adapter */ ++ ++/* --- XSCALE on-chip adapters */ ++#define I2C_HW_IOP321 0x00 ++ + /* --- Broadcom SiByte adapters */ + #define I2C_HW_SIBYTE 0x00 + +@@ -206,9 +276,6 @@ + #define I2C_HW_SGI_VINO 0x00 + #define I2C_HW_SGI_MACE 0x01 + +-/* --- Au1550 PSC adapters */ +-#define I2C_HW_AU1550_PSC 0x00 +- + /* --- SMBus only adapters */ + #define I2C_HW_SMBUS_PIIX4 0x00 + #define I2C_HW_SMBUS_ALI15X3 0x01 +@@ -218,9 +285,27 @@ + #define I2C_HW_SMBUS_AMD756 0x05 + #define I2C_HW_SMBUS_SIS5595 0x06 + #define I2C_HW_SMBUS_ALI1535 0x07 ++#define I2C_HW_SMBUS_SIS630 0x08 ++#define I2C_HW_SMBUS_SIS645 0x09 ++#define I2C_HW_SMBUS_AMD8111 0x0a ++#define I2C_HW_SMBUS_SCX200 0x0b ++#define I2C_HW_SMBUS_NFORCE2 0x0c + #define I2C_HW_SMBUS_W9968CF 0x0d ++#define I2C_HW_SMBUS_OV511 0x0e /* OV511(+) USB 1.1 webcam ICs */ ++#define I2C_HW_SMBUS_OV518 0x0f /* OV518(+) USB 1.1 webcam ICs */ ++#define I2C_HW_SMBUS_OV519 0x10 /* OV519 USB 1.1 webcam IC */ ++#define I2C_HW_SMBUS_OVFX2 0x11 /* Cypress/OmniVision FX2 webcam */ + + /* --- ISA pseudo-adapter */ + #define I2C_HW_ISA 0x00 + +-#endif /* I2C_ID_H */ ++/* --- IPMI pseudo-adapter */ ++#define I2C_HW_IPMI 0x00 ++ ++/* --- IPMB adapter */ ++#define I2C_HW_IPMB 0x00 ++ ++/* --- MCP107 adapter */ ++#define I2C_HW_MPC107 0x00 ++ ++#endif /* LINUX_I2C_ID_H */ +--- linux-old/drivers/i2c/i2c-pcf-epp.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-pcf-epp.c Mon Dec 13 19:26:34 2004 +@@ -0,0 +1,281 @@ ++/* ------------------------------------------------------------------------- */ ++/* i2c-pcf-epp.c i2c-hw access for PCF8584 style EPP parallel port adapters */ ++/* ------------------------------------------------------------------------- */ ++/* Copyright (C) 1998-99 Hans Berglund ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* With some changes from Ryosuke Tajima */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct i2c_pcf_epp { ++ int pe_base; ++ int pe_irq; ++ int pe_clock; ++ int pe_own; ++} ; ++ ++#define DEFAULT_BASE 0x378 ++#define DEFAULT_IRQ 7 ++#define DEFAULT_CLOCK 0x1c ++#define DEFAULT_OWN 0x55 ++ ++static int base = 0; ++static int irq = 0; ++static int clock = 0; ++static int own = 0; ++static int i2c_debug=0; ++static struct i2c_pcf_epp gpe; ++static wait_queue_head_t pcf_wait; ++static int pcf_pending; ++static spinlock_t irq_driver_lock = SPIN_LOCK_UNLOCKED; ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) if (i2c_debug>=1) x ++#define DEB2(x) if (i2c_debug>=2) x ++#define DEB3(x) if (i2c_debug>=3) x ++#define DEBE(x) x /* error messages */ ++ ++/* --- Convenience defines for the EPP/SPP port: */ ++#define BASE ((struct i2c_pcf_epp *)(data))->pe_base ++// #define DATA BASE /* SPP data port */ ++#define STAT (BASE+1) /* SPP status port */ ++#define CTRL (BASE+2) /* SPP control port */ ++#define EADD (BASE+3) /* EPP address port */ ++#define EDAT (BASE+4) /* EPP data port */ ++ ++/* ----- local functions ---------------------------------------------- */ ++ ++static void pcf_epp_setbyte(void *data, int ctl, int val) ++{ ++ if (ctl) { ++ if (gpe.pe_irq > 0) { ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n", ++ val|I2C_PCF_ENI)); ++ // set A0 pin HIGH ++ outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); ++ // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL))); ++ // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT))); ++ ++ // EPP write data cycle ++ outb(val | I2C_PCF_ENI, EDAT); ++ } else { ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write control 0x%x\n", val)); ++ // set A0 pin HIGH ++ outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); ++ outb(val, CTRL); ++ } ++ } else { ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Write data 0x%x\n", val)); ++ // set A0 pin LO ++ outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL); ++ // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: CTRL port = 0x%x\n", inb(CTRL))); ++ // DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: STAT port = 0x%x\n", inb(STAT))); ++ outb(val, EDAT); ++ } ++} ++ ++static int pcf_epp_getbyte(void *data, int ctl) ++{ ++ int val; ++ ++ if (ctl) { ++ // set A0 pin HIGH ++ outb(inb(CTRL) | PARPORT_CONTROL_INIT, CTRL); ++ val = inb(EDAT); ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read control 0x%x\n", val)); ++ } else { ++ // set A0 pin LOW ++ outb(inb(CTRL) & ~PARPORT_CONTROL_INIT, CTRL); ++ val = inb(EDAT); ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: Read data 0x%x\n", val)); ++ } ++ return (val); ++} ++ ++static int pcf_epp_getown(void *data) ++{ ++ return (gpe.pe_own); ++} ++ ++ ++static int pcf_epp_getclock(void *data) ++{ ++ return (gpe.pe_clock); ++} ++ ++#if 0 ++static void pcf_epp_sleep(unsigned long timeout) ++{ ++ schedule_timeout( timeout * HZ); ++} ++#endif ++ ++static void pcf_epp_waitforpin(void) { ++ int timeout = 10; ++ ++ if (gpe.pe_irq > 0) { ++ spin_lock_irq(&irq_driver_lock); ++ if (pcf_pending == 0) { ++ interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ); ++ //udelay(100); ++ } else { ++ pcf_pending = 0; ++ } ++ spin_unlock_irq(&irq_driver_lock); ++ } else { ++ udelay(100); ++ } ++} ++ ++static void pcf_epp_handler(int this_irq, void *dev_id, struct pt_regs *regs) { ++ pcf_pending = 1; ++ wake_up_interruptible(&pcf_wait); ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: in interrupt handler.\n")); ++} ++ ++ ++static int pcf_epp_init(void *data) ++{ ++ if (check_region(gpe.pe_base, 5) < 0 ) { ++ ++ printk(KERN_WARNING "Could not request port region with base 0x%x\n", gpe.pe_base); ++ return -ENODEV; ++ } else { ++ request_region(gpe.pe_base, 5, "i2c (EPP parallel port adapter)"); ++ } ++ ++ DEB3(printk(KERN_DEBUG "i2c-pcf-epp.o: init status port = 0x%x\n", inb(0x379))); ++ ++ if (gpe.pe_irq > 0) { ++ if (request_irq(gpe.pe_irq, pcf_epp_handler, 0, "PCF8584", 0) < 0) { ++ printk(KERN_NOTICE "i2c-pcf-epp.o: Request irq%d failed\n", gpe.pe_irq); ++ gpe.pe_irq = 0; ++ } else ++ disable_irq(gpe.pe_irq); ++ enable_irq(gpe.pe_irq); ++ } ++ // EPP mode initialize ++ // enable interrupt from nINTR pin ++ outb(inb(CTRL)|0x14, CTRL); ++ // clear ERROR bit of STAT ++ outb(inb(STAT)|0x01, STAT); ++ outb(inb(STAT)&~0x01,STAT); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ ++ * Encapsulate the above functions in the correct operations structure. ++ * This is only done when more than one hardware adapter is supported. ++ */ ++static struct i2c_algo_pcf_data pcf_epp_data = { ++ .setpcf = pcf_epp_setbyte, ++ .getpcf = pcf_epp_getbyte, ++ .getown = pcf_epp_getown, ++ .getclock = pcf_epp_getclock, ++ .waitforpin = pcf_epp_waitforpin, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ, ++}; ++ ++static struct i2c_adapter pcf_epp_ops = { ++ .owner = THIS_MODULE, ++ .name = "PCF8584 EPP adapter", ++ .id = I2C_HW_P_LP, ++ .algo_data = &pcf_epp_data, ++}; ++ ++static int __init i2c_pcfepp_init(void) ++{ ++ struct i2c_pcf_epp *pepp = &gpe; ++ ++ printk(KERN_DEBUG "i2c-pcf-epp.o: i2c pcf8584-epp adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); ++ if (base == 0) ++ pepp->pe_base = DEFAULT_BASE; ++ else ++ pepp->pe_base = base; ++ ++ if (irq == 0) ++ pepp->pe_irq = DEFAULT_IRQ; ++ else if (irq<0) { ++ // switch off irq ++ pepp->pe_irq=0; ++ } else { ++ pepp->pe_irq = irq; ++ } ++ if (clock == 0) ++ pepp->pe_clock = DEFAULT_CLOCK; ++ else ++ pepp->pe_clock = clock; ++ ++ if (own == 0) ++ pepp->pe_own = DEFAULT_OWN; ++ else ++ pepp->pe_own = own; ++ ++ pcf_epp_data.data = (void *)pepp; ++ init_waitqueue_head(&pcf_wait); ++ if (pcf_epp_init(pepp) == 0) { ++ int ret; ++ if ( (ret = i2c_pcf_add_bus(&pcf_epp_ops)) < 0) { ++ printk(KERN_WARNING "i2c_pcf_add_bus caused an error: %d\n",ret); ++ release_region(pepp->pe_base , 5); ++ return ret; ++ } ++ } else { ++ ++ return -ENODEV; ++ } ++ printk(KERN_DEBUG "i2c-pcf-epp.o: found device at %#x.\n", pepp->pe_base); ++ return 0; ++} ++ ++static void __exit pcf_epp_exit(void) ++{ ++ i2c_pcf_del_bus(&pcf_epp_ops); ++ if (gpe.pe_irq > 0) { ++ disable_irq(gpe.pe_irq); ++ free_irq(gpe.pe_irq, 0); ++ } ++ release_region(gpe.pe_base , 5); ++} ++ ++MODULE_AUTHOR("Hans Berglund \n modified by Ryosuke Tajima "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 EPP parallel port adapter"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(base, "i"); ++MODULE_PARM(irq, "i"); ++MODULE_PARM(clock, "i"); ++MODULE_PARM(own, "i"); ++MODULE_PARM(i2c_debug, "i"); ++ ++module_init(i2c_pcfepp_init); ++module_exit(pcf_epp_exit); +--- linux-old/include/linux/i2c-pcf8584.h Thu Jan 1 00:00:00 1970 ++++ linux/include/linux/i2c-pcf8584.h Mon Dec 13 19:26:34 2004 +@@ -0,0 +1,78 @@ ++/* -------------------------------------------------------------------- */ ++/* i2c-pcf8584.h: PCF 8584 global defines */ ++/* -------------------------------------------------------------------- */ ++/* Copyright (C) 1996 Simon G. Vogl ++ 1999 Hans Berglund ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* -------------------------------------------------------------------- */ ++ ++/* With some changes from Frodo Looijaard */ ++ ++/* $Id: i2c-pcf8584.h,v 1.6 2003/07/25 07:56:42 khali Exp $ */ ++ ++#ifndef _LINUX_I2C_PCF8584_H ++#define _LINUX_I2C_PCF8584_H ++ ++/* ----- Control register bits ---------------------------------------- */ ++#define I2C_PCF_PIN 0x80 ++#define I2C_PCF_ESO 0x40 ++#define I2C_PCF_ES1 0x20 ++#define I2C_PCF_ES2 0x10 ++#define I2C_PCF_ENI 0x08 ++#define I2C_PCF_STA 0x04 ++#define I2C_PCF_STO 0x02 ++#define I2C_PCF_ACK 0x01 ++ ++#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) ++#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) ++#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) ++#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) ++ ++/* ----- Status register bits ----------------------------------------- */ ++/*#define I2C_PCF_PIN 0x80 as above*/ ++ ++#define I2C_PCF_INI 0x40 /* 1 if not initialized */ ++#define I2C_PCF_STS 0x20 ++#define I2C_PCF_BER 0x10 ++#define I2C_PCF_AD0 0x08 ++#define I2C_PCF_LRB 0x08 ++#define I2C_PCF_AAS 0x04 ++#define I2C_PCF_LAB 0x02 ++#define I2C_PCF_BB 0x01 ++ ++/* ----- Chip clock frequencies --------------------------------------- */ ++#define I2C_PCF_CLK3 0x00 ++#define I2C_PCF_CLK443 0x10 ++#define I2C_PCF_CLK6 0x14 ++#define I2C_PCF_CLK 0x18 ++#define I2C_PCF_CLK12 0x1c ++ ++/* ----- transmission frequencies ------------------------------------- */ ++#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ ++#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ ++#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ ++#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ ++ ++ ++/* ----- Access to internal registers according to ES1,ES2 ------------ */ ++/* they are mapped to the data port ( a0 = 0 ) */ ++/* available when ESO == 0 : */ ++ ++#define I2C_PCF_OWNADR 0 ++#define I2C_PCF_INTREG I2C_PCF_ES2 ++#define I2C_PCF_CLKREG I2C_PCF_ES1 ++ ++#endif /* _LINUX_I2C_PCF8584_H */ +--- linux-old/drivers/i2c/i2c-philips-par.c Fri Feb 20 01:22:16 2004 ++++ linux/drivers/i2c/i2c-philips-par.c Mon Dec 13 19:26:34 2004 +@@ -21,7 +21,7 @@ + /* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +-/* $Id: i2c-philips-par.c,v 1.18 2000/07/06 19:21:49 frodo Exp $ */ ++/* $Id: i2c-philips-par.c,v 1.33 2004/01/23 20:22:53 khali Exp $ */ + + #include + #include +@@ -29,14 +29,10 @@ + #include + #include + #include +- ++#include + #include + #include + +-#ifndef __exit +-#define __exit __init +-#endif +- + static int type; + + struct i2c_par +@@ -130,59 +126,34 @@ + PARPORT_STATUS_BUSY) ? 0 : 1; + } + +-static int bit_lp_reg(struct i2c_client *client) +-{ +- return 0; +-} +- +-static int bit_lp_unreg(struct i2c_client *client) +-{ +- return 0; +-} +- +-static void bit_lp_inc_use(struct i2c_adapter *adap) +-{ +- MOD_INC_USE_COUNT; +-} +- +-static void bit_lp_dec_use(struct i2c_adapter *adap) +-{ +- MOD_DEC_USE_COUNT; +-} +- + /* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + + static struct i2c_algo_bit_data bit_lp_data = { +- NULL, +- bit_lp_setsda, +- bit_lp_setscl, +- bit_lp_getsda, +- bit_lp_getscl, +- 80, 80, 100, /* waits, timeout */ ++ .setsda = bit_lp_setsda, ++ .setscl = bit_lp_setscl, ++ .getsda = bit_lp_getsda, ++ .getscl = bit_lp_getscl, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ + }; + + static struct i2c_algo_bit_data bit_lp_data2 = { +- NULL, +- bit_lp_setsda2, +- bit_lp_setscl2, +- bit_lp_getsda2, +- NULL, +- 80, 80, 100, /* waits, timeout */ ++ .setsda = bit_lp_setsda2, ++ .setscl = bit_lp_setscl2, ++ .getsda = bit_lp_getsda2, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ + }; + + static struct i2c_adapter bit_lp_ops = { +- "Philips Parallel port adapter", +- I2C_HW_B_LP, +- NULL, +- NULL, +- bit_lp_inc_use, +- bit_lp_dec_use, +- bit_lp_reg, +- +- bit_lp_unreg, ++ .owner = THIS_MODULE, ++ .name = "Philips Parallel port adapter", ++ .id = I2C_HW_B_LP, + }; + + static void i2c_parport_attach (struct parport *port) +@@ -202,6 +173,7 @@ + NULL); + if (!adapter->pdev) { + printk(KERN_ERR "i2c-philips-par: Unable to register with parport.\n"); ++ kfree(adapter); + return; + } + +@@ -210,8 +182,12 @@ + adapter->bit_lp_data = type ? bit_lp_data2 : bit_lp_data; + adapter->bit_lp_data.data = port; + ++ if (parport_claim_or_block(adapter->pdev) < 0 ) { ++ printk(KERN_ERR "i2c-philips-par: Could not claim parallel port.\n"); ++ kfree(adapter); ++ return; ++ } + /* reset hardware to sane state */ +- parport_claim_or_block(adapter->pdev); + adapter->bit_lp_data.setsda(port, 1); + adapter->bit_lp_data.setscl(port, 1); + parport_release(adapter->pdev); +@@ -257,7 +233,7 @@ + NULL + }; + +-int __init i2c_bitlp_init(void) ++static int __init i2c_bitlp_init(void) + { + printk(KERN_INFO "i2c-philips-par.o: i2c Philips parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + +@@ -266,7 +242,7 @@ + return 0; + } + +-void __exit i2c_bitlp_exit(void) ++static void __exit i2c_bitlp_exit(void) + { + parport_unregister_driver(&i2c_driver); + } +@@ -279,14 +255,5 @@ + + MODULE_PARM(type, "i"); + +-#ifdef MODULE +-int init_module(void) +-{ +- return i2c_bitlp_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bitlp_exit(); +-} +-#endif ++module_init(i2c_bitlp_init); ++module_exit(i2c_bitlp_exit); +--- linux-old/drivers/i2c/i2c-pport.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-pport.c Mon Dec 13 19:26:34 2004 +@@ -0,0 +1,169 @@ ++/* ------------------------------------------------------------------------- */ ++/* i2c-pport.c i2c-hw access for primitive i2c par. port adapter */ ++/* ------------------------------------------------------------------------- */ ++/* Copyright (C) 2001 Daniel Smolik ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* ++ See doc/i2c-pport for instructions on wiring to the ++ parallel port connector. ++ ++ Cut & paste :-) based on Velleman K8000 driver by Simon G. Vogl ++ ++ Note that SDA is hardware inverted. ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define DEFAULT_BASE 0x378 ++static int base = DEFAULT_BASE; ++static unsigned char PortData = 0; ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) /* should be reasonable open, close &c. */ ++#define DEB2(x) /* low level debugging - very slow */ ++#define DEBE(x) x /* error messages */ ++#define DEBINIT(x) x /* detection status messages */ ++ ++/* --- Convenience defines for the parallel port: */ ++#define BASE (unsigned int)(data) ++#define DATA BASE /* Centronics data port */ ++#define STAT (BASE+1) /* Centronics status port */ ++#define CTRL (BASE+2) /* Centronics control port */ ++ ++/* we will use SDA - Auto Linefeed(14) POUT (ctrl bit 1) */ ++/* we will use SCL - Initialize printer(16) BUSY (ctrl bit 2) */ ++ ++#define SET_SCL | 0x04 ++#define CLR_SCL & 0xFB ++ ++#define SET_SDA & 0xFD ++#define CLR_SDA | 0x02 ++ ++ ++/* ----- local functions ---------------------------------------------- */ ++ ++ ++static void bit_pport_setscl(void *data, int state) ++{ ++ if (state) { ++ PortData = PortData SET_SCL; ++ } else { ++ PortData = PortData CLR_SCL; ++ } ++ outb(PortData, CTRL); ++} ++ ++static void bit_pport_setsda(void *data, int state) ++{ ++ if (state) { ++ PortData = PortData SET_SDA; ++ } else { ++ PortData = PortData CLR_SDA; ++ } ++ outb(PortData, CTRL); ++} ++ ++static int bit_pport_getscl(void *data) ++{ ++ return ( 4 == ( (inb_p(CTRL)) & 0x04 ) ); ++} ++ ++static int bit_pport_getsda(void *data) ++{ ++ return ( 0 == ( (inb_p(CTRL)) & 0x02 ) ); ++} ++ ++static int bit_pport_init(void) ++{ ++ if (!request_region((base+2),1, "i2c (PPORT adapter)")) { ++ return -ENODEV; ++ } ++ ++ PortData = inb(base+2); ++ bit_pport_setsda((void*)base, 1); ++ bit_pport_setscl((void*)base, 1); ++ ++ return 0; ++} ++ ++ ++/* ------------------------------------------------------------------------ ++ * Encapsulate the above functions in the correct operations structure. ++ * This is only done when more than one hardware adapter is supported. ++ */ ++static struct i2c_algo_bit_data bit_pport_data = { ++ .setsda = bit_pport_setsda, ++ .setscl = bit_pport_setscl, ++ .getsda = bit_pport_getsda, ++ .getscl = bit_pport_getscl, ++ .udelay = 40, ++ .mdelay = 80, ++ .timeout = HZ ++}; ++ ++static struct i2c_adapter bit_pport_ops = { ++ .owner = THIS_MODULE, ++ .name = "Primitive Parallel port adaptor", ++ .id = I2C_HW_B_PPORT, ++ .algo_data = &bit_pport_data, ++}; ++ ++static int __init i2c_bitpport_init(void) ++{ ++ printk("i2c-pport.o: i2c Primitive parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); ++ ++ bit_pport_data.data = (void*)base; ++ if (bit_pport_init() < 0) ++ return -ENODEV; ++ ++ if (i2c_bit_add_bus(&bit_pport_ops) < 0) { ++ release_region(base+2, 1); ++ return -ENODEV; ++ } ++ ++ printk("i2c-pport.o: found device at %#x.\n",base); ++ return 0; ++} ++ ++static void __exit i2c_bitpport_exit(void) ++{ ++ i2c_bit_del_bus(&bit_pport_ops); ++ release_region((base+2),1); ++} ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("Daniel Smolik "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for Primitive parallel port adapter"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(base, "i"); ++ ++module_init(i2c_bitpport_init); ++module_exit(i2c_bitpport_exit); +--- linux-old/drivers/i2c/i2c-proc.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-proc.c Mon Dec 13 19:26:35 2004 +@@ -23,29 +23,26 @@ + This driver puts entries in /proc/sys/dev/sensors for each I2C device + */ + ++#include + #include + #include + #include + #include + #include + #include ++#include + #include +-#include +- + #include + #include ++#include + +-#include +- +-#ifndef THIS_MODULE +-#define THIS_MODULE NULL ++#ifndef CONFIG_SYSCTL ++#error Your kernel lacks sysctl support (CONFIG_SYSCTL)! + #endif + +-static int i2c_create_name(char **name, const char *prefix, +- struct i2c_adapter *adapter, int addr); + static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude); +-static int i2c_write_reals(int nrels, void *buffer, int *bufsize, ++static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, + long *results, int magnitude); + static int i2c_proc_chips(ctl_table * ctl, int write, + struct file *filp, void *buffer, +@@ -55,22 +52,10 @@ + void *newval, size_t newlen, + void **context); + +-int __init sensors_init(void); +- + #define SENSORS_ENTRY_MAX 20 + static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; + + static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; +-static unsigned short i2c_inodes[SENSORS_ENTRY_MAX]; +- +-static ctl_table sysctl_table[] = { +- {CTL_DEV, "dev", NULL, 0, 0555}, +- {0}, +- {DEV_SENSORS, "sensors", NULL, 0, 0555}, +- {0}, +- {0, NULL, NULL, 0, 0555}, +- {0} +-}; + + static ctl_table i2c_proc_dev_sensors[] = { + {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, +@@ -91,31 +76,45 @@ + + + static struct ctl_table_header *i2c_proc_header; +-static int i2c_initialized; + + /* This returns a nice name for a new directory; for example lm78-isa-0310 + (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for + a LM75 chip on the third i2c bus at address 0x4e). + name is allocated first. */ +-int i2c_create_name(char **name, const char *prefix, +- struct i2c_adapter *adapter, int addr) ++static char *generate_name(struct i2c_client *client, const char *prefix) + { +- char name_buffer[50]; +- int id; +- if (i2c_is_isa_adapter(adapter)) ++ struct i2c_adapter *adapter = client->adapter; ++ int addr = client->addr; ++ char name_buffer[50], *name; ++ ++ if (i2c_is_isa_adapter(adapter)) { + sprintf(name_buffer, "%s-isa-%04x", prefix, addr); +- else { +- if ((id = i2c_adapter_id(adapter)) < 0) +- return -ENOENT; ++ } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) { ++ int id = i2c_adapter_id(adapter); ++ if (id < 0) ++ return ERR_PTR(-ENOENT); + sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); ++ } else { /* dummy adapter, generate prefix */ ++ int end, i; ++ ++ sprintf(name_buffer, "%s-", prefix); ++ end = strlen(name_buffer); ++ ++ for (i = 0; i < 32; i++) { ++ if (adapter->algo->name[i] == ' ') ++ break; ++ name_buffer[end++] = tolower(adapter->algo->name[i]); ++ } ++ ++ name_buffer[end] = 0; ++ sprintf(name_buffer + end, "-%04x", addr); + } +- *name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); +- if (!*name) { +- printk (KERN_WARNING "i2c_create_name: not enough memory\n"); +- return -ENOMEM; +- } +- strcpy(*name, name_buffer); +- return 0; ++ ++ name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); ++ if (!name) ++ return ERR_PTR(-ENOMEM); ++ strcpy(name, name_buffer); ++ return name; + } + + /* This rather complex function must be called when you want to add an entry +@@ -124,139 +123,91 @@ + ctl_template should be a template of the newly created directory. It is + copied in memory. The extra2 field of each file is set to point to client. + If any driver wants subdirectories within the newly created directory, +- this function must be updated! +- controlling_mod is the controlling module. It should usually be +- THIS_MODULE when calling. Note that this symbol is not defined in +- kernels before 2.3.13; define it to NULL in that case. We will not use it +- for anything older than 2.3.27 anyway. */ ++ this function must be updated! */ + int i2c_register_entry(struct i2c_client *client, const char *prefix, +- ctl_table * ctl_template, +- struct module *controlling_mod) ++ struct ctl_table *ctl_template) + { +- int i, res, len, id; +- ctl_table *new_table; +- char *name; +- struct ctl_table_header *new_header; +- +- if ((res = i2c_create_name(&name, prefix, client->adapter, +- client->addr))) return res; ++ struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl; ++ struct ctl_table_header *hdr; ++ struct ctl_table *tmp, *leaf; ++ const char *name; ++ int id, len = 0; + +- for (id = 0; id < SENSORS_ENTRY_MAX; id++) +- if (!i2c_entries[id]) { +- break; +- } +- if (id == SENSORS_ENTRY_MAX) { +- kfree(name); +- return -ENOMEM; +- } +- id += 256; ++ name = generate_name(client, prefix); ++ if (IS_ERR(name)) ++ return PTR_ERR(name); + +- len = 0; +- while (ctl_template[len].procname) +- len++; +- len += 7; +- if (!(new_table = kmalloc(sizeof(ctl_table) * len, GFP_KERNEL))) { +- kfree(name); +- return -ENOMEM; +- } +- +- memcpy(new_table, sysctl_table, 6 * sizeof(ctl_table)); +- new_table[0].child = &new_table[2]; +- new_table[2].child = &new_table[4]; +- new_table[4].child = &new_table[6]; +- new_table[4].procname = name; +- new_table[4].ctl_name = id; +- memcpy(new_table + 6, ctl_template, (len - 6) * sizeof(ctl_table)); +- for (i = 6; i < len; i++) +- new_table[i].extra2 = client; +- +- if (!(new_header = register_sysctl_table(new_table, 0))) { +- kfree(new_table); +- kfree(name); +- return -ENOMEM; ++ for (id = 0; id < SENSORS_ENTRY_MAX; id++) { ++ if (!i2c_entries[id]) ++ goto free_slot; + } + +- i2c_entries[id - 256] = new_header; ++ goto out_free_name; + +- i2c_clients[id - 256] = client; +-#ifdef DEBUG +- if (!new_header || !new_header->ctl_table || +- !new_header->ctl_table->child || +- !new_header->ctl_table->child->child || +- !new_header->ctl_table->child->child->de) { +- printk +- ("i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n"); +- return id; +- } +-#endif /* DEBUG */ +- i2c_inodes[id - 256] = +- new_header->ctl_table->child->child->de->low_ino; +- new_header->ctl_table->child->child->de->owner = controlling_mod; +- +- return id; ++ free_slot: ++ while (ctl_template[len].ctl_name) ++ len++; ++ tbl = kmalloc(sizeof(*tbl) + sizeof(ctl_table) * (len + 1), ++ GFP_KERNEL); ++ if (!tbl) ++ goto out_free_name; ++ memset(tbl, 0, sizeof(*tbl)); ++ ++ /* The client sysctls */ ++ leaf = (struct ctl_table *) (tbl + 1); ++ memcpy(leaf, ctl_template, sizeof(ctl_table) * (len+1)); ++ for (tmp = leaf; tmp->ctl_name; tmp++) ++ tmp->extra2 = client; ++ ++ tbl->sensors->ctl_name = id+256; ++ tbl->sensors->procname = name; ++ tbl->sensors->mode = 0555; ++ tbl->sensors->child = leaf; ++ ++ tbl->dev->ctl_name = DEV_SENSORS; ++ tbl->dev->procname = "sensors"; ++ tbl->dev->mode = 0555; ++ tbl->dev->child = tbl->sensors; ++ ++ tbl->root->ctl_name = CTL_DEV; ++ tbl->root->procname = "dev"; ++ tbl->root->mode = 0555; ++ tbl->root->child = tbl->dev; ++ ++ hdr = register_sysctl_table(tbl->root, 0); ++ if (!hdr) ++ goto out_free_tbl; ++ ++ i2c_entries[id] = hdr; ++ i2c_clients[id] = client; ++ ++ return (id + 256); /* XXX(hch) why?? */ ++ ++ out_free_tbl: ++ kfree(tbl); ++ out_free_name: ++ kfree(name); ++ return -ENOMEM; + } + + void i2c_deregister_entry(int id) + { +- ctl_table *table; +- char *temp; + id -= 256; +- if (i2c_entries[id]) { +- table = i2c_entries[id]->ctl_table; +- unregister_sysctl_table(i2c_entries[id]); +- /* 2-step kfree needed to keep gcc happy about const points */ +- (const char *) temp = table[4].procname; +- kfree(temp); +- kfree(table); +- i2c_entries[id] = NULL; +- i2c_clients[id] = NULL; +- } +-} + +-/* Monitor access for /proc/sys/dev/sensors; make unloading i2c-proc.o +- impossible if some process still uses it or some file in it */ +-void i2c_fill_inode(struct inode *inode, int fill) +-{ +- if (fill) +- MOD_INC_USE_COUNT; +- else +- MOD_DEC_USE_COUNT; +-} +- +-/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading +- the corresponding module impossible if some process still uses it or +- some file in it */ +-void i2c_dir_fill_inode(struct inode *inode, int fill) +-{ +- int i; +- struct i2c_client *client; ++ if (i2c_entries[id]) { ++ struct ctl_table_header *hdr = i2c_entries[id]; ++ struct ctl_table *tbl = hdr->ctl_table; + +-#ifdef DEBUG +- if (!inode) { +- printk("i2c-proc.o: Warning: inode NULL in fill_inode()\n"); +- return; ++ unregister_sysctl_table(hdr); ++ kfree(tbl->child->child->procname); ++ kfree(tbl); /* actually the whole anonymous struct */ + } +-#endif /* def DEBUG */ + +- for (i = 0; i < SENSORS_ENTRY_MAX; i++) +- if (i2c_clients[i] +- && (i2c_inodes[i] == inode->i_ino)) break; +-#ifdef DEBUG +- if (i == SENSORS_ENTRY_MAX) { +- printk +- ("i2c-proc.o: Warning: inode (%ld) not found in fill_inode()\n", +- inode->i_ino); +- return; +- } +-#endif /* def DEBUG */ +- client = i2c_clients[i]; +- if (fill) +- client->driver->inc_use(client); +- else +- client->driver->dec_use(client); ++ i2c_entries[id] = NULL; ++ i2c_clients[id] = NULL; + } + +-int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, ++static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, + void *buffer, size_t * lenp) + { + char BUF[SENSORS_PREFIX_MAX + 30]; +@@ -294,7 +245,7 @@ + return 0; + } + +-int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, ++static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen, + void *oldval, size_t * oldlenp, void *newval, + size_t newlen, void **context) + { +@@ -456,7 +407,7 @@ + WARNING! This is tricky code. I have tested it, but there may still be + hidden bugs in it, even leading to crashes and things! + */ +-int i2c_parse_reals(int *nrels, void *buffer, int bufsize, ++static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, + long *results, int magnitude) + { + int maxels, min, mag; +@@ -557,7 +508,7 @@ + return 0; + } + +-int i2c_write_reals(int nrels, void *buffer, int *bufsize, ++static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, + long *results, int magnitude) + { + #define BUFLEN 20 +@@ -646,6 +597,7 @@ + I2C_FUNC_SMBUS_QUICK)) return -1; + + for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { ++ /* XXX: WTF is going on here??? */ + if ((is_isa && check_region(addr, 1)) || + (!is_isa && i2c_check_addr(adapter, addr))) + continue; +@@ -846,46 +798,33 @@ + return 0; + } + +-int __init sensors_init(void) ++static int __init i2c_proc_init(void) + { + printk(KERN_INFO "i2c-proc.o version %s (%s)\n", I2C_VERSION, I2C_DATE); +- i2c_initialized = 0; + if (! + (i2c_proc_header = +- register_sysctl_table(i2c_proc, 0))) return -ENOMEM; ++ register_sysctl_table(i2c_proc, 0))) { ++ printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); ++ return -EPERM; ++ } + i2c_proc_header->ctl_table->child->de->owner = THIS_MODULE; +- i2c_initialized++; + return 0; + } + ++static void __exit i2c_proc_exit(void) ++{ ++ unregister_sysctl_table(i2c_proc_header); ++} ++ ++EXPORT_SYMBOL(i2c_register_entry); + EXPORT_SYMBOL(i2c_deregister_entry); +-EXPORT_SYMBOL(i2c_detect); + EXPORT_SYMBOL(i2c_proc_real); +-EXPORT_SYMBOL(i2c_register_entry); + EXPORT_SYMBOL(i2c_sysctl_real); +- +-#ifdef MODULE ++EXPORT_SYMBOL(i2c_detect); + + MODULE_AUTHOR("Frodo Looijaard "); + MODULE_DESCRIPTION("i2c-proc driver"); + MODULE_LICENSE("GPL"); + +-int i2c_cleanup(void) +-{ +- if (i2c_initialized >= 1) { +- unregister_sysctl_table(i2c_proc_header); +- i2c_initialized--; +- } +- return 0; +-} +- +-int init_module(void) +-{ +- return sensors_init(); +-} +- +-int cleanup_module(void) +-{ +- return i2c_cleanup(); +-} +-#endif /* MODULE */ ++module_init(i2c_proc_init); ++module_exit(i2c_proc_exit); +--- linux-old/include/linux/i2c-proc.h Tue Nov 6 00:55:29 2001 ++++ linux/include/linux/i2c-proc.h Mon Dec 13 19:26:36 2004 +@@ -1,6 +1,7 @@ + /* +- sensors.h - Part of lm_sensors, Linux kernel modules for hardware +- monitoring ++ i2c-proc.h - Part of the i2c package ++ was originally sensors.h - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify +@@ -18,14 +19,9 @@ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +-#ifndef SENSORS_SENSORS_H +-#define SENSORS_SENSORS_H ++#ifndef _LINUX_I2C_PROC_H ++#define _LINUX_I2C_PROC_H + +-#ifdef __KERNEL__ +- +-/* Next two must be included before sysctl.h can be included, in 2.0 kernels */ +-#include +-#include + #include + + /* The type of callback functions used in sensors_{proc,sysctl}_real */ +@@ -73,8 +69,7 @@ + these functions must be updated! */ + extern int i2c_register_entry(struct i2c_client *client, + const char *prefix, +- ctl_table * ctl_template, +- struct module *controlling_mod); ++ ctl_table * ctl_template); + + extern void i2c_deregister_entry(int id); + +@@ -347,6 +342,31 @@ + {NULL}}; \ + SENSORS_INSMOD + ++#define SENSORS_INSMOD_8(chip1,chip2,chip3,chip4,chip5,chip6,chip7,chip8) \ ++ enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8 }; \ ++ SENSORS_MODULE_PARM(force, \ ++ "List of adapter,address pairs to boldly assume " \ ++ "to be present"); \ ++ SENSORS_MODULE_PARM_FORCE(chip1); \ ++ SENSORS_MODULE_PARM_FORCE(chip2); \ ++ SENSORS_MODULE_PARM_FORCE(chip3); \ ++ SENSORS_MODULE_PARM_FORCE(chip4); \ ++ SENSORS_MODULE_PARM_FORCE(chip5); \ ++ SENSORS_MODULE_PARM_FORCE(chip6); \ ++ SENSORS_MODULE_PARM_FORCE(chip7); \ ++ SENSORS_MODULE_PARM_FORCE(chip8); \ ++ static struct i2c_force_data forces[] = {{force,any_chip}, \ ++ {force_ ## chip1,chip1}, \ ++ {force_ ## chip2,chip2}, \ ++ {force_ ## chip3,chip3}, \ ++ {force_ ## chip4,chip4}, \ ++ {force_ ## chip5,chip5}, \ ++ {force_ ## chip6,chip6}, \ ++ {force_ ## chip7,chip7}, \ ++ {force_ ## chip8,chip8}, \ ++ {NULL}}; \ ++ SENSORS_INSMOD ++ + typedef int i2c_found_addr_proc(struct i2c_adapter *adapter, + int addr, unsigned short flags, + int kind); +@@ -362,7 +382,7 @@ + + /* This macro is used to scale user-input to sensible values in almost all + chip drivers. */ +-extern inline int SENSORS_LIMIT(long value, long low, long high) ++static inline int SENSORS_LIMIT(long value, long low, long high) + { + if (value < low) + return low; +@@ -372,8 +392,6 @@ + return value; + } + +-#endif /* def __KERNEL__ */ +- + + /* The maximum length of the prefix */ + #define SENSORS_PREFIX_MAX 20 +@@ -392,5 +410,5 @@ + char name[SENSORS_PREFIX_MAX + 13]; + }; + +-#endif /* def SENSORS_SENSORS_H */ ++#endif /* def _LINUX_I2C_PROC_H */ + +--- linux-old/drivers/i2c/i2c-rpx.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-rpx.c Mon Dec 13 19:26:36 2004 +@@ -0,0 +1,101 @@ ++/* ++ * Embedded Planet RPX Lite MPC8xx CPM I2C interface. ++ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). ++ * ++ * moved into proper i2c interface; ++ * Brad Parker (brad@heeltoe.com) ++ * ++ * RPX lite specific parts of the i2c interface ++ * Update: There actually isn't anything RPXLite-specific about this module. ++ * This should work for most any 8xx board. The console messages have been ++ * changed to eliminate RPXLite references. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static void ++rpx_iic_init(struct i2c_algo_8xx_data *data) ++{ ++ volatile cpm8xx_t *cp; ++ volatile immap_t *immap; ++ ++ cp = cpmp; /* Get pointer to Communication Processor */ ++ immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ ++ ++ data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; ++ ++ /* Check for and use a microcode relocation patch. ++ */ ++ if ((data->reloc = data->iip->iic_rpbase)) ++ data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase]; ++ ++ data->i2c = (i2c8xx_t *)&(immap->im_i2c); ++ data->cp = cp; ++ ++ /* Initialize Port B IIC pins. ++ */ ++ cp->cp_pbpar |= 0x00000030; ++ cp->cp_pbdir |= 0x00000030; ++ cp->cp_pbodr |= 0x00000030; ++ ++ /* Allocate space for two transmit and two receive buffer ++ * descriptors in the DP ram. ++ */ ++ data->dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 4); ++ ++ /* ptr to i2c area */ ++ data->i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c); ++} ++ ++static int rpx_install_isr(int irq, void (*func)(void *, void *), void *data) ++{ ++ /* install interrupt handler */ ++ cpm_install_handler(irq, (void (*)(void *, struct pt_regs *)) func, data); ++ ++ return 0; ++} ++ ++static struct i2c_algo_8xx_data rpx_data = { ++ .setisr = rpx_install_isr ++}; ++ ++static struct i2c_adapter rpx_ops = { ++ .owner = THIS_MODULE, ++ .name = "m8xx", ++ .id = I2C_HW_MPC8XX_EPON, ++ .algo_data = &rpx_data, ++}; ++ ++static int __init i2c_rpx_init(void) ++{ ++ printk("i2c-rpx.o: i2c MPC8xx module version %s (%s)\n", I2C_VERSION, I2C_DATE); ++ ++ /* reset hardware to sane state */ ++ rpx_iic_init(&rpx_data); ++ ++ if (i2c_8xx_add_bus(&rpx_ops) < 0) { ++ printk("i2c-rpx: Unable to register with I2C\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void __exit i2c_rpx_exit(void) ++{ ++ i2c_8xx_del_bus(&rpx_ops); ++} ++ ++MODULE_AUTHOR("Dan Malek "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards"); ++ ++module_init(i2c_rpx_init); ++module_exit(i2c_rpx_exit); +--- linux-old/drivers/i2c/i2c-velleman.c Tue Jan 20 15:10:31 2004 ++++ linux/drivers/i2c/i2c-velleman.c Mon Dec 13 19:26:36 2004 +@@ -18,18 +18,18 @@ + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + /* ------------------------------------------------------------------------- */ + +-/* $Id: i2c-velleman.c,v 1.19 2000/01/24 02:06:33 mds Exp $ */ ++/* $Id: i2c-velleman.c,v 1.33 2003/12/24 17:49:44 khali Exp $ */ + + #include + #include + #include + #include +-#include /* for 2.0 kernels to get NULL */ +-#include /* for 2.0 kernels to get ENODEV */ +-#include +- ++#include ++#include + #include + #include ++#include ++#include /* for HZ */ + + /* ----- global defines ----------------------------------------------- */ + #define DEB(x) /* should be reasonable open, close &c. */ +@@ -90,75 +90,38 @@ + + static int bit_velle_init(void) + { +- if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { +- DEBE(printk("i2c-velleman.o: Port %#x already in use.\n", +- base)); ++ if (!request_region(base, (base == 0x3bc) ? 3 : 8, ++ "i2c (Vellemann adapter)")) + return -ENODEV; +- } else { +- request_region(base, (base == 0x3bc)? 3 : 8, +- "i2c (Vellemann adapter)"); +- bit_velle_setsda((void*)base,1); +- bit_velle_setscl((void*)base,1); +- } +- return 0; +-} + +-static void __exit bit_velle_exit(void) +-{ +- release_region( base , (base == 0x3bc)? 3 : 8 ); +-} +- +- +-static int bit_velle_reg(struct i2c_client *client) +-{ +- return 0; +-} +- +-static int bit_velle_unreg(struct i2c_client *client) +-{ ++ bit_velle_setsda((void*)base,1); ++ bit_velle_setscl((void*)base,1); + return 0; + } + +-static void bit_velle_inc_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_INC_USE_COUNT; +-#endif +-} +- +-static void bit_velle_dec_use(struct i2c_adapter *adap) +-{ +-#ifdef MODULE +- MOD_DEC_USE_COUNT; +-#endif +-} +- + /* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + + static struct i2c_algo_bit_data bit_velle_data = { +- NULL, +- bit_velle_setsda, +- bit_velle_setscl, +- bit_velle_getsda, +- bit_velle_getscl, +- 10, 10, 100, /* waits, timeout */ ++ .setsda = bit_velle_setsda, ++ .setscl = bit_velle_setscl, ++ .getsda = bit_velle_getsda, ++ .getscl = bit_velle_getscl, ++ .udelay = 10, ++ .mdelay = 10, ++ .timeout = HZ + }; + + static struct i2c_adapter bit_velle_ops = { +- "Velleman K8000", +- I2C_HW_B_VELLE, +- NULL, +- &bit_velle_data, +- bit_velle_inc_use, +- bit_velle_dec_use, +- bit_velle_reg, +- bit_velle_unreg, ++ .owner = THIS_MODULE, ++ .name = "Velleman K8000", ++ .id = I2C_HW_B_VELLE, ++ .algo_data = &bit_velle_data, + }; + +-int __init i2c_bitvelle_init(void) ++static int __init i2c_bitvelle_init(void) + { + printk(KERN_INFO "i2c-velleman.o: i2c Velleman K8000 adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + if (base==0) { +@@ -184,24 +147,19 @@ + return 0; + } + ++static void __exit i2c_bitvelle_exit(void) ++{ ++ i2c_bit_del_bus(&bit_velle_ops); ++ release_region(base, (base == 0x3bc) ? 3 : 8); ++} ++ + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + MODULE_LICENSE("GPL"); + + MODULE_PARM(base, "i"); + +-int init_module(void) +-{ +- return i2c_bitvelle_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bit_del_bus(&bit_velle_ops); +- bit_velle_exit(); +-} +- +-#endif ++module_init(i2c_bitvelle_init); ++module_exit(i2c_bitvelle_exit); +--- linux-old/include/linux/i2c.h Tue Jan 20 15:10:34 2004 ++++ linux/include/linux/i2c.h Mon Dec 13 19:26:37 2004 +@@ -23,36 +23,33 @@ + /* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + +-/* $Id: i2c.h,v 1.46 2001/08/31 00:04:07 phil Exp $ */ ++/* $Id: i2c.h,v 1.80 2004/10/07 23:47:17 phil Exp $ */ + +-#ifndef I2C_H +-#define I2C_H ++#ifndef _LINUX_I2C_H ++#define _LINUX_I2C_H + +-#define I2C_DATE "20010830" +-#define I2C_VERSION "2.6.1" ++#define I2C_DATE "20041007" ++#define I2C_VERSION "2.8.8" + +-#include /* id values of adapters et. al. */ ++#include + #include +- +- +-struct i2c_msg; +- +- +-#ifdef __KERNEL__ +- +-/* --- Includes and compatibility declarations ------------------------ */ +- ++#include ++#include + #include +-#include ++#include ++ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) ++#define MODULE_LICENSE(x) ++#endif + + /* --- General options ------------------------------------------------ */ + +-#define I2C_ALGO_MAX 4 /* control memory consumption */ +-#define I2C_ADAP_MAX 16 ++#define I2C_ADAP_MAX 16 /* control memory consumption */ + #define I2C_DRIVER_MAX 16 + #define I2C_CLIENT_MAX 32 +-#define I2C_DUMMY_MAX 4 + ++struct i2c_msg; + struct i2c_algorithm; + struct i2c_adapter; + struct i2c_client; +@@ -60,7 +57,6 @@ + struct i2c_client_address_data; + union i2c_smbus_data; + +- + /* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to +@@ -113,6 +109,8 @@ + extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); ++extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, ++ u8 command, u8 *values); + extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); +@@ -125,6 +123,7 @@ + */ + + struct i2c_driver { ++ struct module *owner; + char name[32]; + int id; + unsigned int flags; /* div., see below */ +@@ -148,18 +147,6 @@ + * with the device. + */ + int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); +- +- /* These two are mainly used for bookkeeping & dynamic unloading of +- * kernel modules. inc_use tells the driver that a client is being +- * used by another module & that it should increase its ref. counter. +- * dec_use is the inverse operation. +- * NB: Make sure you have no circular dependencies, or else you get a +- * deadlock when trying to unload the modules. +- * You should use the i2c_{inc,dec}_use_client functions instead of +- * calling this function directly. +- */ +- void (*inc_use)(struct i2c_client *client); +- void (*dec_use)(struct i2c_client *client); + }; + + /* +@@ -192,6 +179,7 @@ + * to name two of the most common. + */ + struct i2c_algorithm { ++ struct module *owner; /* future use --km */ + char name[32]; /* textual description */ + unsigned int id; + +@@ -221,16 +209,13 @@ + * with the access algorithms necessary to access it. + */ + struct i2c_adapter { ++ struct module *owner; + char name[32]; /* some useful name to identify the adapter */ + unsigned int id;/* == is algo->id | hwdep.struct->id, */ + /* for registered values see below */ + struct i2c_algorithm *algo;/* the algorithm to access the bus */ + void *algo_data; + +- /* --- These may be NULL, but should increase the module use count */ +- void (*inc_use)(struct i2c_adapter *); +- void (*dec_use)(struct i2c_adapter *); +- + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); +@@ -241,11 +226,11 @@ + /* and can be set via the i2c_ioctl call */ + + /* data fields that are valid for all devices */ +- struct semaphore lock; ++ struct semaphore bus; ++ struct semaphore list; + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; +- int client_count; + + int timeout; + int retries; +@@ -264,6 +249,9 @@ + #define I2C_CLIENT_ALLOW_USE 0x01 /* Client allows access */ + #define I2C_CLIENT_ALLOW_MULTIPLE_USE 0x02 /* Allow multiple access-locks */ + /* on an i2c_client */ ++#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ ++#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ ++ /* Must equal I2C_M_TEN below */ + + /* i2c_client_address_data is the struct for holding default client + * addresses for a driver and for the parameters supplied on the +@@ -302,12 +290,6 @@ + extern int i2c_attach_client(struct i2c_client *); + extern int i2c_detach_client(struct i2c_client *); + +-/* Only call these if you grab a resource that makes unloading the +- client and the adapter it is on completely impossible. Like when a +- /proc directory is entered. */ +-extern void i2c_inc_use_client(struct i2c_client *); +-extern void i2c_dec_use_client(struct i2c_client *); +- + /* New function: This is to get an i2c_client-struct for controlling the + client either by using i2c_control-function or having the + client-module export functions that can be used with the i2c_client +@@ -341,6 +323,15 @@ + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc); + ++static inline int i2c_client_command(struct i2c_client *client, ++ unsigned int cmd, void *arg) ++{ ++ if (client->driver && client->driver->command) ++ return client->driver->command(client, cmd, arg); ++ else ++ return -EINVAL; ++} ++ + /* An ioctl like call to set div. parameters of the adapter. + */ + extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); +@@ -358,8 +349,6 @@ + /* Return 1 if adapter supports everything we need, 0 if not. */ + extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func); + +-#endif /* __KERNEL__ */ +- + /* + * I2C Message - used for pure i2c transaction, also from /dev interface + */ +@@ -370,15 +359,28 @@ + #define I2C_M_RD 0x01 + #define I2C_M_NOSTART 0x4000 + #define I2C_M_REV_DIR_ADDR 0x2000 ++#define I2C_M_IGNORE_NAK 0x1000 ++#define I2C_M_NO_RD_ACK 0x0800 ++#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ ++#define I2C_M_RECV_PEC 0x0200 /* receive one more than the returned ++ length byte for the PEC */ + __u16 len; /* msg length */ + __u8 *buf; /* pointer to msg data */ ++ int err; ++ short done; + }; + + /* To determine what functionality is present */ + + #define I2C_FUNC_I2C 0x00000001 + #define I2C_FUNC_10BIT_ADDR 0x00000002 +-#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART} */ ++#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */ ++#define I2C_FUNC_SMBUS_HWPEC_CALC 0x00000008 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC 0x00000800 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC 0x00001000 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_PROC_CALL_PEC 0x00002000 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC 0x00004000 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ + #define I2C_FUNC_SMBUS_QUICK 0x00010000 + #define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 + #define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +@@ -389,8 +391,12 @@ + #define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 + #define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 + #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +-#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* New I2C-like block */ +-#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* transfer */ ++#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ ++#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ ++#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000 /* I2C-like block xfer */ ++#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */ ++#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC 0x40000000 /* SMBus 2.0 */ ++#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */ + + #define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE +@@ -402,13 +408,28 @@ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA + #define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK ++#define I2C_FUNC_SMBUS_I2C_BLOCK_2 I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \ ++ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 ++#define I2C_FUNC_SMBUS_BLOCK_DATA_PEC I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC | \ ++ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC ++#define I2C_FUNC_SMBUS_WORD_DATA_PEC I2C_FUNC_SMBUS_READ_WORD_DATA_PEC | \ ++ I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC ++ ++#define I2C_FUNC_SMBUS_READ_BYTE_PEC I2C_FUNC_SMBUS_READ_BYTE_DATA ++#define I2C_FUNC_SMBUS_WRITE_BYTE_PEC I2C_FUNC_SMBUS_WRITE_BYTE_DATA ++#define I2C_FUNC_SMBUS_READ_BYTE_DATA_PEC I2C_FUNC_SMBUS_READ_WORD_DATA ++#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA_PEC I2C_FUNC_SMBUS_WRITE_WORD_DATA ++#define I2C_FUNC_SMBUS_BYTE_PEC I2C_FUNC_SMBUS_BYTE_DATA ++#define I2C_FUNC_SMBUS_BYTE_DATA_PEC I2C_FUNC_SMBUS_WORD_DATA + + #define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ +- I2C_FUNC_SMBUS_WRITE_BLOCK_DATA ++ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ ++ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \ ++ I2C_FUNC_SMBUS_I2C_BLOCK + + /* + * Data for SMBus Messages +@@ -418,8 +439,9 @@ + union i2c_smbus_data { + __u8 byte; + __u16 word; +- __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ ++ __u8 block[I2C_SMBUS_BLOCK_MAX + 3]; /* block[0] is used for length */ + /* one more for read length in block process call */ ++ /* and one more for PEC */ + }; + + /* smbus_access read or write markers */ +@@ -435,6 +457,11 @@ + #define I2C_SMBUS_PROC_CALL 4 + #define I2C_SMBUS_BLOCK_DATA 5 + #define I2C_SMBUS_I2C_BLOCK_DATA 6 ++#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ ++#define I2C_SMBUS_BLOCK_DATA_PEC 8 /* SMBus 2.0 */ ++#define I2C_SMBUS_PROC_CALL_PEC 9 /* SMBus 2.0 */ ++#define I2C_SMBUS_BLOCK_PROC_CALL_PEC 10 /* SMBus 2.0 */ ++#define I2C_SMBUS_WORD_DATA_PEC 11 /* SMBus 2.0 */ + + + /* ----- commands for the ioctl like i2c_command call: +@@ -460,6 +487,7 @@ + + #define I2C_FUNCS 0x0705 /* Get the adapter functionality */ + #define I2C_RDWR 0x0707 /* Combined R/W transfer (one stop only)*/ ++#define I2C_PEC 0x0708 /* != 0 for SMBus PEC */ + #if 0 + #define I2C_ACK_TEST 0x0710 /* See if a slave is at a specific address */ + #endif +@@ -475,16 +503,6 @@ + + #define I2C_MAJOR 89 /* Device major number */ + +-#ifdef __KERNEL__ +- +-# ifndef NULL +-# define NULL ( (void *) 0 ) +-# endif +- +-# ifndef ENODEV +-# include +-# endif +- + /* These defines are used for probing i2c client addresses */ + /* Default fill of many variables */ + #define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ +@@ -546,5 +564,11 @@ + #define i2c_is_isa_adapter(adapptr) \ + ((adapptr)->algo->id == I2C_ALGO_ISA) + +-#endif /* def __KERNEL__ */ +-#endif /* I2C_H */ ++/* Tiny delay function used by the i2c bus drivers */ ++static inline void i2c_delay(signed long timeout) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(timeout); ++} ++ ++#endif /* _LINUX_I2C_H */ diff --git a/packages/linux/linux-mtx-1-2.4.27/17-lmsensors.2.8.8.patch b/packages/linux/linux-mtx-1-2.4.27/17-lmsensors.2.8.8.patch index e69de29bb2..78d95c47ad 100644 --- a/packages/linux/linux-mtx-1-2.4.27/17-lmsensors.2.8.8.patch +++ b/packages/linux/linux-mtx-1-2.4.27/17-lmsensors.2.8.8.patch @@ -0,0 +1,40940 @@ +--- linux-old/drivers/i2c/i2c-ali1535.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-ali1535.c Mon Dec 13 20:18:40 2004 +@@ -0,0 +1,601 @@ ++/* ++ i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2000 Frodo Looijaard , ++ Philip Edelbrock , ++ Mark D. Studebaker , ++ Dan Eaton and ++ Stephen Rousset ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This is the driver for the SMB Host controller on ++ Acer Labs Inc. (ALI) M1535 South Bridge. ++ ++ The M1535 is a South bridge for portable systems. ++ It is very similar to the M15x3 South bridges also produced ++ by Acer Labs Inc. Some of the registers within the part ++ have moved and some have been redefined slightly. Additionally, ++ the sequencing of the SMBus transactions has been modified ++ to be more consistent with the sequencing recommended by ++ the manufacturer and observed through testing. These ++ changes are reflected in this driver and can be identified ++ by comparing this driver to the i2c-ali15x3 driver. ++ For an overview of these chips see http://www.acerlabs.com ++ ++ The SMB controller is part of the 7101 device, which is an ++ ACPI-compliant Power Management Unit (PMU). ++ ++ The whole 7101 device has to be enabled for the SMB to work. ++ You can't just enable the SMB alone. ++ The SMB and the ACPI have separate I/O spaces. ++ We make sure that the SMB is enabled. We leave the ACPI alone. ++ ++ This driver controls the SMB Host only. ++ ++ This driver does not use interrupts. ++*/ ++ ++ ++/* Note: we assume there can only be one ALI1535, with one SMBus interface */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++ ++/* ALI1535 SMBus address offsets */ ++#define SMBHSTSTS (0 + ali1535_smba) ++#define SMBHSTTYP (1 + ali1535_smba) ++#define SMBHSTPORT (2 + ali1535_smba) ++#define SMBHSTCMD (7 + ali1535_smba) ++#define SMBHSTADD (3 + ali1535_smba) ++#define SMBHSTDAT0 (4 + ali1535_smba) ++#define SMBHSTDAT1 (5 + ali1535_smba) ++#define SMBBLKDAT (6 + ali1535_smba) ++ ++/* PCI Address Constants */ ++#define SMBCOM 0x004 ++#define SMBREV 0x008 ++#define SMBCFG 0x0D1 ++#define SMBBA 0x0E2 ++#define SMBHSTCFG 0x0F0 ++#define SMBCLK 0x0F2 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 /* times 1/100 sec */ ++#define ALI1535_SMB_IOSIZE 32 ++ ++/* ++*/ ++#define ALI1535_SMB_DEFAULTBASE 0x8040 ++ ++/* ALI1535 address lock bits */ ++#define ALI1535_LOCK 0x06 < dwe > ++ ++/* ALI1535 command constants */ ++#define ALI1535_QUICK 0x00 ++#define ALI1535_BYTE 0x10 ++#define ALI1535_BYTE_DATA 0x20 ++#define ALI1535_WORD_DATA 0x30 ++#define ALI1535_BLOCK_DATA 0x40 ++#define ALI1535_I2C_READ 0x60 ++ ++#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ ++ /* I2C read */ ++#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ ++#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ ++ /* Alert-Response-Address */ ++ /* (read) */ ++#define ALI1535_KILL 0x04 /* Kill Command (write) */ ++#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ ++ /* Alert-Response-Address */ ++ /* (read) */ ++ ++#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ ++ /* of 10-bit address in I2C */ ++ /* Read Command */ ++ ++/* ALI1535 status register bits */ ++#define ALI1535_STS_IDLE 0x04 ++#define ALI1535_STS_BUSY 0x08 /* host busy */ ++#define ALI1535_STS_DONE 0x10 /* transaction complete */ ++#define ALI1535_STS_DEV 0x20 /* device error */ ++#define ALI1535_STS_BUSERR 0x40 /* bus error */ ++#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ ++#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ ++ ++#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ ++ ++/* ALI1535 device address register bits */ ++#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ ++ /* Address field */ ++ /* -> Write = 0 */ ++ /* -> Read = 1 */ ++#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ ++ ++static int ali1535_transaction(void); ++ ++static unsigned short ali1535_smba = 0; ++DECLARE_MUTEX(i2c_ali1535_sem); ++ ++ ++/* Detect whether a ALI1535 can be found, and initialize it, where necessary. ++ Note the differences between kernels with the old PCI BIOS interface and ++ newer kernels with the real PCI interface. In compat.h some things are ++ defined to make the transition easier. */ ++int ali1535_setup(struct pci_dev *ALI1535_dev) ++{ ++ int error_return = 0; ++ unsigned char temp; ++ ++/* Check the following things: ++ - SMB I/O address is initialized ++ - Device is enabled ++ - We can use the addresses ++*/ ++ ++/* Determine the address of the SMBus area */ ++ pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba); ++ ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); ++ if (ali1535_smba == 0) { ++ printk ++ ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n"); ++ error_return = -ENODEV; ++ } ++ ++ if (error_return == -ENODEV) ++ goto END; ++ ++ if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) { ++ printk ++ ("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n", ++ ali1535_smba); ++ error_return = -ENODEV; ++ } ++ ++ if (error_return == -ENODEV) ++ goto END; ++ ++ /* check if whole device is enabled */ ++ pci_read_config_byte(ALI1535_dev, SMBCFG, &temp); ++ if ((temp & ALI1535_SMBIO_EN) == 0) { ++ printk ++ ("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n"); ++ error_return = -ENODEV; ++ goto END; ++ } ++ ++/* Is SMB Host controller enabled? */ ++ pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp); ++ if ((temp & 1) == 0) { ++ printk ++ ("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n"); ++ error_return = -ENODEV; ++ goto END; ++ } ++ ++/* set SMB clock to 74KHz as recommended in data sheet */ ++ pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20); ++ ++ /* Everything is happy, let's grab the memory and set things up. */ ++ request_region(ali1535_smba, ALI1535_SMB_IOSIZE, "ali1535-smb"); ++ ++#ifdef DEBUG ++/* ++ The interrupt routing for SMB is set up in register 0x77 in the ++ 1533 ISA Bridge device, NOT in the 7101 device. ++ Don't bother with finding the 1533 device and reading the register. ++ if ((....... & 0x0F) == 1) ++ printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n"); ++*/ ++ pci_read_config_byte(ALI1535_dev, SMBREV, &temp); ++ printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp); ++ printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba); ++#endif /* DEBUG */ ++ ++ END: ++ return error_return; ++} ++ ++ ++/* Another internally used function */ ++int ali1535_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++#ifdef DEBUG ++ printk ++ ("i2c-ali1535.o: Transaction (pre): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " ++ "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++#endif ++ ++ /* get status */ ++ temp = inb_p(SMBHSTSTS); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ /* Check the busy bit first */ ++ if (temp & ALI1535_STS_BUSY) { ++/* ++ If the host controller is still busy, it may have timed out in the previous transaction, ++ resulting in a "SMBus Timeout" printk. ++ I've tried the following to reset a stuck busy bit. ++ 1. Reset the controller with an KILL command. ++ (this doesn't seem to clear the controller if an external device is hung) ++ 2. Reset the controller and the other SMBus devices with a T_OUT command. ++ (this clears the host busy bit if an external device is hung, ++ but it comes back upon a new access to a device) ++ 3. Disable and reenable the controller in SMBHSTCFG ++ Worst case, nothing seems to work except power reset. ++*/ ++/* Abort - reset the host controller */ ++/* ++#ifdef DEBUG ++ printk("i2c-ali1535.o: Resetting host controller to clear busy condition\n",temp); ++#endif ++ outb_p(ALI1535_KILL, SMBHSTTYP); ++ temp = inb_p(SMBHSTSTS); ++ if (temp & ALI1535_STS_BUSY) { ++*/ ++ ++/* ++ Try resetting entire SMB bus, including other devices - ++ This may not work either - it clears the BUSY bit but ++ then the BUSY bit may come back on when you try and use the chip again. ++ If that's the case you are stuck. ++*/ ++ printk ++ ("i2c-ali1535.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", ++ temp); ++ outb_p(ALI1535_T_OUT, SMBHSTTYP); ++ temp = inb_p(SMBHSTSTS); ++ } ++/* ++ } ++*/ ++ ++ /* now check the error bits and the busy bit */ ++ if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { ++ /* do a clear-on-write */ ++ outb_p(0xFF, SMBHSTSTS); ++ if ((temp = inb_p(SMBHSTSTS)) & ++ (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { ++ /* this is probably going to be correctable only by a power reset ++ as one of the bits now appears to be stuck */ ++ /* This may be a bus or device with electrical problems. */ ++ printk ++ ("i2c-ali1535.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", ++ temp); ++ return -1; ++ } ++ } else { ++ /* check and clear done bit */ ++ if (temp & ALI1535_STS_DONE) { ++ outb_p(temp, SMBHSTSTS); ++ } ++ } ++ ++ /* start the transaction by writing anything to the start register */ ++ outb_p(0xFF, SMBHSTPORT); ++ ++ /* We will always wait for a fraction of a second! */ ++ timeout = 0; ++ do { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) ++ && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ result = -1; ++ printk("i2c-ali1535.o: SMBus Timeout!\n"); ++ } ++ ++ if (temp & ALI1535_STS_FAIL) { ++ result = -1; ++#ifdef DEBUG ++ printk("i2c-ali1535.o: Error: Failed bus transaction\n"); ++#endif ++ } ++ ++/* ++ Unfortunately the ALI SMB controller maps "no response" and "bus collision" ++ into a single bit. No reponse is the usual case so don't do a printk. ++ This means that bus collisions go unreported. ++*/ ++ if (temp & ALI1535_STS_BUSERR) { ++ result = -1; ++#ifdef DEBUG ++ printk ++ ("i2c-ali1535.o: Error: no response or bus collision ADD=%02x\n", ++ inb_p(SMBHSTADD)); ++#endif ++ } ++ ++/* haven't ever seen this */ ++ if (temp & ALI1535_STS_DEV) { ++ result = -1; ++ printk("i2c-ali1535.o: Error: device error\n"); ++ } ++ ++/* ++ check to see if the "command complete" indication is set ++ */ ++ if (!(temp & ALI1535_STS_DONE)) { ++ result = -1; ++ printk("i2c-ali1535.o: Error: command never completed\n"); ++ } ++#ifdef DEBUG ++ printk ++ ("i2c-ali1535.o: Transaction (post): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, " ++ "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++#endif ++ ++/* ++ take consequent actions for error conditions ++ */ ++ if (!(temp & ALI1535_STS_DONE)) { ++ /* issue "kill" to reset host controller */ ++ outb_p(ALI1535_KILL,SMBHSTTYP); ++ outb_p(0xFF,SMBHSTSTS); ++ } ++ else if (temp & ALI1535_STS_ERR) { ++ /* issue "timeout" to reset all devices on bus */ ++ outb_p(ALI1535_T_OUT,SMBHSTTYP); ++ outb_p(0xFF,SMBHSTSTS); ++ } ++ ++ return result; ++} ++ ++/* Return -1 on error. */ ++s32 ali1535_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, u8 command, ++ int size, union i2c_smbus_data * data) ++{ ++ int i, len; ++ int temp; ++ int timeout; ++ s32 result = 0; ++ ++ down(&i2c_ali1535_sem); ++/* make sure SMBus is idle */ ++ temp = inb_p(SMBHSTSTS); ++ for (timeout = 0; ++ (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); ++ timeout++) { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } ++ if (timeout >= MAX_TIMEOUT) { ++ printk("i2c-ali1535.o: Idle wait Timeout! STS=0x%02x\n", ++ temp); ++ } ++ ++/* clear status register (clear-on-write) */ ++ outb_p(0xFF, SMBHSTSTS); ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI1535_QUICK; ++ outb_p(size, SMBHSTTYP); /* output command */ ++ break; ++ case I2C_SMBUS_BYTE: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI1535_BYTE; ++ outb_p(size, SMBHSTTYP); /* output command */ ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMBHSTCMD); ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI1535_BYTE_DATA; ++ outb_p(size, SMBHSTTYP); /* output command */ ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, SMBHSTDAT0); ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI1535_WORD_DATA; ++ outb_p(size, SMBHSTTYP); /* output command */ ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word & 0xff, SMBHSTDAT0); ++ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); ++ } ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI1535_BLOCK_DATA; ++ outb_p(size, SMBHSTTYP); /* output command */ ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) { ++ len = 0; ++ data->block[0] = len; ++ } ++ if (len > 32) { ++ len = 32; ++ data->block[0] = len; ++ } ++ outb_p(len, SMBHSTDAT0); ++ outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= len; i++) ++ outb_p(data->block[i], SMBBLKDAT); ++ } ++ break; ++ default: ++ printk ++ (KERN_WARNING "i2c-ali1535.o: Unsupported transaction %d\n", size); ++ result = -1; ++ goto EXIT; ++ } ++ ++ if (ali1535_transaction()) /* Error in transaction */ ++ { ++ result = -1; ++ goto EXIT; ++ } ++ ++ if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) ++ { ++ result = 0; ++ goto EXIT; ++ } ++ ++ switch (size) { ++ case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case ALI1535_BYTE_DATA: ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case ALI1535_WORD_DATA: ++ data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); ++ break; ++ case ALI1535_BLOCK_DATA: ++ len = inb_p(SMBHSTDAT0); ++ if (len > 32) ++ len = 32; ++ data->block[0] = len; ++ outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= data->block[0]; i++) { ++ data->block[i] = inb_p(SMBBLKDAT); ++#ifdef DEBUG ++ printk ++ ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n", ++ len, i, data->block[i]); ++#endif /* DEBUG */ ++ } ++ break; ++ } ++EXIT: ++ up(&i2c_ali1535_sem); ++ return result; ++} ++ ++ ++u32 ali1535_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-i2c SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = ali1535_access, ++ .functionality = ali1535_func, ++}; ++ ++static struct i2c_adapter ali1535_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535, ++ .algo = &smbus_algorithm, ++}; ++ ++ ++static struct pci_device_id ali1535_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_AL, ++ .device = PCI_DEVICE_ID_AL_M7101, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ if (ali1535_setup(dev)) { ++ printk ++ ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ ++ sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x", ++ ali1535_smba); ++ return i2c_add_adapter(&ali1535_adapter); ++} ++ ++static void __devexit ali1535_remove(struct pci_dev *dev) ++{ ++ i2c_del_adapter(&ali1535_adapter); ++ release_region(ali1535_smba, ALI1535_SMB_IOSIZE); ++} ++ ++ ++static struct pci_driver ali1535_driver = { ++ .name = "ali1535 smbus", ++ .id_table = ali1535_ids, ++ .probe = ali1535_probe, ++ .remove = __devexit_p(ali1535_remove), ++}; ++ ++static int __init i2c_ali1535_init(void) ++{ ++ printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&ali1535_driver); ++} ++ ++ ++static void __exit i2c_ali1535_exit(void) ++{ ++ pci_unregister_driver(&ali1535_driver); ++} ++ ++#ifdef RLX ++EXPORT_SYMBOL(ali1535_smba); ++EXPORT_SYMBOL(ali1535_access); ++EXPORT_SYMBOL(i2c_ali1535_sem); ++#endif ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard , Philip Edelbrock , " ++ "Mark D. Studebaker and Dan Eaton "); ++MODULE_DESCRIPTION("ALI1535 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_ali1535_init); ++module_exit(i2c_ali1535_exit); ++ +--- linux-old/drivers/i2c/i2c-ali15x3.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-ali15x3.c Mon Dec 13 20:18:40 2004 +@@ -0,0 +1,533 @@ ++/* ++ ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1999 Frodo Looijaard and ++ Philip Edelbrock and ++ Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This is the driver for the SMB Host controller on ++ Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. ++ ++ The M1543C is a South bridge for desktop systems. ++ The M1533 is a South bridge for portable systems. ++ They are part of the following ALI chipsets: ++ "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge ++ with AGP and 100MHz CPU Front Side bus ++ "Aladdin V": Includes the M1541 Socket 7 North bridge ++ with AGP and 100MHz CPU Front Side bus ++ "Aladdin IV": Includes the M1541 Socket 7 North bridge ++ with host bus up to 83.3 MHz. ++ For an overview of these chips see http://www.acerlabs.com ++ ++ The M1533/M1543C devices appear as FOUR separate devices ++ on the PCI bus. An output of lspci will show something similar ++ to the following: ++ ++ 00:02.0 USB Controller: Acer Laboratories Inc. M5237 ++ 00:03.0 Bridge: Acer Laboratories Inc. M7101 ++ 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 ++ 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 ++ ++ The SMB controller is part of the 7101 device, which is an ++ ACPI-compliant Power Management Unit (PMU). ++ ++ The whole 7101 device has to be enabled for the SMB to work. ++ You can't just enable the SMB alone. ++ The SMB and the ACPI have separate I/O spaces. ++ We make sure that the SMB is enabled. We leave the ACPI alone. ++ ++ This driver controls the SMB Host only. ++ The SMB Slave controller on the M15X3 is not enabled. ++ ++ This driver does not use interrupts. ++*/ ++ ++/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ ++ ++/* #define DEBUG 1 */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++/* ALI15X3 SMBus address offsets */ ++#define SMBHSTSTS (0 + ali15x3_smba) ++#define SMBHSTCNT (1 + ali15x3_smba) ++#define SMBHSTSTART (2 + ali15x3_smba) ++#define SMBHSTCMD (7 + ali15x3_smba) ++#define SMBHSTADD (3 + ali15x3_smba) ++#define SMBHSTDAT0 (4 + ali15x3_smba) ++#define SMBHSTDAT1 (5 + ali15x3_smba) ++#define SMBBLKDAT (6 + ali15x3_smba) ++ ++/* PCI Address Constants */ ++#define SMBCOM 0x004 ++#define SMBBA 0x014 ++#define SMBATPC 0x05B /* used to unlock xxxBA registers */ ++#define SMBHSTCFG 0x0E0 ++#define SMBSLVC 0x0E1 ++#define SMBCLK 0x0E2 ++#define SMBREV 0x008 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 200 /* times 1/100 sec */ ++#define ALI15X3_SMB_IOSIZE 32 ++ ++/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. ++ We don't use these here. If the bases aren't set to some value we ++ tell user to upgrade BIOS and we fail. ++*/ ++#define ALI15X3_SMB_DEFAULTBASE 0xE800 ++ ++/* ALI15X3 address lock bits */ ++#define ALI15X3_LOCK 0x06 ++ ++/* ALI15X3 command constants */ ++#define ALI15X3_ABORT 0x02 ++#define ALI15X3_T_OUT 0x04 ++#define ALI15X3_QUICK 0x00 ++#define ALI15X3_BYTE 0x10 ++#define ALI15X3_BYTE_DATA 0x20 ++#define ALI15X3_WORD_DATA 0x30 ++#define ALI15X3_BLOCK_DATA 0x40 ++#define ALI15X3_BLOCK_CLR 0x80 ++ ++/* ALI15X3 status register bits */ ++#define ALI15X3_STS_IDLE 0x04 ++#define ALI15X3_STS_BUSY 0x08 ++#define ALI15X3_STS_DONE 0x10 ++#define ALI15X3_STS_DEV 0x20 /* device error */ ++#define ALI15X3_STS_COLL 0x40 /* collision or no response */ ++#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ ++#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ ++ ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the device at the given address. */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the i2c controller"); ++ ++static unsigned short ali15x3_smba = 0; ++ ++static int ali15x3_setup(struct pci_dev *ALI15X3_dev) ++{ ++ u16 a; ++ unsigned char temp; ++ ++ /* Check the following things: ++ - SMB I/O address is initialized ++ - Device is enabled ++ - We can use the addresses ++ */ ++ ++ /* Unlock the register. ++ The data sheet says that the address registers are read-only ++ if the lock bits are 1, but in fact the address registers ++ are zero unless you clear the lock bits. ++ */ ++ pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); ++ if (temp & ALI15X3_LOCK) { ++ temp &= ~ALI15X3_LOCK; ++ pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); ++ } ++ ++ /* Determine the address of the SMBus area */ ++ pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); ++ ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); ++ if (ali15x3_smba == 0 && force_addr == 0) { ++ dev_err(ALI15X3_dev, "ALI15X3_smb region uninitialized " ++ "- upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ ++ if(force_addr) ++ ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); ++ ++ if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb")) { ++ dev_err(ALI15X3_dev, ++ "ALI15X3_smb region 0x%x already in use!\n", ++ ali15x3_smba); ++ return -ENODEV; ++ } ++ ++ if(force_addr) { ++ dev_info(ALI15X3_dev, "forcing ISA address 0x%04X\n", ++ ali15x3_smba); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) ++ return -ENODEV; ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(ALI15X3_dev, SMBBA, &a)) ++ return -ENODEV; ++ if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { ++ /* make sure it works */ ++ dev_err(ALI15X3_dev, ++ "force address failed - not supported?\n"); ++ return -ENODEV; ++ } ++ } ++ /* check if whole device is enabled */ ++ pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); ++ if ((temp & 1) == 0) { ++ dev_info(ALI15X3_dev, "enabling SMBus device\n"); ++ pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); ++ } ++ ++ /* Is SMB Host controller enabled? */ ++ pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); ++ if ((temp & 1) == 0) { ++ dev_info(ALI15X3_dev, "enabling SMBus controller\n"); ++ pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); ++ } ++ ++ /* set SMB clock to 74KHz as recommended in data sheet */ ++ pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); ++ ++ /* ++ The interrupt routing for SMB is set up in register 0x77 in the ++ 1533 ISA Bridge device, NOT in the 7101 device. ++ Don't bother with finding the 1533 device and reading the register. ++ if ((....... & 0x0F) == 1) ++ dev_dbg(ALI15X3_dev, "ALI15X3 using Interrupt 9 for SMBus.\n"); ++ */ ++ pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); ++ dev_dbg(ALI15X3_dev, "SMBREV = 0x%X\n", temp); ++ dev_dbg(ALI15X3_dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba); ++ ++ return 0; ++} ++ ++/* Another internally used function */ ++static int ali15x3_transaction(struct i2c_adapter *adap) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ dev_dbg(adap, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), ++ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), ++ inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); ++ ++ /* get status */ ++ temp = inb_p(SMBHSTSTS); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ /* Check the busy bit first */ ++ if (temp & ALI15X3_STS_BUSY) { ++ /* ++ If the host controller is still busy, it may have timed out in the ++ previous transaction, resulting in a "SMBus Timeout" Dev. ++ I've tried the following to reset a stuck busy bit. ++ 1. Reset the controller with an ABORT command. ++ (this doesn't seem to clear the controller if an external ++ device is hung) ++ 2. Reset the controller and the other SMBus devices with a ++ T_OUT command. (this clears the host busy bit if an ++ external device is hung, but it comes back upon a new access ++ to a device) ++ 3. Disable and reenable the controller in SMBHSTCFG ++ Worst case, nothing seems to work except power reset. ++ */ ++ /* Abort - reset the host controller */ ++ /* ++ Try resetting entire SMB bus, including other devices - ++ This may not work either - it clears the BUSY bit but ++ then the BUSY bit may come back on when you try and use the chip again. ++ If that's the case you are stuck. ++ */ ++ dev_info(adap, "Resetting entire SMB Bus to " ++ "clear busy condition (%02x)\n", temp); ++ outb_p(ALI15X3_T_OUT, SMBHSTCNT); ++ temp = inb_p(SMBHSTSTS); ++ } ++ ++ /* now check the error bits and the busy bit */ ++ if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { ++ /* do a clear-on-write */ ++ outb_p(0xFF, SMBHSTSTS); ++ if ((temp = inb_p(SMBHSTSTS)) & ++ (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { ++ /* this is probably going to be correctable only by a power reset ++ as one of the bits now appears to be stuck */ ++ /* This may be a bus or device with electrical problems. */ ++ dev_err(adap, "SMBus reset failed! (0x%02x) - " ++ "controller or device on bus is probably hung\n", ++ temp); ++ return -1; ++ } ++ } else { ++ /* check and clear done bit */ ++ if (temp & ALI15X3_STS_DONE) { ++ outb_p(temp, SMBHSTSTS); ++ } ++ } ++ ++ /* start the transaction by writing anything to the start register */ ++ outb_p(0xFF, SMBHSTSTART); ++ ++ /* We will always wait for a fraction of a second! */ ++ timeout = 0; ++ do { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) ++ && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ result = -1; ++ dev_err(adap, "SMBus Timeout!\n"); ++ } ++ ++ if (temp & ALI15X3_STS_TERM) { ++ result = -1; ++ dev_dbg(adap, "Error: Failed bus transaction\n"); ++ } ++ ++ /* ++ Unfortunately the ALI SMB controller maps "no response" and "bus ++ collision" into a single bit. No reponse is the usual case so don't ++ do a printk. ++ This means that bus collisions go unreported. ++ */ ++ if (temp & ALI15X3_STS_COLL) { ++ result = -1; ++ dev_dbg(adap, ++ "Error: no response or bus collision ADD=%02x\n", ++ inb_p(SMBHSTADD)); ++ } ++ ++ /* haven't ever seen this */ ++ if (temp & ALI15X3_STS_DEV) { ++ result = -1; ++ dev_err(adap, "Error: device error\n"); ++ } ++ dev_dbg(adap, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), ++ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), ++ inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); ++ return result; ++} ++ ++/* Return -1 on error. */ ++static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, u8 command, ++ int size, union i2c_smbus_data * data) ++{ ++ int i, len; ++ int temp; ++ int timeout; ++ ++ /* clear all the bits (clear-on-write) */ ++ outb_p(0xFF, SMBHSTSTS); ++ /* make sure SMBus is idle */ ++ temp = inb_p(SMBHSTSTS); ++ for (timeout = 0; ++ (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); ++ timeout++) { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } ++ if (timeout >= MAX_TIMEOUT) { ++ dev_err(adap, "Idle wait Timeout! STS=0x%02x\n", temp); ++ } ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = ALI15X3_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMBHSTCMD); ++ size = ALI15X3_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, SMBHSTDAT0); ++ size = ALI15X3_BYTE_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word & 0xff, SMBHSTDAT0); ++ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); ++ } ++ size = ALI15X3_WORD_DATA; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) { ++ len = 0; ++ data->block[0] = len; ++ } ++ if (len > 32) { ++ len = 32; ++ data->block[0] = len; ++ } ++ outb_p(len, SMBHSTDAT0); ++ /* Reset SMBBLKDAT */ ++ outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); ++ for (i = 1; i <= len; i++) ++ outb_p(data->block[i], SMBBLKDAT); ++ } ++ size = ALI15X3_BLOCK_DATA; ++ break; ++ default: ++ printk ++ (KERN_WARNING "i2c-ali15x3.o: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ outb_p(size, SMBHSTCNT); /* output command */ ++ ++ if (ali15x3_transaction(adap)) /* Error in transaction */ ++ return -1; ++ ++ if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) ++ return 0; ++ ++ ++ switch (size) { ++ case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case ALI15X3_BYTE_DATA: ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case ALI15X3_WORD_DATA: ++ data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); ++ break; ++ case ALI15X3_BLOCK_DATA: ++ len = inb_p(SMBHSTDAT0); ++ if (len > 32) ++ len = 32; ++ data->block[0] = len; ++ /* Reset SMBBLKDAT */ ++ outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); ++ for (i = 1; i <= data->block[0]; i++) { ++ data->block[i] = inb_p(SMBBLKDAT); ++ dev_dbg(adap, "Blk: len=%d, i=%d, data=%02x\n", ++ len, i, data->block[i]); ++ } ++ break; ++ } ++ return 0; ++} ++ ++static u32 ali15x3_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = ali15x3_access, ++ .functionality = ali15x3_func, ++}; ++ ++static struct i2c_adapter ali15x3_adapter = { ++ .owner = THIS_MODULE, ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, ++ .algo = &smbus_algorithm, ++ .name = "unset", ++}; ++ ++static struct pci_device_id ali15x3_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_AL, ++ .device = PCI_DEVICE_ID_AL_M7101, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ if (ali15x3_setup(dev)) { ++ dev_err(dev, ++ "ALI15X3 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ ++ snprintf(ali15x3_adapter.name, 32, ++ "SMBus ALI15X3 adapter at %04x", ali15x3_smba); ++ return i2c_add_adapter(&ali15x3_adapter); ++} ++ ++static void __devexit ali15x3_remove(struct pci_dev *dev) ++{ ++ i2c_del_adapter(&ali15x3_adapter); ++ release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); ++} ++ ++static struct pci_driver ali15x3_driver = { ++ .name = "ali15x3 smbus", ++ .id_table = ali15x3_ids, ++ .probe = ali15x3_probe, ++ .remove = __devexit_p(ali15x3_remove), ++}; ++ ++static int __init i2c_ali15x3_init(void) ++{ ++ printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&ali15x3_driver); ++} ++ ++static void __exit i2c_ali15x3_exit(void) ++{ ++ pci_unregister_driver(&ali15x3_driver); ++} ++ ++MODULE_AUTHOR ("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "and Mark D. Studebaker "); ++MODULE_DESCRIPTION("ALI15X3 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_ali15x3_init); ++module_exit(i2c_ali15x3_exit); +--- linux-old/drivers/i2c/i2c-amd756.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-amd756.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,425 @@ ++/* ++ amd756.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ ++ Copyright (c) 1999-2002 Merlin Hughes ++ ++ Shamelessly ripped from i2c-piix4.c: ++ ++ Copyright (c) 1998, 1999 Frodo Looijaard and ++ Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ 2002-04-08: Added nForce support. (Csaba Halasz) ++ 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) ++ 2002-12-28: Rewritten into something that resembles a Linux driver (hch) ++ 2003-11-29: Added back AMD8111 removed by the previous rewrite. ++ (Philip Pokorny) ++ 2004-02-15: Don't register driver to avoid driver conflicts. ++ (Daniel Rune Jensen) ++*/ ++ ++/* ++ Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce ++ Note: we assume there can only be one device, with one SMBus interface. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#define DRV_NAME "i2c-amd756" ++ ++/* AMD756 SMBus address offsets */ ++#define SMB_ADDR_OFFSET 0xE0 ++#define SMB_IOSIZE 16 ++#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport) ++#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport) ++#define SMB_HOST_ADDRESS (0x4 + amd756_ioport) ++#define SMB_HOST_DATA (0x6 + amd756_ioport) ++#define SMB_HOST_COMMAND (0x8 + amd756_ioport) ++#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport) ++#define SMB_HAS_DATA (0xA + amd756_ioport) ++#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport) ++#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport) ++#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport) ++ ++/* PCI Address Constants */ ++ ++/* address of I/O space */ ++#define SMBBA 0x058 /* mh */ ++#define SMBBANFORCE 0x014 ++ ++/* general configuration */ ++#define SMBGCFG 0x041 /* mh */ ++ ++/* silicon revision code */ ++#define SMBREV 0x008 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++ ++/* AMD756 constants */ ++#define AMD756_QUICK 0x00 ++#define AMD756_BYTE 0x01 ++#define AMD756_BYTE_DATA 0x02 ++#define AMD756_WORD_DATA 0x03 ++#define AMD756_PROCESS_CALL 0x04 ++#define AMD756_BLOCK_DATA 0x05 ++ ++ ++static unsigned short amd756_ioport = 0; ++ ++/* ++ SMBUS event = I/O 28-29 bit 11 ++ see E0 for the status bits and enabled in E2 ++ ++*/ ++ ++#define GS_ABRT_STS (1 << 0) ++#define GS_COL_STS (1 << 1) ++#define GS_PRERR_STS (1 << 2) ++#define GS_HST_STS (1 << 3) ++#define GS_HCYC_STS (1 << 4) ++#define GS_TO_STS (1 << 5) ++#define GS_SMB_STS (1 << 11) ++ ++#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ ++ GS_HCYC_STS | GS_TO_STS ) ++ ++#define GE_CYC_TYPE_MASK (7) ++#define GE_HOST_STC (1 << 3) ++#define GE_ABORT (1 << 5) ++ ++ ++static int amd756_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ pr_debug(DRV_NAME ++ ": Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", ++ inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), ++ inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { ++ pr_debug(DRV_NAME ": SMBus busy (%04x). Waiting... \n", temp); ++ do { ++ i2c_delay(1); ++ temp = inw_p(SMB_GLOBAL_STATUS); ++ } while ((temp & (GS_HST_STS | GS_SMB_STS)) && ++ (timeout++ < MAX_TIMEOUT)); ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ pr_debug(DRV_NAME ": Busy wait timeout (%04x)\n", temp); ++ goto abort; ++ } ++ timeout = 0; ++ } ++ ++ /* start the transaction by setting the start bit */ ++ outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); ++ ++ /* We will always wait for a fraction of a second! */ ++ do { ++ i2c_delay(1); ++ temp = inw_p(SMB_GLOBAL_STATUS); ++ } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ pr_debug(DRV_NAME ": Completion timeout!\n"); ++ goto abort; ++ } ++ ++ if (temp & GS_PRERR_STS) { ++ result = -1; ++ pr_debug(DRV_NAME ": SMBus Protocol error (no response)!\n"); ++ } ++ ++ if (temp & GS_COL_STS) { ++ result = -1; ++ printk(KERN_WARNING DRV_NAME ": SMBus collision!\n"); ++ } ++ ++ if (temp & GS_TO_STS) { ++ result = -1; ++ pr_debug(DRV_NAME ": SMBus protocol timeout!\n"); ++ } ++ ++ if (temp & GS_HCYC_STS) ++ pr_debug(DRV_NAME ": SMBus protocol success!\n"); ++ ++ outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); ++ ++#ifdef DEBUG ++ if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { ++ pr_debug(DRV_NAME ++ ": Failed reset at end of transaction (%04x)\n", temp); ++ } ++ ++ pr_debug(DRV_NAME ++ ": Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", ++ inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), ++ inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); ++#endif ++ ++ return result; ++ ++ abort: ++ printk(KERN_WARNING DRV_NAME ": Sending abort.\n"); ++ outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); ++ i2c_delay(100); ++ outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); ++ return -1; ++} ++ ++/* Return -1 on error. */ ++ ++static s32 amd756_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data * data) ++{ ++ int i, len; ++ ++ /** TODO: Should I supporte the 10-bit transfers? */ ++ switch (size) { ++ /* TODO: proc call is supported, I'm just not sure what to do here... */ ++ case I2C_SMBUS_QUICK: ++ outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMB_HOST_ADDRESS); ++ size = AMD756_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMB_HOST_ADDRESS); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMB_HOST_DATA); ++ size = AMD756_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMB_HOST_ADDRESS); ++ outb_p(command, SMB_HOST_COMMAND); ++ if (read_write == I2C_SMBUS_WRITE) ++ outw_p(data->byte, SMB_HOST_DATA); ++ size = AMD756_BYTE_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMB_HOST_ADDRESS); ++ outb_p(command, SMB_HOST_COMMAND); ++ if (read_write == I2C_SMBUS_WRITE) ++ outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ ++ size = AMD756_WORD_DATA; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMB_HOST_ADDRESS); ++ outb_p(command, SMB_HOST_COMMAND); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) ++ len = 0; ++ if (len > 32) ++ len = 32; ++ outw_p(len, SMB_HOST_DATA); ++ /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ ++ for (i = 1; i <= len; i++) ++ outb_p(data->block[i], ++ SMB_HOST_BLOCK_DATA); ++ } ++ size = AMD756_BLOCK_DATA; ++ break; ++ default: ++ printk ++ (KERN_WARNING "i2c-amd756.o: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ /* How about enabling interrupts... */ ++ outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); ++ ++ if (amd756_transaction()) /* Error in transaction */ ++ return -1; ++ ++ if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) ++ return 0; ++ ++ ++ switch (size) { ++ case AMD756_BYTE: ++ data->byte = inw_p(SMB_HOST_DATA); ++ break; ++ case AMD756_BYTE_DATA: ++ data->byte = inw_p(SMB_HOST_DATA); ++ break; ++ case AMD756_WORD_DATA: ++ data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ ++ break; ++ case AMD756_BLOCK_DATA: ++ data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f; ++ if(data->block[0] > 32) ++ data->block[0] = 32; ++ /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ ++ for (i = 1; i <= data->block[0]; i++) ++ data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); ++ break; ++ } ++ ++ return 0; ++} ++ ++static u32 amd756_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = amd756_access, ++ .functionality = amd756_func, ++}; ++ ++static struct i2c_adapter amd756_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756, ++ .algo = &smbus_algorithm, ++}; ++ ++enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 }; ++ ++static struct pci_device_id amd756_ids[] __devinitdata = { ++ {PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 }, ++ {PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 }, ++ {PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 }, ++ {PCI_VENDOR_ID_AMD, 0x746B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111 }, ++ {PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE }, ++ { 0, } ++}; ++ ++static int __devinit amd756_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ int nforce = (id->driver_data == NFORCE); ++ int error; ++ u8 temp; ++ ++ if (amd756_ioport) { ++ printk(KERN_ERR DRV_NAME ": Only one device supported. " ++ "(you have a strange motherboard, btw..)\n"); ++ return -ENODEV; ++ } ++ ++ if (nforce) { ++ if (PCI_FUNC(pdev->devfn) != 1) ++ return -ENODEV; ++ ++ pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport); ++ amd756_ioport &= 0xfffc; ++ } else { /* amd */ ++ if (PCI_FUNC(pdev->devfn) != 3) ++ return -ENODEV; ++ ++ pci_read_config_byte(pdev, SMBGCFG, &temp); ++ if ((temp & 128) == 0) { ++ printk(KERN_ERR DRV_NAME ++ ": Error: SMBus controller I/O not enabled!\n"); ++ return -ENODEV; ++ } ++ ++ /* Determine the address of the SMBus areas */ ++ /* Technically it is a dword but... */ ++ pci_read_config_word(pdev, SMBBA, &amd756_ioport); ++ amd756_ioport &= 0xff00; ++ amd756_ioport += SMB_ADDR_OFFSET; ++ } ++ ++ if (!request_region(amd756_ioport, SMB_IOSIZE, "amd756-smbus")) { ++ printk(KERN_ERR DRV_NAME ++ ": SMB region 0x%x already in use!\n", amd756_ioport); ++ return -ENODEV; ++ } ++ ++#ifdef DEBUG ++ pci_read_config_byte(pdev, SMBREV, &temp); ++ printk(KERN_DEBUG DRV_NAME ": SMBREV = 0x%X\n", temp); ++ printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport); ++#endif ++ ++ sprintf(amd756_adapter.name, ++ "SMBus AMD756 adapter at %04x", amd756_ioport); ++ ++ error = i2c_add_adapter(&amd756_adapter); ++ if (error) { ++ printk(KERN_ERR DRV_NAME ++ ": Adapter registration failed, module not inserted.\n"); ++ goto out_err; ++ } ++ ++ return 0; ++ ++ out_err: ++ release_region(amd756_ioport, SMB_IOSIZE); ++ return error; ++} ++ ++ ++static int __init i2c_amd756_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk(KERN_INFO "i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(amd756_ids, dev); ++ if (id && amd756_probe(dev, id) >= 0) ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++ ++static void __exit i2c_amd756_exit(void) ++{ ++ i2c_del_adapter(&amd756_adapter); ++ release_region(amd756_ioport, SMB_IOSIZE); ++} ++ ++MODULE_AUTHOR("Merlin Hughes "); ++MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_amd756_init) ++module_exit(i2c_amd756_exit) +--- linux-old/drivers/i2c/i2c-amd8111.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-amd8111.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,421 @@ ++/* ++ * SMBus 2.0 driver for AMD-8111 IO-Hub. ++ * ++ * Copyright (c) 2002 Vojtech Pavlik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#ifndef I2C_HW_SMBUS_AMD8111 ++#error Your i2c is too old - i2c-2.7.0 or greater required! ++#endif ++ ++/* kernel 2.4.9 needs this */ ++#ifndef min_t ++#define min_t(type,x,y) min(type,x,y) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR ("Vojtech Pavlik "); ++MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); ++ ++struct amd_smbus { ++ struct pci_dev *dev; ++ struct i2c_adapter adapter; ++ int base; ++ int size; ++}; ++ ++/* ++ * AMD PCI control registers definitions. ++ */ ++ ++#define AMD_PCI_MISC 0x48 ++ ++#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ ++#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ ++#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ ++ ++/* ++ * ACPI 2.0 chapter 13 PCI interface definitions. ++ */ ++ ++#define AMD_EC_DATA 0x00 /* data register */ ++#define AMD_EC_SC 0x04 /* status of controller */ ++#define AMD_EC_CMD 0x04 /* command register */ ++#define AMD_EC_ICR 0x08 /* interrupt control register */ ++ ++#define AMD_EC_SC_SMI 0x04 /* smi event pending */ ++#define AMD_EC_SC_SCI 0x02 /* sci event pending */ ++#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ ++#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ ++#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ ++#define AMD_EC_SC_OBF 0x01 /* data ready for host */ ++ ++#define AMD_EC_CMD_RD 0x80 /* read EC */ ++#define AMD_EC_CMD_WR 0x81 /* write EC */ ++#define AMD_EC_CMD_BE 0x82 /* enable burst mode */ ++#define AMD_EC_CMD_BD 0x83 /* disable burst mode */ ++#define AMD_EC_CMD_QR 0x84 /* query EC */ ++ ++/* ++ * ACPI 2.0 chapter 13 access of registers of the EC ++ */ ++ ++unsigned int amd_ec_wait_write(struct amd_smbus *smbus) ++{ ++ int timeout = 500; ++ ++ while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF)) ++ udelay(1); ++ ++ if (!timeout) { ++ printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++unsigned int amd_ec_wait_read(struct amd_smbus *smbus) ++{ ++ int timeout = 500; ++ ++ while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF)) ++ udelay(1); ++ ++ if (!timeout) { ++ printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for OBF to set\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data) ++{ ++ if (amd_ec_wait_write(smbus)) ++ return -1; ++ outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); ++ ++ if (amd_ec_wait_write(smbus)) ++ return -1; ++ outb(address, smbus->base + AMD_EC_DATA); ++ ++ if (amd_ec_wait_read(smbus)) ++ return -1; ++ *data = inb(smbus->base + AMD_EC_DATA); ++ ++ return 0; ++} ++ ++unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data) ++{ ++ if (amd_ec_wait_write(smbus)) ++ return -1; ++ outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); ++ ++ if (amd_ec_wait_write(smbus)) ++ return -1; ++ outb(address, smbus->base + AMD_EC_DATA); ++ ++ if (amd_ec_wait_write(smbus)) ++ return -1; ++ outb(data, smbus->base + AMD_EC_DATA); ++ ++ return 0; ++} ++ ++/* ++ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model ++ */ ++ ++#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ ++#define AMD_SMB_STS 0x01 /* status */ ++#define AMD_SMB_ADDR 0x02 /* address */ ++#define AMD_SMB_CMD 0x03 /* command */ ++#define AMD_SMB_DATA 0x04 /* 32 data registers */ ++#define AMD_SMB_BCNT 0x24 /* number of data bytes */ ++#define AMD_SMB_ALRM_A 0x25 /* alarm address */ ++#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ ++ ++#define AMD_SMB_STS_DONE 0x80 ++#define AMD_SMB_STS_ALRM 0x40 ++#define AMD_SMB_STS_RES 0x20 ++#define AMD_SMB_STS_STATUS 0x1f ++ ++#define AMD_SMB_STATUS_OK 0x00 ++#define AMD_SMB_STATUS_FAIL 0x07 ++#define AMD_SMB_STATUS_DNAK 0x10 ++#define AMD_SMB_STATUS_DERR 0x11 ++#define AMD_SMB_STATUS_CMD_DENY 0x12 ++#define AMD_SMB_STATUS_UNKNOWN 0x13 ++#define AMD_SMB_STATUS_ACC_DENY 0x17 ++#define AMD_SMB_STATUS_TIMEOUT 0x18 ++#define AMD_SMB_STATUS_NOTSUP 0x19 ++#define AMD_SMB_STATUS_BUSY 0x1A ++#define AMD_SMB_STATUS_PEC 0x1F ++ ++#define AMD_SMB_PRTCL_WRITE 0x00 ++#define AMD_SMB_PRTCL_READ 0x01 ++#define AMD_SMB_PRTCL_QUICK 0x02 ++#define AMD_SMB_PRTCL_BYTE 0x04 ++#define AMD_SMB_PRTCL_BYTE_DATA 0x06 ++#define AMD_SMB_PRTCL_WORD_DATA 0x08 ++#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a ++#define AMD_SMB_PRTCL_PROC_CALL 0x0c ++#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d ++#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a ++#define AMD_SMB_PRTCL_PEC 0x80 ++ ++ ++s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, ++ char read_write, u8 command, int size, union i2c_smbus_data * data) ++{ ++ struct amd_smbus *smbus = adap->algo_data; ++ unsigned char protocol, len, pec, temp[2]; ++ int i; ++ ++ protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE; ++ pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; ++ ++ switch (size) { ++ ++ case I2C_SMBUS_QUICK: ++ protocol |= AMD_SMB_PRTCL_QUICK; ++ read_write = I2C_SMBUS_WRITE; ++ break; ++ ++ case I2C_SMBUS_BYTE: ++ if (read_write == I2C_SMBUS_WRITE) ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ protocol |= AMD_SMB_PRTCL_BYTE; ++ break; ++ ++ case I2C_SMBUS_BYTE_DATA: ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) ++ amd_ec_write(smbus, AMD_SMB_DATA, data->byte); ++ protocol |= AMD_SMB_PRTCL_BYTE_DATA; ++ break; ++ ++ case I2C_SMBUS_WORD_DATA: ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) { ++ amd_ec_write(smbus, AMD_SMB_DATA, data->word); ++ amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); ++ } ++ protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; ++ break; ++ ++ case I2C_SMBUS_BLOCK_DATA: ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = min_t(u8, data->block[0], 32); ++ amd_ec_write(smbus, AMD_SMB_BCNT, len); ++ for (i = 0; i < len; i++) ++ amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); ++ } ++ protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; ++ break; ++ ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ len = min_t(u8, data->block[0], 32); ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ amd_ec_write(smbus, AMD_SMB_BCNT, len); ++ if (read_write == I2C_SMBUS_WRITE) ++ for (i = 0; i < len; i++) ++ amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); ++ protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; ++ break; ++ ++ case I2C_SMBUS_PROC_CALL: ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ amd_ec_write(smbus, AMD_SMB_DATA, data->word); ++ amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); ++ protocol = AMD_SMB_PRTCL_PROC_CALL | pec; ++ read_write = I2C_SMBUS_READ; ++ break; ++ ++ case I2C_SMBUS_BLOCK_PROC_CALL: ++ protocol |= pec; ++ len = min_t(u8, data->block[0], 31); ++ amd_ec_write(smbus, AMD_SMB_CMD, command); ++ amd_ec_write(smbus, AMD_SMB_BCNT, len); ++ for (i = 0; i < len; i++) ++ amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); ++ protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; ++ read_write = I2C_SMBUS_READ; ++ break; ++ ++ case I2C_SMBUS_WORD_DATA_PEC: ++ case I2C_SMBUS_BLOCK_DATA_PEC: ++ case I2C_SMBUS_PROC_CALL_PEC: ++ case I2C_SMBUS_BLOCK_PROC_CALL_PEC: ++ printk(KERN_WARNING "i2c-amd8111.c: Unexpected software PEC transaction %d\n.", size); ++ return -1; ++ ++ default: ++ printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); ++ amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); ++ ++ amd_ec_read(smbus, AMD_SMB_STS, temp + 0); ++ ++ if (~temp[0] & AMD_SMB_STS_DONE) { ++ udelay(500); ++ amd_ec_read(smbus, AMD_SMB_STS, temp + 0); ++ } ++ ++ if (~temp[0] & AMD_SMB_STS_DONE) { ++ i2c_delay(HZ/100); ++ amd_ec_read(smbus, AMD_SMB_STS, temp + 0); ++ } ++ ++ if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) ++ return -1; ++ ++ if (read_write == I2C_SMBUS_WRITE) ++ return 0; ++ ++ switch (size) { ++ ++ case I2C_SMBUS_BYTE: ++ case I2C_SMBUS_BYTE_DATA: ++ amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); ++ break; ++ ++ case I2C_SMBUS_WORD_DATA: ++ case I2C_SMBUS_PROC_CALL: ++ amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); ++ amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); ++ data->word = (temp[1] << 8) | temp[0]; ++ break; ++ ++ case I2C_SMBUS_BLOCK_DATA: ++ case I2C_SMBUS_BLOCK_PROC_CALL: ++ amd_ec_read(smbus, AMD_SMB_BCNT, &len); ++ len = min_t(u8, len, 32); ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ for (i = 0; i < len; i++) ++ amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1); ++ data->block[0] = len; ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++u32 amd8111_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | ++ I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | ++ I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus 2.0 adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = amd8111_access, ++ .functionality = amd8111_func, ++}; ++ ++ ++static struct pci_device_id amd8111_ids[] __devinitdata = { ++ { 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ { 0, } ++}; ++ ++static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ struct amd_smbus *smbus; ++ int error; ++ ++ if (~pci_resource_flags(dev, 0) & IORESOURCE_IO) ++ return -1; ++ ++ if (!(smbus = (void*)kmalloc(sizeof(struct amd_smbus), GFP_KERNEL))) ++ return -1; ++ memset(smbus, 0, sizeof(struct amd_smbus)); ++ ++ pci_set_drvdata(dev, smbus); ++ smbus->dev = dev; ++ smbus->base = pci_resource_start(dev, 0); ++ smbus->size = pci_resource_len(dev, 0); ++ ++ if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0")) { ++ kfree(smbus); ++ return -1; ++ } ++ ++ smbus->adapter.owner = THIS_MODULE; ++ sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base); ++ smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111; ++ smbus->adapter.algo = &smbus_algorithm; ++ smbus->adapter.algo_data = smbus; ++ ++ error = i2c_add_adapter(&smbus->adapter); ++ if (error) { ++ printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n"); ++ release_region(smbus->base, smbus->size); ++ kfree(smbus); ++ return -1; ++ } ++ ++ pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); ++ ++ printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at %#x\n", smbus->base); ++ return 0; ++} ++ ++ ++static void __devexit amd8111_remove(struct pci_dev *dev) ++{ ++ struct amd_smbus *smbus = (void*) pci_get_drvdata(dev); ++ i2c_del_adapter(&smbus->adapter); ++ release_region(smbus->base, smbus->size); ++ kfree(smbus); ++} ++ ++static struct pci_driver amd8111_driver = { ++ .name = "amd8111 smbus 2.0", ++ .id_table = amd8111_ids, ++ .probe = amd8111_probe, ++ .remove = __devexit_p(amd8111_remove), ++}; ++ ++static int __init i2c_amd8111_init(void) ++{ ++ printk(KERN_INFO "i2c-amd8111.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&amd8111_driver); ++} ++ ++ ++static void __exit i2c_amd8111_exit(void) ++{ ++ pci_unregister_driver(&amd8111_driver); ++} ++ ++module_init(i2c_amd8111_init); ++module_exit(i2c_amd8111_exit); +--- linux-old/drivers/i2c/i2c-hydra.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-hydra.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,175 @@ ++/* ++ i2c-hydra.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ i2c Support for the Apple `Hydra' Mac I/O ++ ++ Copyright (c) 1999 Geert Uytterhoeven ++ ++ Based on i2c Support for Via Technologies 82C586B South Bridge ++ Copyright (c) 1998, 1999 Kyösti Mälkki ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for HZ */ ++ ++MODULE_LICENSE("GPL"); ++ ++ ++#define HYDRA_CACHE_PD 0x00000030 ++ ++#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */ ++#define HYDRA_CPD_PD1 0x00000002 ++#define HYDRA_CPD_PD2 0x00000004 ++#define HYDRA_CPD_PD3 0x00000008 ++ ++#define HYDRA_SCLK HYDRA_CPD_PD0 ++#define HYDRA_SDAT HYDRA_CPD_PD1 ++#define HYDRA_SCLK_OE 0x00000010 ++#define HYDRA_SDAT_OE 0x00000020 ++ ++static unsigned long hydra_base; ++ ++static inline void pdregw(u32 val) ++{ ++ writel(val, hydra_base + HYDRA_CACHE_PD); ++} ++ ++static inline u32 pdregr(void) ++{ ++ u32 val = readl(hydra_base + HYDRA_CACHE_PD); ++ return val; ++} ++ ++static void bit_hydra_setscl(void *data, int state) ++{ ++ u32 val = pdregr(); ++ if (state) ++ val &= ~HYDRA_SCLK_OE; ++ else { ++ val &= ~HYDRA_SCLK; ++ val |= HYDRA_SCLK_OE; ++ } ++ pdregw(val); ++ pdregr(); /* flush posted write */ ++} ++ ++static void bit_hydra_setsda(void *data, int state) ++{ ++ u32 val = pdregr(); ++ if (state) ++ val &= ~HYDRA_SDAT_OE; ++ else { ++ val &= ~HYDRA_SDAT; ++ val |= HYDRA_SDAT_OE; ++ } ++ pdregw(val); ++ pdregr(); /* flush posted write */ ++} ++ ++static int bit_hydra_getscl(void *data) ++{ ++ return (pdregr() & HYDRA_SCLK) != 0; ++} ++ ++static int bit_hydra_getsda(void *data) ++{ ++ return (pdregr() & HYDRA_SDAT) != 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static struct i2c_algo_bit_data bit_hydra_data = { ++ .setsda = bit_hydra_setsda, ++ .setscl = bit_hydra_setscl, ++ .getsda = bit_hydra_getsda, ++ .getscl = bit_hydra_getscl, ++ .udelay = 5, ++ .mdelay = 5, ++ .timeout = HZ ++}; ++ ++static struct i2c_adapter bit_hydra_ops = { ++ .owner = THIS_MODULE, ++ .name = "Hydra i2c", ++ .id = I2C_HW_B_HYDRA, ++ .algo_data = &bit_hydra_data, ++}; ++ ++static struct pci_device_id hydra_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_APPLE, ++ .device = PCI_DEVICE_ID_APPLE_HYDRA, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit hydra_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ unsigned int base_addr; ++ ++ base_addr = dev->resource[0].start; ++ hydra_base = (unsigned long) ioremap(base_addr, 0x100); ++ ++ pdregw(0); /* clear SCLK_OE and SDAT_OE */ ++ return i2c_bit_add_bus(&bit_hydra_ops); ++} ++ ++static void __devexit hydra_remove(struct pci_dev *dev) ++{ ++ pdregw(0); /* clear SCLK_OE and SDAT_OE */ ++ i2c_bit_del_bus(&bit_hydra_ops); ++ iounmap((void *) hydra_base); ++} ++ ++ ++static struct pci_driver hydra_driver = { ++ .name = "hydra smbus", ++ .id_table = hydra_ids, ++ .probe = hydra_probe, ++ .remove = __devexit_p(hydra_remove), ++}; ++ ++static int __init i2c_hydra_init(void) ++{ ++ return pci_module_init(&hydra_driver); ++} ++ ++ ++static void __exit i2c_hydra_exit(void) ++{ ++ pci_unregister_driver(&hydra_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Geert Uytterhoeven "); ++MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); ++ ++module_init(i2c_hydra_init); ++module_exit(i2c_hydra_exit); ++ +--- linux-old/drivers/i2c/i2c-i801.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-i801.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,656 @@ ++/* ++ i801.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998 - 2002 Frodo Looijaard , ++ Philip Edelbrock , and Mark D. Studebaker ++ ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ SUPPORTED DEVICES PCI ID ++ 82801AA 2413 ++ 82801AB 2423 ++ 82801BA 2443 ++ 82801CA/CAM 2483 ++ 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) ++ 82801EB 24D3 (HW PEC supported, 32 byte buffer not supported) ++ 6300ESB 25A4 ("") ++ ICH6 266A ++ This driver supports several versions of Intel's I/O Controller Hubs (ICH). ++ For SMBus support, they are similar to the PIIX4 and are part ++ of Intel's '810' and other chipsets. ++ See the doc/busses/i2c-i801 file for details. ++ I2C Block Read and Process Call are not supported. ++*/ ++ ++/* Note: we assume there can only be one I801, with one SMBus interface */ ++ ++/* #define DEBUG 1 */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++/* 82801DB is undefined before kernel 2.4.19 */ ++#ifndef PCI_DEVICE_ID_INTEL_82801DB_3 ++#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 ++#endif ++ ++#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC ++#define HAVE_PEC ++#endif ++ ++/* I801 SMBus address offsets */ ++#define SMBHSTSTS (0 + i801_smba) ++#define SMBHSTCNT (2 + i801_smba) ++#define SMBHSTCMD (3 + i801_smba) ++#define SMBHSTADD (4 + i801_smba) ++#define SMBHSTDAT0 (5 + i801_smba) ++#define SMBHSTDAT1 (6 + i801_smba) ++#define SMBBLKDAT (7 + i801_smba) ++#define SMBPEC (8 + i801_smba) /* ICH4 only */ ++#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ ++#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ ++ ++/* PCI Address Constants */ ++#define SMBBA 0x020 ++#define SMBHSTCFG 0x040 ++#define SMBREV 0x008 ++ ++/* Host configuration bits for SMBHSTCFG */ ++#define SMBHSTCFG_HST_EN 1 ++#define SMBHSTCFG_SMB_SMI_EN 2 ++#define SMBHSTCFG_I2C_EN 4 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 100 ++#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ ++ ++/* I801 command constants */ ++#define I801_QUICK 0x00 ++#define I801_BYTE 0x04 ++#define I801_BYTE_DATA 0x08 ++#define I801_WORD_DATA 0x0C ++#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ ++#define I801_BLOCK_DATA 0x14 ++#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ ++#define I801_BLOCK_LAST 0x34 ++#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ ++#define I801_START 0x40 ++#define I801_PEC_EN 0x80 /* ICH4 only */ ++ ++/* insmod parameters */ ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the I801 at the given address. VERY DANGEROUS! */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Forcibly enable the I801 at the given address. " ++ "EXTREMELY DANGEROUS!"); ++ ++static int i801_transaction(void); ++static int i801_block_transaction(union i2c_smbus_data *data, ++ char read_write, int command); ++ ++static unsigned short i801_smba; ++static struct pci_dev *I801_dev; ++static int isich4; ++ ++static int i801_setup(struct pci_dev *dev) ++{ ++ int error_return = 0; ++ unsigned char temp; ++ ++ /* Note: we keep on searching until we have found 'function 3' */ ++ if(PCI_FUNC(dev->devfn) != 3) ++ return -ENODEV; ++ ++ I801_dev = dev; ++ if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_3 || ++ dev->device == 0x24d3 || ++ dev->device == 0x25a4) ++ isich4 = 1; ++ else ++ isich4 = 0; ++ ++ /* Determine the address of the SMBus areas */ ++ if (force_addr) { ++ i801_smba = force_addr & 0xfff0; ++ } else { ++ pci_read_config_word(I801_dev, SMBBA, &i801_smba); ++ i801_smba &= 0xfff0; ++ if(i801_smba == 0) { ++ dev_err(dev, "SMB base address uninitialized" ++ "- upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ } ++ ++ if (!request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus")) { ++ dev_err(dev, "I801_smb region 0x%x already in use!\n", ++ i801_smba); ++ error_return = -EBUSY; ++ goto END; ++ } ++ ++ pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); ++ temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ ++ pci_write_config_byte(I801_dev, SMBHSTCFG, temp); ++ ++ /* If force_addr is set, we program the new address here. Just to make ++ sure, we disable the device first. */ ++ if (force_addr) { ++ pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); ++ pci_write_config_word(I801_dev, SMBBA, i801_smba); ++ pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); ++ dev_warn(dev, "WARNING: I801 SMBus interface set to " ++ "new address %04x!\n", i801_smba); ++ } else if ((temp & 1) == 0) { ++ pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); ++ dev_warn(dev, "enabling SMBus device\n"); ++ } ++ ++ if (temp & 0x02) ++ dev_dbg(dev, "I801 using Interrupt SMI# for SMBus.\n"); ++ else ++ dev_dbg(dev, "I801 using PCI Interrupt for SMBus.\n"); ++ ++ pci_read_config_byte(I801_dev, SMBREV, &temp); ++ dev_dbg(dev, "SMBREV = 0x%X\n", temp); ++ dev_dbg(dev, "I801_smba = 0x%X\n", i801_smba); ++ ++END: ++ return error_return; ++} ++ ++ ++static int i801_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ dev_dbg(I801_dev, "Transaction (pre): CNT=%02x, CMD=%02x," ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ ++ if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { ++ dev_dbg(I801_dev, "SMBus busy (%02x). Resetting... \n", ++ temp); ++ outb_p(temp, SMBHSTSTS); ++ if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { ++ dev_dbg(I801_dev, "Failed! (%02x)\n", temp); ++ return -1; ++ } else { ++ dev_dbg(I801_dev, "Successfull!\n"); ++ } ++ } ++ ++ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); ++ ++ /* We will always wait for a fraction of a second! */ ++ do { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ dev_dbg(I801_dev, "SMBus Timeout!\n"); ++ result = -1; ++ } ++ ++ if (temp & 0x10) { ++ result = -1; ++ dev_dbg(I801_dev, "Error: Failed bus transaction\n"); ++ } ++ ++ if (temp & 0x08) { ++ result = -1; ++ dev_err(I801_dev, "Bus collision! SMBus may be locked " ++ "until next hard reset. (sorry!)\n"); ++ /* Clock stops and slave is stuck in mid-transmission */ ++ } ++ ++ if (temp & 0x04) { ++ result = -1; ++ dev_dbg(I801_dev, "Error: no response!\n"); ++ } ++ ++ if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) ++ outb_p(inb(SMBHSTSTS), SMBHSTSTS); ++ ++ if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { ++ dev_dbg(I801_dev, "Failed reset at end of transaction" ++ "(%02x)\n", temp); ++ } ++ dev_dbg(I801_dev, "Transaction (post): CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++ return result; ++} ++ ++/* All-inclusive block transaction function */ ++static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ++ int command) ++{ ++ int i, len; ++ int smbcmd; ++ int temp; ++ int result = 0; ++ int timeout; ++ unsigned char hostc, errmask; ++ ++ if (command == I2C_SMBUS_I2C_BLOCK_DATA) { ++ if (read_write == I2C_SMBUS_WRITE) { ++ /* set I2C_EN bit in configuration register */ ++ pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); ++ pci_write_config_byte(I801_dev, SMBHSTCFG, ++ hostc | SMBHSTCFG_I2C_EN); ++ } else { ++ dev_err(I801_dev, ++ "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); ++ return -1; ++ } ++ } ++ ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 1) ++ len = 1; ++ if (len > 32) ++ len = 32; ++ outb_p(len, SMBHSTDAT0); ++ outb_p(data->block[1], SMBBLKDAT); ++ } else { ++ len = 32; /* max for reads */ ++ } ++ ++ if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { ++ /* set 32 byte buffer */ ++ } ++ ++ for (i = 1; i <= len; i++) { ++ if (i == len && read_write == I2C_SMBUS_READ) ++ smbcmd = I801_BLOCK_LAST; ++ else ++ smbcmd = I801_BLOCK_DATA; ++ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); ++ ++ dev_dbg(I801_dev, "Block (pre %d): CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i, ++ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), ++ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ temp = inb_p(SMBHSTSTS); ++ if (i == 1) { ++ /* Erronenous conditions before transaction: ++ * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ ++ errmask=0x9f; ++ } else { ++ /* Erronenous conditions during transaction: ++ * Failed, Bus_Err, Dev_Err, Intr */ ++ errmask=0x1e; ++ } ++ if (temp & errmask) { ++ dev_dbg(I801_dev, "SMBus busy (%02x). " ++ "Resetting... \n", temp); ++ outb_p(temp, SMBHSTSTS); ++ if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { ++ dev_err(I801_dev, ++ "Reset failed! (%02x)\n", temp); ++ result = -1; ++ goto END; ++ } ++ if (i != 1) { ++ /* if die in middle of block transaction, fail */ ++ result = -1; ++ goto END; ++ } ++ } ++ ++ if (i == 1) ++ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); ++ ++ /* We will always wait for a fraction of a second! */ ++ timeout = 0; ++ do { ++ temp = inb_p(SMBHSTSTS); ++ i2c_delay(1); ++ } ++ while ((!(temp & 0x80)) ++ && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ result = -1; ++ dev_dbg(I801_dev, "SMBus Timeout!\n"); ++ } ++ ++ if (temp & 0x10) { ++ result = -1; ++ dev_dbg(I801_dev, ++ "Error: Failed bus transaction\n"); ++ } else if (temp & 0x08) { ++ result = -1; ++ dev_err(I801_dev, "Bus collision!\n"); ++ } else if (temp & 0x04) { ++ result = -1; ++ dev_dbg(I801_dev, "Error: no response!\n"); ++ } ++ ++ if (i == 1 && read_write == I2C_SMBUS_READ) { ++ len = inb_p(SMBHSTDAT0); ++ if (len < 1) ++ len = 1; ++ if (len > 32) ++ len = 32; ++ data->block[0] = len; ++ } ++ ++ /* Retrieve/store value in SMBBLKDAT */ ++ if (read_write == I2C_SMBUS_READ) ++ data->block[i] = inb_p(SMBBLKDAT); ++ if (read_write == I2C_SMBUS_WRITE && i+1 <= len) ++ outb_p(data->block[i+1], SMBBLKDAT); ++ if ((temp & 0x9e) != 0x00) ++ outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ ++ ++ if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { ++ dev_dbg(I801_dev, ++ "Bad status (%02x) at end of transaction\n", ++ temp); ++ } ++ dev_dbg(I801_dev, "Block (post %d): CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i, ++ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), ++ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); ++ ++ if (result < 0) ++ goto END; ++ } ++ ++#ifdef HAVE_PEC ++ if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { ++ /* wait for INTR bit as advised by Intel */ ++ timeout = 0; ++ do { ++ temp = inb_p(SMBHSTSTS); ++ i2c_delay(1); ++ } while ((!(temp & 0x02)) ++ && (timeout++ < MAX_TIMEOUT)); ++ ++ if (timeout >= MAX_TIMEOUT) { ++ dev_dbg(I801_dev, "PEC Timeout!\n"); ++ } ++ outb_p(temp, SMBHSTSTS); ++ } ++#endif ++ result = 0; ++END: ++ if (command == I2C_SMBUS_I2C_BLOCK_DATA) { ++ /* restore saved configuration register value */ ++ pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); ++ } ++ return result; ++} ++ ++/* Return -1 on error. */ ++static s32 i801_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, u8 command, ++ int size, union i2c_smbus_data * data) ++{ ++ int hwpec = 0; ++ int block = 0; ++ int ret, xact = 0; ++ ++#ifdef HAVE_PEC ++ if(isich4) ++ hwpec = (flags & I2C_CLIENT_PEC) != 0; ++#endif ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ xact = I801_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMBHSTCMD); ++ xact = I801_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, SMBHSTDAT0); ++ xact = I801_BYTE_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word & 0xff, SMBHSTDAT0); ++ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); ++ } ++ xact = I801_WORD_DATA; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++#ifdef HAVE_PEC ++ case I2C_SMBUS_BLOCK_DATA_PEC: ++ if(hwpec && size == I2C_SMBUS_BLOCK_DATA) ++ size = I2C_SMBUS_BLOCK_DATA_PEC; ++#endif ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ block = 1; ++ break; ++ case I2C_SMBUS_PROC_CALL: ++ default: ++ dev_err(I801_dev, "Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++#ifdef HAVE_PEC ++ if(isich4 && hwpec) { ++ if(size != I2C_SMBUS_QUICK && ++ size != I2C_SMBUS_I2C_BLOCK_DATA) ++ outb_p(1, SMBAUXCTL); /* enable HW PEC */ ++ } ++#endif ++ if(block) ++ ret = i801_block_transaction(data, read_write, size); ++ else { ++ outb_p(xact | ENABLE_INT9, SMBHSTCNT); ++ ret = i801_transaction(); ++ } ++ ++#ifdef HAVE_PEC ++ if(isich4 && hwpec) { ++ if(size != I2C_SMBUS_QUICK && ++ size != I2C_SMBUS_I2C_BLOCK_DATA) ++ outb_p(0, SMBAUXCTL); ++ } ++#endif ++ ++ if(block) ++ return ret; ++ if(ret) ++ return -1; ++ if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) ++ return 0; ++ ++ switch (xact & 0x7f) { ++ case I801_BYTE: /* Result put in SMBHSTDAT0 */ ++ case I801_BYTE_DATA: ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case I801_WORD_DATA: ++ data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); ++ break; ++ } ++ return 0; ++} ++ ++ ++static u32 i801_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK ++#ifdef HAVE_PEC ++ | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC | ++ I2C_FUNC_SMBUS_HWPEC_CALC ++ : 0) ++#endif ++ ; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = i801_access, ++ .functionality = i801_func, ++}; ++ ++static struct i2c_adapter i801_adapter = { ++ .owner = THIS_MODULE, ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, ++ .algo = &smbus_algorithm, ++ .name = "unset", ++}; ++ ++static struct pci_device_id i801_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82801AA_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82801AB_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82801BA_2, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82801CA_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82801DB_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x24d3, /* 82801EB ICH5 */ ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x25a4, /* PCI_DEVICE_ID_INTEL_ESB_4 */ ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x266a, /* PCI_DEVICE_ID_INTEL_ICH6_16 */ ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ ++ if (i801_setup(dev)) { ++ dev_warn(dev, ++ "I801 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ ++ snprintf(i801_adapter.name, 32, ++ "SMBus I801 adapter at %04x", i801_smba); ++ return i2c_add_adapter(&i801_adapter); ++} ++ ++static void __devexit i801_remove(struct pci_dev *dev) ++{ ++ i2c_del_adapter(&i801_adapter); ++ release_region(i801_smba, (isich4 ? 16 : 8)); ++} ++ ++static struct pci_driver i801_driver = { ++ .name = "i801 smbus", ++ .id_table = i801_ids, ++ .probe = i801_probe, ++ .remove = __devexit_p(i801_remove), ++}; ++ ++static int __init i2c_i801_init(void) ++{ ++ printk(KERN_INFO "i2c-i801 version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&i801_driver); ++} ++ ++static void __exit i2c_i801_exit(void) ++{ ++ pci_unregister_driver(&i801_driver); ++} ++ ++MODULE_AUTHOR ("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "and Mark D. Studebaker "); ++MODULE_DESCRIPTION("I801 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_i801_init); ++module_exit(i2c_i801_exit); +--- linux-old/drivers/i2c/i2c-i810.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-i810.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,310 @@ ++/* ++ i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999, 2000 Frodo Looijaard , ++ Philip Edelbrock , ++ Ralph Metzler , and ++ Mark D. Studebaker ++ ++ Based on code written by Ralph Metzler and ++ Simon Vogl ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++/* ++ This interfaces to the I810/I815 to provide access to ++ the DDC Bus and the I2C Bus. ++ ++ SUPPORTED DEVICES PCI ID ++ i810AA 7121 ++ i810AB 7123 ++ i810E 7125 ++ i815 1132 ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for HZ */ ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++#ifndef PCI_DEVICE_ID_INTEL_82815_2 ++#define PCI_DEVICE_ID_INTEL_82815_2 0x1132 ++#endif ++ ++/* GPIO register locations */ ++#define I810_IOCONTROL_OFFSET 0x5000 ++#define I810_HVSYNC 0x00 /* not used */ ++#define I810_GPIOA 0x10 ++#define I810_GPIOB 0x14 ++ ++/* bit locations in the registers */ ++#define SCL_DIR_MASK 0x0001 ++#define SCL_DIR 0x0002 ++#define SCL_VAL_MASK 0x0004 ++#define SCL_VAL_OUT 0x0008 ++#define SCL_VAL_IN 0x0010 ++#define SDA_DIR_MASK 0x0100 ++#define SDA_DIR 0x0200 ++#define SDA_VAL_MASK 0x0400 ++#define SDA_VAL_OUT 0x0800 ++#define SDA_VAL_IN 0x1000 ++ ++/* initialization states */ ++#define INIT1 0x1 ++#define INIT2 0x2 ++#define INIT3 0x4 ++ ++/* delays */ ++#define CYCLE_DELAY 10 ++#define TIMEOUT (HZ / 2) ++ ++ ++static void config_i810(struct pci_dev *dev); ++ ++ ++static unsigned long ioaddr; ++ ++/* The i810 GPIO registers have individual masks for each bit ++ so we never have to read before writing. Nice. */ ++ ++static void bit_i810i2c_setscl(void *data, int val) ++{ ++ writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, ++ ioaddr + I810_GPIOB); ++ readl(ioaddr + I810_GPIOB); /* flush posted write */ ++} ++ ++static void bit_i810i2c_setsda(void *data, int val) ++{ ++ writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, ++ ioaddr + I810_GPIOB); ++ readl(ioaddr + I810_GPIOB); /* flush posted write */ ++} ++ ++/* The GPIO pins are open drain, so the pins could always remain outputs. ++ However, some chip versions don't latch the inputs unless they ++ are set as inputs. ++ We rely on the i2c-algo-bit routines to set the pins high before ++ reading the input from other chips. Following guidance in the 815 ++ prog. ref. guide, we do a "dummy write" of 0 to the register before ++ reading which forces the input value to be latched. We presume this ++ applies to the 810 as well; shouldn't hurt anyway. This is necessary to get ++ i2c_algo_bit bit_test=1 to pass. */ ++ ++static int bit_i810i2c_getscl(void *data) ++{ ++ writel(SCL_DIR_MASK, ioaddr + I810_GPIOB); ++ writel(0, ioaddr + I810_GPIOB); ++ return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN)); ++} ++ ++static int bit_i810i2c_getsda(void *data) ++{ ++ writel(SDA_DIR_MASK, ioaddr + I810_GPIOB); ++ writel(0, ioaddr + I810_GPIOB); ++ return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN)); ++} ++ ++static void bit_i810ddc_setscl(void *data, int val) ++{ ++ writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, ++ ioaddr + I810_GPIOA); ++ readl(ioaddr + I810_GPIOA); /* flush posted write */ ++} ++ ++static void bit_i810ddc_setsda(void *data, int val) ++{ ++ writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, ++ ioaddr + I810_GPIOA); ++ readl(ioaddr + I810_GPIOA); /* flush posted write */ ++} ++ ++static int bit_i810ddc_getscl(void *data) ++{ ++ writel(SCL_DIR_MASK, ioaddr + I810_GPIOA); ++ writel(0, ioaddr + I810_GPIOA); ++ return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN)); ++} ++ ++static int bit_i810ddc_getsda(void *data) ++{ ++ writel(SDA_DIR_MASK, ioaddr + I810_GPIOA); ++ writel(0, ioaddr + I810_GPIOA); ++ return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN)); ++} ++ ++ ++/* Configures the chip */ ++void config_i810(struct pci_dev *dev) ++{ ++ unsigned long cadr; ++ ++ /* map I810 memory */ ++ cadr = dev->resource[1].start; ++ cadr += I810_IOCONTROL_OFFSET; ++ cadr &= PCI_BASE_ADDRESS_MEM_MASK; ++ ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000); ++ if(ioaddr) { ++ bit_i810i2c_setscl(NULL, 1); ++ bit_i810i2c_setsda(NULL, 1); ++ bit_i810ddc_setscl(NULL, 1); ++ bit_i810ddc_setsda(NULL, 1); ++ } ++} ++ ++ ++static struct i2c_algo_bit_data i810_i2c_bit_data = { ++ .setsda = bit_i810i2c_setsda, ++ .setscl = bit_i810i2c_setscl, ++ .getsda = bit_i810i2c_getsda, ++ .getscl = bit_i810i2c_getscl, ++ .udelay = CYCLE_DELAY, ++ .mdelay = CYCLE_DELAY, ++ .timeout = TIMEOUT, ++}; ++ ++static struct i2c_adapter i810_i2c_adapter = { ++ .owner = THIS_MODULE, ++ .name = "I810/I815 I2C Adapter", ++ .id = I2C_HW_B_I810, ++ .algo_data = &i810_i2c_bit_data, ++}; ++ ++static struct i2c_algo_bit_data i810_ddc_bit_data = { ++ .setsda = bit_i810ddc_setsda, ++ .setscl = bit_i810ddc_setscl, ++ .getsda = bit_i810ddc_getsda, ++ .getscl = bit_i810ddc_getscl, ++ .udelay = CYCLE_DELAY, ++ .mdelay = CYCLE_DELAY, ++ .timeout = TIMEOUT, ++}; ++ ++static struct i2c_adapter i810_ddc_adapter = { ++ .owner = THIS_MODULE, ++ .name = "I810/I815 DDC Adapter", ++ .id = I2C_HW_B_I810, ++ .algo_data = &i810_ddc_bit_data, ++}; ++ ++ ++static struct pci_device_id i810_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82810_IG1, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82810_IG3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x7125, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82815_2, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x2562, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ int retval; ++ ++ config_i810(dev); ++ printk("i2c-i810.o: i810/i815 found.\n"); ++ ++ retval = i2c_bit_add_bus(&i810_i2c_adapter); ++ if(retval) ++ return retval; ++ retval = i2c_bit_add_bus(&i810_ddc_adapter); ++ if(retval) ++ i2c_bit_del_bus(&i810_i2c_adapter); ++ return retval; ++} ++ ++static void __devexit i810_remove(struct pci_dev *dev) ++{ ++ i2c_bit_del_bus(&i810_ddc_adapter); ++ i2c_bit_del_bus(&i810_i2c_adapter); ++} ++ ++ ++/* Don't register driver to avoid driver conflicts */ ++/* ++static struct pci_driver i810_driver = { ++ .name = "i810 smbus", ++ .id_table = i810_ids, ++ .probe = i810_probe, ++ .remove = __devexit_p(i810_remove), ++}; ++*/ ++ ++static int __init i2c_i810_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE); ++/* ++ return pci_module_init(&i810_driver); ++*/ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(i810_ids, dev); ++ if(id) ++ if(i810_probe(dev, id) >= 0) ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++static void __exit i2c_i810_exit(void) ++{ ++/* ++ pci_unregister_driver(&i810_driver); ++*/ ++ i810_remove(NULL); ++ iounmap((void *)ioaddr); ++} ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); ++MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); ++ ++module_init(i2c_i810_init); ++module_exit(i2c_i810_exit); +--- linux-old/drivers/i2c/i2c-isa.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-isa.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,74 @@ ++/* ++ i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* This implements an i2c algorithm/adapter for ISA bus. Not that this is ++ on first sight very useful; almost no functionality is preserved. ++ Except that it makes writing drivers for chips which can be on both ++ the SMBus and the ISA bus very much easier. See lm78.c for an example ++ of this. */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++static u32 isa_func(struct i2c_adapter *adapter); ++ ++/* This is the actual algorithm we define */ ++static struct i2c_algorithm isa_algorithm = { ++ .name = "ISA bus algorithm", ++ .id = I2C_ALGO_ISA, ++ .functionality = isa_func, ++}; ++ ++/* There can only be one... */ ++static struct i2c_adapter isa_adapter = { ++ .owner = THIS_MODULE, ++ .name = "ISA main adapter", ++ .id = I2C_ALGO_ISA | I2C_HW_ISA, ++ .algo = &isa_algorithm, ++}; ++ ++/* We can't do a thing... */ ++static u32 isa_func(struct i2c_adapter *adapter) ++{ ++ return 0; ++} ++ ++static int __init i2c_isa_init(void) ++{ ++ printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_adapter(&isa_adapter); ++} ++ ++static void __exit i2c_isa_exit(void) ++{ ++ i2c_del_adapter(&isa_adapter); ++} ++ ++MODULE_AUTHOR("Frodo Looijaard "); ++MODULE_DESCRIPTION("ISA bus access through i2c"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_isa_init); ++module_exit(i2c_isa_exit); +--- linux-old/drivers/i2c/i2c-nforce2.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-nforce2.c Mon Dec 13 20:18:41 2004 +@@ -0,0 +1,409 @@ ++/* ++ SMBus driver for nVidia nForce2 MCP ++ ++ Copyright (c) 2003 Hans-Frieder Vogt , ++ Based on ++ SMBus 2.0 driver for AMD-8111 IO-Hub ++ Copyright (c) 2002 Vojtech Pavlik ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ SUPPORTED DEVICES PCI ID ++ nForce2 MCP 0064 ++ ++ This driver supports the 2 SMBuses that are included in the MCP2 of the ++ nForce2 chipset. ++*/ ++ ++/* Note: we assume there can only be one nForce2, with two SMBus interfaces */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* kernel 2.4.9 needs this */ ++#ifndef min_t ++#define min_t(type,x,y) min(type,x,y) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR ("Hans-Frieder Vogt "); ++MODULE_DESCRIPTION("nForce2 SMBus driver"); ++ ++#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS ++#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 ++#endif ++ ++ ++struct nforce2_smbus { ++ struct pci_dev *dev; ++ struct i2c_adapter adapter; ++ int base; ++ int size; ++}; ++ ++ ++/* ++ * nVidia nForce2 SMBus control register definitions ++ */ ++#define NFORCE_PCI_SMB1 0x50 ++#define NFORCE_PCI_SMB2 0x54 ++ ++ ++/* ++ * ACPI 2.0 chapter 13 SMBus 2.0 EC register model ++ */ ++#define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */ ++#define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */ ++#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */ ++#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */ ++#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */ ++#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data bytes */ ++#define NVIDIA_SMB_ALRM_A (smbus->base + 0x25) /* alarm address */ ++#define NVIDIA_SMB_ALRM_D (smbus->base + 0x26) /* 2 bytes alarm data */ ++ ++#define NVIDIA_SMB_STS_DONE 0x80 ++#define NVIDIA_SMB_STS_ALRM 0x40 ++#define NVIDIA_SMB_STS_RES 0x20 ++#define NVIDIA_SMB_STS_STATUS 0x1f ++ ++#define NVIDIA_SMB_PRTCL_WRITE 0x00 ++#define NVIDIA_SMB_PRTCL_READ 0x01 ++#define NVIDIA_SMB_PRTCL_QUICK 0x02 ++#define NVIDIA_SMB_PRTCL_BYTE 0x04 ++#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06 ++#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08 ++#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a ++#define NVIDIA_SMB_PRTCL_PROC_CALL 0x0c ++#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL 0x0d ++#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA 0x4a ++#define NVIDIA_SMB_PRTCL_PEC 0x80 ++ ++ ++/* Other settings */ ++#define MAX_TIMEOUT 256 ++ ++ ++ ++static s32 nforce2_access(struct i2c_adapter *adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data *data); ++/* ++static int nforce2_block_transaction(union i2c_smbus_data *data, ++ char read_write, int i2c_enable); ++ */ ++static u32 nforce2_func(struct i2c_adapter *adapter); ++ ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = nforce2_access, ++ .functionality = nforce2_func, ++}; ++ ++/* Return -1 on error. See smbus.h for more information */ ++s32 nforce2_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, ++ char read_write, u8 command, int size, ++ union i2c_smbus_data * data) ++{ ++ struct nforce2_smbus *smbus = adap->algo_data; ++ unsigned char protocol, pec, temp; ++ unsigned char len = 0; /* to keep the compiler quiet */ ++ int timeout = 0; ++ int i; ++ ++ protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : NVIDIA_SMB_PRTCL_WRITE; ++ pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0; ++ ++ switch (size) { ++ ++ case I2C_SMBUS_QUICK: ++ protocol |= NVIDIA_SMB_PRTCL_QUICK; ++ read_write = I2C_SMBUS_WRITE; ++ break; ++ ++ case I2C_SMBUS_BYTE: ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, NVIDIA_SMB_CMD); ++ protocol |= NVIDIA_SMB_PRTCL_BYTE; ++ break; ++ ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(command, NVIDIA_SMB_CMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, NVIDIA_SMB_DATA); ++ protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA; ++ break; ++ ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(command, NVIDIA_SMB_CMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word, NVIDIA_SMB_DATA); ++ outb_p(data->word >> 8, NVIDIA_SMB_DATA+1); ++ } ++ protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec; ++ break; ++ ++ case I2C_SMBUS_BLOCK_DATA: ++ outb_p(command, NVIDIA_SMB_CMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = min_t(u8, data->block[0], 32); ++ outb_p(len, NVIDIA_SMB_BCNT); ++ for (i = 0; i < len; i++) ++ outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i); ++ } ++ protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec; ++ break; ++ ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ len = min_t(u8, data->block[0], 32); ++ outb_p(command, NVIDIA_SMB_CMD); ++ outb_p(len, NVIDIA_SMB_BCNT); ++ if (read_write == I2C_SMBUS_WRITE) ++ for (i = 0; i < len; i++) ++ outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i); ++ protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA; ++ break; ++ ++ case I2C_SMBUS_PROC_CALL: ++ printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_PROC_CALL not supported!\n"); ++ return -1; ++ /* ++ outb_p(command, NVIDIA_SMB_CMD); ++ outb_p(data->word, NVIDIA_SMB_DATA); ++ outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1); ++ protocol = NVIDIA_SMB_PRTCL_PROC_CALL | pec; ++ read_write = I2C_SMBUS_READ; ++ break; ++ */ ++ ++ case I2C_SMBUS_BLOCK_PROC_CALL: ++ printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_BLOCK_PROC_CALL not supported!\n"); ++ return -1; ++ /* ++ protocol |= pec; ++ len = min_t(u8, data->block[0], 31); ++ outb_p(command, NVIDIA_SMB_CMD); ++ outb_p(len, NVIDIA_SMB_BCNT); ++ for (i = 0; i < len; i++) ++ outb_p(data->block[i + 1], NVIDIA_SMB_DATA + i); ++ protocol = NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL | pec; ++ read_write = I2C_SMBUS_READ; ++ break; ++ */ ++ ++ case I2C_SMBUS_WORD_DATA_PEC: ++ case I2C_SMBUS_BLOCK_DATA_PEC: ++ case I2C_SMBUS_PROC_CALL_PEC: ++ case I2C_SMBUS_BLOCK_PROC_CALL_PEC: ++ printk(KERN_WARNING "i2c-nforce2.c: Unexpected software PEC transaction %d\n.", size); ++ return -1; ++ ++ default: ++ printk(KERN_WARNING "i2c-nforce2.c: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR); ++ outb_p(protocol, NVIDIA_SMB_PRTCL); ++ ++ temp = inb_p(NVIDIA_SMB_STS); ++ ++#if 0 ++ do { ++ i2c_delay(1); ++ temp = inb_p(NVIDIA_SMB_STS); ++ } while (((temp & NVIDIA_SMB_STS_DONE) == 0) && (timeout++ < MAX_TIMEOUT)); ++#endif ++ if (~temp & NVIDIA_SMB_STS_DONE) { ++ udelay(500); ++ temp = inb_p(NVIDIA_SMB_STS); ++ } ++ if (~temp & NVIDIA_SMB_STS_DONE) { ++ i2c_delay(HZ/100); ++ temp = inb_p(NVIDIA_SMB_STS); ++ } ++ ++ if ((timeout >= MAX_TIMEOUT) || (~temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) ++ return -1; ++ ++ if (read_write == I2C_SMBUS_WRITE) ++ return 0; ++ ++ switch (size) { ++ ++ case I2C_SMBUS_BYTE: ++ case I2C_SMBUS_BYTE_DATA: ++ data->byte = inb_p(NVIDIA_SMB_DATA); ++ break; ++ ++ case I2C_SMBUS_WORD_DATA: ++ /* case I2C_SMBUS_PROC_CALL: not supported */ ++ data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8); ++ break; ++ ++ case I2C_SMBUS_BLOCK_DATA: ++ /* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */ ++ len = inb_p(NVIDIA_SMB_BCNT); ++ len = min_t(u8, len, 32); ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ for (i = 0; i < len; i++) ++ data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i); ++ data->block[0] = len; ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++u32 nforce2_func(struct i2c_adapter *adapter) ++{ ++ /* other functionality might be possible, but is not tested */ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* | ++ I2C_FUNC_SMBUS_BLOCK_DATA */; ++} ++ ++ ++static struct pci_device_id nforce2_ids[] = { ++ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ { 0 } ++}; ++ ++ ++static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg, struct nforce2_smbus *smbus, char *name) ++{ ++ u16 iobase; ++ int error; ++ ++ if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) { ++ printk (KERN_ERR "i2c-nforce2.o: Error reading PCI config for %s\n", name); ++ return -1; ++ } ++ smbus->dev = dev; ++ smbus->base = iobase & 0xfffc; ++ smbus->size = 8; ++ ++ if (!request_region(smbus->base, smbus->size, "nForce2 SMBus")) { ++ printk (KERN_ERR "i2c-nforce2.o: Error requesting region %02x .. %02X for %s\n", smbus->base, smbus->base+smbus->size-1, name); ++ return -1; ++ } ++ ++ /* TODO: find a better way to find out whether this file is compiled ++ * with i2c 2.7.0 of earlier ++ */ ++#ifdef I2C_HW_SMBUS_AMD8111 ++ smbus->adapter.owner = THIS_MODULE; ++#endif ++ sprintf(smbus->adapter.name, "SMBus nForce2 adapter at %04x", smbus->base); ++ smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_NFORCE2; ++ smbus->adapter.algo = &smbus_algorithm; ++ smbus->adapter.algo_data = smbus; ++ ++ error = i2c_add_adapter(&smbus->adapter); ++ if (error) { ++ printk(KERN_WARNING "i2c-nforce2.o: Failed to register adapter.\n"); ++ release_region(smbus->base, smbus->size); ++ return -1; ++ } ++ printk(KERN_INFO "i2c-nforce2.o: nForce2 SMBus adapter at %#x\n", smbus->base); ++ return 0; ++} ++ ++ ++static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ struct nforce2_smbus *smbuses; ++ int res1, res2; ++ ++ /* we support 2 SMBus adapters */ ++ if (!(smbuses = (void *)kmalloc(2*sizeof(struct nforce2_smbus), ++ GFP_KERNEL))) ++ return -ENOMEM; ++ memset (smbuses, 0, 2*sizeof(struct nforce2_smbus)); ++ pci_set_drvdata(dev, smbuses); ++ ++ /* SMBus adapter 1 */ ++ res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); ++ if (res1 < 0) { ++ printk (KERN_ERR "i2c-nforce2.o: Error probing SMB1.\n"); ++ smbuses[0].base = 0; /* to have a check value */ ++ } ++ res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2"); ++ if (res2 < 0) { ++ printk (KERN_ERR "i2c-nforce2.o: Error probing SMB2.\n"); ++ smbuses[1].base = 0; /* to have a check value */ ++ } ++ if ((res1 < 0) && (res2 < 0)) { ++ /* we did not find even one of the SMBuses, so we give up */ ++ kfree(smbuses); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++static void __devexit nforce2_remove(struct pci_dev *dev) ++{ ++ struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev); ++ ++ if (smbuses[0].base) { ++ i2c_del_adapter(&smbuses[0].adapter); ++ release_region(smbuses[0].base, smbuses[0].size); ++ } ++ if (smbuses[1].base) { ++ i2c_del_adapter(&smbuses[1].adapter); ++ release_region(smbuses[1].base, smbuses[1].size); ++ } ++ kfree(smbuses); ++} ++ ++static struct pci_driver nforce2_driver = { ++ .name = "nForce2 SMBus", ++ .id_table = nforce2_ids, ++ .probe = nforce2_probe, ++ .remove = __devexit_p(nforce2_remove), ++}; ++ ++int __init nforce2_init(void) ++{ ++ printk(KERN_INFO "i2c-nforce2.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&nforce2_driver); ++} ++ ++void __exit nforce2_exit(void) ++{ ++ pci_unregister_driver(&nforce2_driver); ++} ++ ++module_init(nforce2_init); ++module_exit(nforce2_exit); ++ +--- linux-old/drivers/i2c/i2c-piix4.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-piix4.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,549 @@ ++/* ++ piix4.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998 - 2002 Frodo Looijaard and ++ Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports: ++ Intel PIIX4, 440MX ++ Serverworks OSB4, CSB5, CSB6 ++ SMSC Victory66 ++ ++ Note: we assume there can only be one device, with one SMBus interface. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++ ++struct sd { ++ const unsigned short mfr; ++ const unsigned short dev; ++ const unsigned char fn; ++ const char *name; ++}; ++ ++/* PIIX4 SMBus address offsets */ ++#define SMBHSTSTS (0 + piix4_smba) ++#define SMBHSLVSTS (1 + piix4_smba) ++#define SMBHSTCNT (2 + piix4_smba) ++#define SMBHSTCMD (3 + piix4_smba) ++#define SMBHSTADD (4 + piix4_smba) ++#define SMBHSTDAT0 (5 + piix4_smba) ++#define SMBHSTDAT1 (6 + piix4_smba) ++#define SMBBLKDAT (7 + piix4_smba) ++#define SMBSLVCNT (8 + piix4_smba) ++#define SMBSHDWCMD (9 + piix4_smba) ++#define SMBSLVEVT (0xA + piix4_smba) ++#define SMBSLVDAT (0xC + piix4_smba) ++ ++/* count for request_region */ ++#define SMBIOSIZE 8 ++ ++/* PCI Address Constants */ ++#define SMBBA 0x090 ++#define SMBHSTCFG 0x0D2 ++#define SMBSLVC 0x0D3 ++#define SMBSHDW1 0x0D4 ++#define SMBSHDW2 0x0D5 ++#define SMBREV 0x0D6 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++#define ENABLE_INT9 0 ++ ++/* PIIX4 constants */ ++#define PIIX4_QUICK 0x00 ++#define PIIX4_BYTE 0x04 ++#define PIIX4_BYTE_DATA 0x08 ++#define PIIX4_WORD_DATA 0x0C ++#define PIIX4_BLOCK_DATA 0x14 ++ ++/* insmod parameters */ ++ ++/* If force is set to anything different from 0, we forcibly enable the ++ PIIX4. DANGEROUS! */ ++static int force = 0; ++MODULE_PARM(force, "i"); ++MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the PIIX4 at the given address. VERY DANGEROUS! */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Forcibly enable the PIIX4 at the given address. " ++ "EXTREMELY DANGEROUS!"); ++ ++static int fix_hstcfg = 0; ++MODULE_PARM(fix_hstcfg, "i"); ++MODULE_PARM_DESC(fix_hstcfg, ++ "Fix config register. Needed on some boards (Force CPCI735)."); ++ ++static int piix4_transaction(void); ++ ++static unsigned short piix4_smba = 0; ++ ++#ifdef CONFIG_X86 ++/* ++ * Get DMI information. ++ */ ++ ++static int __devinit ibm_dmi_probe(void) ++{ ++ extern int is_unsafe_smbus; ++ return is_unsafe_smbus; ++} ++#endif ++ ++/* Detect whether a PIIX4 can be found, and initialize it, where necessary. ++ Note the differences between kernels with the old PCI BIOS interface and ++ newer kernels with the real PCI interface. In compat.h some things are ++ defined to make the transition easier. */ ++static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, ++ const struct pci_device_id *id) ++{ ++ unsigned char temp; ++ ++ /* match up the function */ ++ if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data) ++ return -ENODEV; ++ ++ printk(KERN_INFO "Found %s device\n", PIIX4_dev->name); ++ ++#ifdef CONFIG_X86 ++ if(ibm_dmi_probe() && PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { ++ printk(KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module " ++ "may corrupt your serial eeprom! Refusing to load " ++ "module!\n"); ++ return -EPERM; ++ } ++#endif ++ ++ /* Determine the address of the SMBus areas */ ++ if (force_addr) { ++ piix4_smba = force_addr & 0xfff0; ++ force = 0; ++ } else { ++ pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); ++ piix4_smba &= 0xfff0; ++ if(piix4_smba == 0) { ++ printk(KERN_ERR "i2c-piix4.o: SMB base address " ++ "uninitialized - upgrade BIOS or use " ++ "force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ } ++ ++ if (!request_region(piix4_smba, SMBIOSIZE, "piix4-smbus")) { ++ printk(KERN_ERR "i2c-piix4.o: SMB region 0x%x already in " ++ "use!\n", piix4_smba); ++ return -ENODEV; ++ } ++ ++ pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); ++ ++ /* Some BIOS will set up the chipset incorrectly and leave a register ++ in an undefined state (causing I2C to act very strangely). */ ++ if (temp & 0x02) { ++ if (fix_hstcfg) { ++ printk(KERN_INFO "i2c-piix4.o: Working around buggy " ++ "BIOS (I2C)\n"); ++ temp &= 0xfd; ++ pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp); ++ } else { ++ printk(KERN_INFO "i2c-piix4.o: Unusual config register " ++ "value\n"); ++ printk(KERN_INFO "i2c-piix4.o: Try using fix_hstcfg=1 " ++ "if you experience problems\n"); ++ } ++ } ++ ++ /* If force_addr is set, we program the new address here. Just to make ++ sure, we disable the PIIX4 first. */ ++ if (force_addr) { ++ pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); ++ pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); ++ pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); ++ printk(KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to " ++ "new address %04x!\n", piix4_smba); ++ } else if ((temp & 1) == 0) { ++ if (force) { ++ /* This should never need to be done, but has been ++ * noted that many Dell machines have the SMBus ++ * interface on the PIIX4 disabled!? NOTE: This assumes ++ * I/O space and other allocations WERE done by the ++ * Bios! Don't complain if your hardware does weird ++ * things after enabling this. :') Check for Bios ++ * updates before resorting to this. ++ */ ++ pci_write_config_byte(PIIX4_dev, SMBHSTCFG, ++ temp | 1); ++ printk(KERN_NOTICE "i2c-piix4.o: WARNING: SMBus " ++ "interface has been FORCEFULLY ENABLED!\n"); ++ } else { ++ printk(KERN_ERR "i2c-piix4.o: Host SMBus controller " ++ "not enabled!\n"); ++ release_region(piix4_smba, SMBIOSIZE); ++ piix4_smba = 0; ++ return -ENODEV; ++ } ++ } ++ ++#ifdef DEBUG ++ if ((temp & 0x0E) == 8) ++ printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for " ++ "SMBus.\n"); ++ else if ((temp & 0x0E) == 0) ++ printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# " ++ "for SMBus.\n"); ++ else ++ printk(KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration " ++ "(or code out of date)!\n"); ++ ++ pci_read_config_byte(PIIX4_dev, SMBREV, &temp); ++ printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp); ++ printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); ++#endif /* DEBUG */ ++ ++ return 0; ++} ++ ++ ++/* Another internally used function */ ++int piix4_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++#ifdef DEBUG ++ printk ++ (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " ++ "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), ++ inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); ++#endif ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n", ++ temp); ++#endif ++ outb_p(temp, SMBHSTSTS); ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++#ifdef DEBUG ++ printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp); ++#endif ++ return -1; ++ } else { ++#ifdef DEBUG ++ printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n"); ++#endif ++ } ++ } ++ ++ /* start the transaction by setting bit 6 */ ++ outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); ++ ++ /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ ++ do { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); ++ ++#ifdef DEBUG ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n"); ++ result = -1; ++ } ++#endif ++ ++ if (temp & 0x10) { ++ result = -1; ++#ifdef DEBUG ++ printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n"); ++#endif ++ } ++ ++ if (temp & 0x08) { ++ result = -1; ++ printk ++ (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" ++ "reset. (sorry!)\n"); ++ /* Clock stops and slave is stuck in mid-transmission */ ++ } ++ ++ if (temp & 0x04) { ++ result = -1; ++#ifdef DEBUG ++ printk(KERN_ERR "i2c-piix4.o: Error: no response!\n"); ++#endif ++ } ++ ++ if (inb_p(SMBHSTSTS) != 0x00) ++ outb_p(inb(SMBHSTSTS), SMBHSTSTS); ++ ++#ifdef DEBUG ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++ printk ++ (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n", ++ temp); ++ } ++ printk ++ (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " ++ "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), ++ inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); ++#endif ++ return result; ++} ++ ++/* Return -1 on error. */ ++s32 piix4_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data * data) ++{ ++ int i, len; ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = PIIX4_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMBHSTCMD); ++ size = PIIX4_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, SMBHSTDAT0); ++ size = PIIX4_BYTE_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word & 0xff, SMBHSTDAT0); ++ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); ++ } ++ size = PIIX4_WORD_DATA; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) ++ len = 0; ++ if (len > 32) ++ len = 32; ++ outb_p(len, SMBHSTDAT0); ++ i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= len; i++) ++ outb_p(data->block[i], SMBBLKDAT); ++ } ++ size = PIIX4_BLOCK_DATA; ++ break; ++ default: ++ printk ++ (KERN_WARNING "i2c-piix4.o: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); ++ ++ if (piix4_transaction()) /* Error in transaction */ ++ return -1; ++ ++ if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) ++ return 0; ++ ++ ++ switch (size) { ++ case PIIX4_BYTE: /* Where is the result put? I assume here it is in ++ SMBHSTDAT0 but it might just as well be in the ++ SMBHSTCMD. No clue in the docs */ ++ ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case PIIX4_BYTE_DATA: ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case PIIX4_WORD_DATA: ++ data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); ++ break; ++ case PIIX4_BLOCK_DATA: ++ data->block[0] = inb_p(SMBHSTDAT0); ++ i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= data->block[0]; i++) ++ data->block[i] = inb_p(SMBBLKDAT); ++ break; ++ } ++ return 0; ++} ++ ++ ++u32 piix4_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = piix4_access, ++ .functionality = piix4_func, ++}; ++ ++static struct i2c_adapter piix4_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, ++ .algo = &smbus_algorithm, ++}; ++ ++#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB6 ++#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 ++#endif ++ ++static struct pci_device_id piix4_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82371AB_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 3 ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_OSB4, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 0, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 0, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB6, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 0, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = PCI_DEVICE_ID_INTEL_82443MX_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 3, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_EFAR, ++ .device = PCI_DEVICE_ID_EFAR_SLC90E66_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = 0, ++ }, ++ { 0, } ++}; ++ ++static int __devinit piix4_probe(struct pci_dev *dev, ++ const struct pci_device_id *id) ++{ ++ int retval; ++ ++ retval = piix4_setup(dev, id); ++ if (retval) ++ return retval; ++ ++ sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", ++ piix4_smba); ++ ++ if ((retval = i2c_add_adapter(&piix4_adapter))) { ++ printk(KERN_ERR "i2c-piix4.o: Couldn't register adapter!\n"); ++ release_region(piix4_smba, SMBIOSIZE); ++ piix4_smba = 0; ++ } ++ ++ return retval; ++} ++ ++static void __devexit piix4_remove(struct pci_dev *dev) ++{ ++ if (piix4_smba) { ++ i2c_del_adapter(&piix4_adapter); ++ release_region(piix4_smba, SMBIOSIZE); ++ piix4_smba = 0; ++ } ++} ++ ++static struct pci_driver piix4_driver = { ++ .name = "piix4 smbus", ++ .id_table = piix4_ids, ++ .probe = piix4_probe, ++ .remove = __devexit_p(piix4_remove), ++}; ++ ++static int __init i2c_piix4_init(void) ++{ ++ printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&piix4_driver); ++} ++ ++static void __exit i2c_piix4_exit(void) ++{ ++ pci_unregister_driver(&piix4_driver); ++} ++ ++MODULE_AUTHOR("Frodo Looijaard and " ++ "Philip Edelbrock "); ++MODULE_DESCRIPTION("PIIX4 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_piix4_init); ++module_exit(i2c_piix4_exit); +--- linux-old/drivers/i2c/i2c-savage4.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-savage4.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,229 @@ ++/* ++ i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (C) 1998-2003 The LM Sensors Team ++ Alexander Wold ++ Mark D. Studebaker ++ ++ Based on i2c-voodoo3.c. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* This interfaces to the I2C bus of the Savage4 to gain access to ++ the BT869 and possibly other I2C devices. The DDC bus is not ++ yet supported because its register is not memory-mapped. ++ However we leave the DDC code here, commented out, to make ++ it easier to add later. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for HZ */ ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* 3DFX defines */ ++/* #define PCI_VENDOR_ID_S3 0x5333 */ ++#define PCI_CHIP_SAVAGE3D 0x8A20 ++#define PCI_CHIP_SAVAGE3D_MV 0x8A21 ++#define PCI_CHIP_SAVAGE4 0x8A22 ++#define PCI_CHIP_SAVAGE2000 0x9102 ++#define PCI_CHIP_PROSAVAGE_PM 0x8A25 ++#define PCI_CHIP_PROSAVAGE_KM 0x8A26 ++#define PCI_CHIP_SAVAGE_MX_MV 0x8c10 ++#define PCI_CHIP_SAVAGE_MX 0x8c11 ++#define PCI_CHIP_SAVAGE_IX_MV 0x8c12 ++#define PCI_CHIP_SAVAGE_IX 0x8c13 ++ ++#define REG 0xff20 /* Serial Port 1 Register */ ++ ++/* bit locations in the register */ ++//#define DDC_ENAB 0x00040000 ++//#define DDC_SCL_OUT 0x00080000 ++//#define DDC_SDA_OUT 0x00100000 ++//#define DDC_SCL_IN 0x00200000 ++//#define DDC_SDA_IN 0x00400000 ++#define I2C_ENAB 0x00000020 ++#define I2C_SCL_OUT 0x00000001 ++#define I2C_SDA_OUT 0x00000002 ++#define I2C_SCL_IN 0x00000008 ++#define I2C_SDA_IN 0x00000010 ++ ++/* initialization states */ ++#define INIT2 0x20 ++/* #define INIT3 0x4 */ ++ ++/* delays */ ++#define CYCLE_DELAY 10 ++#define TIMEOUT (HZ / 2) ++ ++ ++static void config_s4(struct pci_dev *dev); ++ ++static unsigned long ioaddr; ++ ++/* The sav GPIO registers don't have individual masks for each bit ++ so we always have to read before writing. */ ++ ++static void bit_savi2c_setscl(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= I2C_SCL_OUT; ++ else ++ r &= ~I2C_SCL_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++static void bit_savi2c_setsda(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= I2C_SDA_OUT; ++ else ++ r &= ~I2C_SDA_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++/* The GPIO pins are open drain, so the pins always remain outputs. ++ We rely on the i2c-algo-bit routines to set the pins high before ++ reading the input from other chips. */ ++ ++static int bit_savi2c_getscl(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); ++} ++ ++static int bit_savi2c_getsda(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); ++} ++ ++/* Configures the chip */ ++ ++void config_s4(struct pci_dev *dev) ++{ ++ unsigned int cadr; ++ ++ /* map memory */ ++ cadr = dev->resource[0].start; ++ cadr &= PCI_BASE_ADDRESS_MEM_MASK; ++ ioaddr = (unsigned long)ioremap_nocache(cadr, 0x0080000); ++ if(ioaddr) { ++// writel(0x8160, ioaddr + REG2); ++ writel(0x00000020, ioaddr + REG); ++ printk("i2c-savage4: Using Savage4 at 0x%lx\n", ioaddr); ++ } ++} ++ ++ ++static struct i2c_algo_bit_data sav_i2c_bit_data = { ++ .setsda = bit_savi2c_setsda, ++ .setscl = bit_savi2c_setscl, ++ .getsda = bit_savi2c_getsda, ++ .getscl = bit_savi2c_getscl, ++ .udelay = CYCLE_DELAY, ++ .mdelay = CYCLE_DELAY, ++ .timeout = TIMEOUT ++}; ++ ++static struct i2c_adapter savage4_i2c_adapter = { ++ .owner = THIS_MODULE, ++ .name = "I2C Savage4 adapter", ++ .id = I2C_HW_B_SAVG, ++ .algo_data = &sav_i2c_bit_data, ++}; ++ ++static struct pci_device_id savage4_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_S3, ++ .device = PCI_CHIP_SAVAGE4, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_S3, ++ .device = PCI_CHIP_SAVAGE2000, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ config_s4(dev); ++ return i2c_bit_add_bus(&savage4_i2c_adapter); ++} ++ ++static void __devexit savage4_remove(struct pci_dev *dev) ++{ ++ i2c_bit_del_bus(&savage4_i2c_adapter); ++} ++ ++ ++/* Don't register driver to avoid driver conflicts */ ++/* ++static struct pci_driver savage4_driver = { ++ .name = "savage4 smbus", ++ .id_table = savage4_ids, ++ .probe = savage4_probe, ++ .remove = __devexit_p(savage4_remove), ++}; ++*/ ++ ++static int __init i2c_savage4_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk("i2c-savage4.o version %s (%s)\n", LM_VERSION, LM_DATE); ++/* ++ return pci_module_init(&savage4_driver); ++*/ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(savage4_ids, dev); ++ if(id) ++ if(savage4_probe(dev, id) >= 0) ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++static void __exit i2c_savage4_exit(void) ++{ ++/* ++ pci_unregister_driver(&savage4_driver); ++*/ ++ savage4_remove(NULL); ++ iounmap((void *)ioaddr); ++} ++ ++MODULE_AUTHOR("Alexander Wold " ++ "and Mark D. Studebaker "); ++MODULE_DESCRIPTION("Savage4 I2C/SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_savage4_init); ++module_exit(i2c_savage4_exit); +--- linux-old/drivers/i2c/i2c-sis5595.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-sis5595.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,481 @@ ++/* ++ sis5595.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard and ++ Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Note: we assume there can only be one SIS5595 with one SMBus interface */ ++ ++/* ++ Note: all have mfr. ID 0x1039. ++ SUPPORTED PCI ID ++ 5595 0008 ++ ++ Note: these chips contain a 0008 device which is incompatible with the ++ 5595. We recognize these by the presence of the listed ++ "blacklist" PCI ID and refuse to load. ++ ++ NOT SUPPORTED PCI ID BLACKLIST PCI ID ++ 540 0008 0540 ++ 550 0008 0550 ++ 5513 0008 5511 ++ 5581 0008 5597 ++ 5582 0008 5597 ++ 5597 0008 5597 ++ 5598 0008 5597/5598 ++ 630 0008 0630 ++ 645 0008 0645 ++ 646 0008 0646 ++ 648 0008 0648 ++ 650 0008 0650 ++ 651 0008 0651 ++ 730 0008 0730 ++ 735 0008 0735 ++ 745 0008 0745 ++ 746 0008 0746 ++*/ ++ ++/* TO DO: ++ * Add Block Transfers (ugly, but supported by the adapter) ++ * Add adapter resets ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++static int blacklist[] = { ++ PCI_DEVICE_ID_SI_540, ++ PCI_DEVICE_ID_SI_550, ++ PCI_DEVICE_ID_SI_630, ++ PCI_DEVICE_ID_SI_730, ++ PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but ++ that ID shows up in other chips so we ++ use the 5511 ID for recognition */ ++ PCI_DEVICE_ID_SI_5597, ++ PCI_DEVICE_ID_SI_5598, ++ 0x645, ++ 0x646, ++ 0x648, ++ 0x650, ++ 0x651, ++ 0x735, ++ 0x745, ++ 0x746, ++ 0 }; ++ ++/* Length of ISA address segment */ ++#define SIS5595_EXTENT 8 ++/* SIS5595 SMBus registers */ ++#define SMB_STS_LO 0x00 ++#define SMB_STS_HI 0x01 ++#define SMB_CTL_LO 0x02 ++#define SMB_CTL_HI 0x03 ++#define SMB_ADDR 0x04 ++#define SMB_CMD 0x05 ++#define SMB_PCNT 0x06 ++#define SMB_CNT 0x07 ++#define SMB_BYTE 0x08 ++#define SMB_DEV 0x10 ++#define SMB_DB0 0x11 ++#define SMB_DB1 0x12 ++#define SMB_HAA 0x13 ++ ++/* PCI Address Constants */ ++#define SMB_INDEX 0x38 ++#define SMB_DAT 0x39 ++#define SIS5595_ENABLE_REG 0x40 ++#define ACPI_BASE 0x90 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++ ++/* SIS5595 constants */ ++#define SIS5595_QUICK 0x00 ++#define SIS5595_BYTE 0x02 ++#define SIS5595_BYTE_DATA 0x04 ++#define SIS5595_WORD_DATA 0x06 ++#define SIS5595_PROC_CALL 0x08 ++#define SIS5595_BLOCK_DATA 0x0A ++ ++/* insmod parameters */ ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the device at the given address. */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the i2c controller"); ++ ++static int sis5595_transaction(void); ++ ++static unsigned short sis5595_base = 0; ++ ++static u8 sis5595_read(u8 reg) ++{ ++ outb(reg, sis5595_base + SMB_INDEX); ++ return inb(sis5595_base + SMB_DAT); ++} ++ ++static void sis5595_write(u8 reg, u8 data) ++{ ++ outb(reg, sis5595_base + SMB_INDEX); ++ outb(data, sis5595_base + SMB_DAT); ++} ++ ++ ++/* Detect whether a SIS5595 can be found, and initialize it, where necessary. ++ Note the differences between kernels with the old PCI BIOS interface and ++ newer kernels with the real PCI interface. In compat.h some things are ++ defined to make the transition easier. */ ++int sis5595_setup(struct pci_dev *SIS5595_dev) ++{ ++ u16 a; ++ u8 val; ++ int *i; ++ ++ /* Look for imposters */ ++ for(i = blacklist; *i != 0; i++) { ++ if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { ++ printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); ++ return -ENODEV; ++ } ++ } ++ ++/* Determine the address of the SMBus areas */ ++ pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); ++ if(sis5595_base == 0 && force_addr == 0) { ++ printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ ++ if(force_addr) ++ sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); ++#ifdef DEBUG ++ printk("ACPI Base address: %04x\n", sis5595_base); ++#endif ++ /* NB: We grab just the two SMBus registers here, but this may still ++ * interfere with ACPI :-( */ ++ if (check_region(sis5595_base + SMB_INDEX, 2)) { ++ printk ++ ("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n", ++ sis5595_base + SMB_INDEX, ++ sis5595_base + SMB_INDEX + 1); ++ return -ENODEV; ++ } ++ ++ if(force_addr) { ++ printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)) ++ return -ENODEV; ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)) ++ return -ENODEV; ++ if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { ++ /* doesn't work for some chips! */ ++ printk("i2c-sis5595.o: force address failed - not supported?\n"); ++ return -ENODEV; ++ } ++ } ++ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) ++ return -ENODEV; ++ if((val & 0x80) == 0) { ++ printk("sis5595.o: enabling ACPI\n"); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, ++ val | 0x80)) ++ return -ENODEV; ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) ++ return -ENODEV; ++ if((val & 0x80) == 0) { /* doesn't work for some chips? */ ++ printk("sis5595.o: ACPI enable failed - not supported?\n"); ++ return -ENODEV; ++ } ++ } ++ ++ /* Everything is happy, let's grab the memory and set things up. */ ++ request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus"); ++ return(0); ++} ++ ++ ++/* Another internally used function */ ++int sis5595_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ if ( ++ (temp = ++ sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != ++ 0x00) { ++#ifdef DEBUG ++ printk("i2c-sis5595.o: SMBus busy (%04x). Resetting... \n", ++ temp); ++#endif ++ sis5595_write(SMB_STS_LO, temp & 0xff); ++ sis5595_write(SMB_STS_HI, temp >> 8); ++ if ( ++ (temp = ++ sis5595_read(SMB_STS_LO) + ++ (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { ++#ifdef DEBUG ++ printk("i2c-sis5595.o: Failed! (%02x)\n", temp); ++#endif ++ return -1; ++ } else { ++#ifdef DEBUG ++ printk("i2c-sis5595.o: Successfull!\n"); ++#endif ++ } ++ } ++ ++ /* start the transaction by setting bit 4 */ ++ sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); ++ ++ /* We will always wait for a fraction of a second! */ ++ do { ++ i2c_delay(1); ++ temp = sis5595_read(SMB_STS_LO); ++ } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++#ifdef DEBUG ++ printk("i2c-sis5595.o: SMBus Timeout!\n"); ++#endif ++ result = -1; ++ } ++ ++ if (temp & 0x10) { ++ result = -1; ++#ifdef DEBUG ++ printk("i2c-sis5595.o: Error: Failed bus transaction\n"); ++#endif ++ } ++ ++ if (temp & 0x20) { ++ result = -1; ++ printk ++ ("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n" ++ "reset (or not...)\n"); ++ /* Clock stops and slave is stuck in mid-transmission */ ++ } ++ ++ if ( ++ (temp = ++ sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != ++ 0x00) { ++ sis5595_write(SMB_STS_LO, temp & 0xff); ++ sis5595_write(SMB_STS_HI, temp >> 8); ++ } ++ ++ if ( ++ (temp = ++ sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != ++ 0x00) { ++ ++#ifdef DEBUG ++ printk ++ ("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n", ++ temp); ++#endif ++ } ++ return result; ++} ++ ++/* Return -1 on error. */ ++s32 sis5595_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data * data) ++{ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ sis5595_write(SMB_ADDR, ++ ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ size = SIS5595_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ sis5595_write(SMB_ADDR, ++ ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis5595_write(SMB_CMD, command); ++ size = SIS5595_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ sis5595_write(SMB_ADDR, ++ ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis5595_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis5595_write(SMB_BYTE, data->byte); ++ size = SIS5595_BYTE_DATA; ++ break; ++ case I2C_SMBUS_PROC_CALL: ++ case I2C_SMBUS_WORD_DATA: ++ sis5595_write(SMB_ADDR, ++ ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis5595_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) { ++ sis5595_write(SMB_BYTE, data->word & 0xff); ++ sis5595_write(SMB_BYTE + 1, ++ (data->word & 0xff00) >> 8); ++ } ++ size = ++ (size == ++ I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : ++ SIS5595_WORD_DATA; ++ break; ++/* ++ case I2C_SMBUS_BLOCK_DATA: ++ printk("sis5595.o: Block data not yet implemented!\n"); ++ return -1; ++ break; ++*/ ++ default: ++ printk ++ (KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ sis5595_write(SMB_CTL_LO, ((size & 0x0E))); ++ ++ if (sis5595_transaction()) /* Error in transaction */ ++ return -1; ++ ++ if ((size != SIS5595_PROC_CALL) && ++ ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) ++ return 0; ++ ++ ++ switch (size) { ++ case SIS5595_BYTE: /* Where is the result put? I assume here it is in ++ SMB_DATA but it might just as well be in the ++ SMB_CMD. No clue in the docs */ ++ case SIS5595_BYTE_DATA: ++ data->byte = sis5595_read(SMB_BYTE); ++ break; ++ case SIS5595_WORD_DATA: ++ case SIS5595_PROC_CALL: ++ data->word = ++ sis5595_read(SMB_BYTE) + ++ (sis5595_read(SMB_BYTE + 1) << 8); ++ break; ++ } ++ return 0; ++} ++ ++ ++u32 sis5595_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_PROC_CALL; ++} ++ ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = sis5595_access, ++ .functionality = sis5595_func, ++}; ++ ++static struct i2c_adapter sis5595_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595, ++ .algo = &smbus_algorithm, ++}; ++ ++ ++static struct pci_device_id sis5595_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_SI, ++ .device = PCI_DEVICE_ID_SI_503, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ ++ if (sis5595_setup(dev)) { ++ printk ++ ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n"); ++ ++ return -ENODEV; ++ } ++ ++ sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", ++ sis5595_base + SMB_INDEX); ++ i2c_add_adapter(&sis5595_adapter); ++ ++ return 0; ++} ++ ++static void __devexit sis5595_remove(struct pci_dev *dev) ++{ ++ i2c_del_adapter(&sis5595_adapter); ++ release_region(sis5595_base + SMB_INDEX, 2); ++} ++ ++ ++static struct pci_driver sis5595_driver = { ++ .name = "sis5595 smbus", ++ .id_table = sis5595_ids, ++ .probe = sis5595_probe, ++ .remove = __devexit_p(sis5595_remove), ++}; ++ ++static int __init i2c_sis5595_init(void) ++{ ++ printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&sis5595_driver); ++} ++ ++ ++static void __exit i2c_sis5595_exit(void) ++{ ++ pci_unregister_driver(&sis5595_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard "); ++MODULE_DESCRIPTION("SIS5595 SMBus driver"); ++ ++module_init(i2c_sis5595_init); ++module_exit(i2c_sis5595_exit); +--- linux-old/drivers/i2c/i2c-sis630.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-sis630.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,527 @@ ++/* ++ i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ ++ Copyright (c) 2002,2003 Alexander Malysh ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Changes: ++ 24.08.2002 ++ Fixed the typo in sis630_access (Thanks to Mark M. Hoffman) ++ Changed sis630_transaction.(Thanks to Mark M. Hoffman) ++ 18.09.2002 ++ Added SIS730 as supported. ++ 21.09.2002 ++ Added high_clock module option.If this option is set ++ used Host Master Clock 56KHz (default 14KHz).For now we save old Host ++ Master Clock and after transaction completed restore (otherwise ++ it's confuse BIOS and hung Machine). ++ 24.09.2002 ++ Fixed typo in sis630_access ++ Fixed logical error by restoring of Host Master Clock ++ 31.07.2003 ++ Added block data read/write support. ++*/ ++ ++/* ++ Status: beta ++ ++ Supports: ++ SIS 630 ++ SIS 730 ++ ++ Note: we assume there can only be one device, with one SMBus interface. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++ ++#ifdef DEBUG ++#define DBG(x...) printk(KERN_DEBUG "i2c-sis630.o: " x) ++#else ++#define DBG(x...) ++#endif ++ ++/* SIS630 SMBus registers */ ++#define SMB_STS 0x80 /* status */ ++#define SMB_EN 0x81 /* status enable */ ++#define SMB_CNT 0x82 ++#define SMBHOST_CNT 0x83 ++#define SMB_ADDR 0x84 ++#define SMB_CMD 0x85 ++#define SMB_PCOUNT 0x86 /* processed count */ ++#define SMB_COUNT 0x87 ++#define SMB_BYTE 0x88 /* ~0x8F data byte field */ ++#define SMBDEV_ADDR 0x90 ++#define SMB_DB0 0x91 ++#define SMB_DB1 0x92 ++#define SMB_SAA 0x93 ++ ++/* register count for request_region */ ++#define SIS630_SMB_IOREGION 20 ++ ++/* PCI address constants */ ++/* acpi base address register */ ++#define SIS630_ACPI_BASE_REG 0x74 ++/* bios control register */ ++#define SIS630_BIOS_CTL_REG 0x40 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++ ++/* SIS630 constants */ ++#define SIS630_QUICK 0x00 ++#define SIS630_BYTE 0x01 ++#define SIS630_BYTE_DATA 0x02 ++#define SIS630_WORD_DATA 0x03 ++#define SIS630_PCALL 0x04 ++#define SIS630_BLOCK_DATA 0x05 ++ ++/* insmod parameters */ ++static int high_clock = 0; ++static int force = 0; ++MODULE_PARM(high_clock, "i"); ++MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); ++MODULE_PARM(force, "i"); ++MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); ++ ++/* acpi base address */ ++static unsigned short acpi_base = 0; ++ ++/* supported chips */ ++static int supported[] = { ++ PCI_DEVICE_ID_SI_630, ++ PCI_DEVICE_ID_SI_730, ++ 0 /* terminates the list */ ++}; ++ ++static inline u8 sis630_read(u8 reg) { ++ return inb(acpi_base + reg); ++} ++ ++static inline void sis630_write(u8 reg, u8 data) { ++ outb(data, acpi_base + reg); ++} ++ ++static int sis630_transaction_start(int size, u8 *oldclock) { ++ int temp; ++ ++ /* ++ Make sure the SMBus host is ready to start transmitting. ++ */ ++ if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { ++ DBG("SMBus busy (%02x).Resetting...\n",temp); ++ /* kill smbus transaction */ ++ sis630_write(SMBHOST_CNT, 0x20); ++ ++ if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { ++ DBG("Failed! (%02x)\n", temp); ++ return -1; ++ } else { ++ DBG("Successfull!\n"); ++ } ++ } ++ ++ /* save old clock, so we can prevent machine for hung */ ++ *oldclock = sis630_read(SMB_CNT); ++ ++ DBG("saved clock 0x%02x\n", *oldclock); ++ ++ /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ ++ if (high_clock > 0) ++ sis630_write(SMB_CNT, 0x20); ++ else ++ sis630_write(SMB_CNT, (*oldclock & ~0x40)); ++ ++ /* clear all sticky bits */ ++ temp = sis630_read(SMB_STS); ++ sis630_write(SMB_STS, temp & 0x1e); ++ ++ /* start the transaction by setting bit 4 and size */ ++ sis630_write(SMBHOST_CNT,0x10 | (size & 0x07)); ++ ++ return 0; ++} ++ ++static int sis630_transaction_wait(int size) { ++ int temp, result = 0, timeout = 0; ++ ++ /* We will always wait for a fraction of a second! */ ++ do { ++ i2c_delay(1); ++ temp = sis630_read(SMB_STS); ++ /* check if block transmitted */ ++ if (size == SIS630_BLOCK_DATA && (temp & 0x10)) ++ break; ++ } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ DBG("SMBus Timeout!\n"); ++ result = -1; ++ } ++ ++ if (temp & 0x02) { ++ result = -1; ++ DBG("Error: Failed bus transaction\n"); ++ } ++ ++ if (temp & 0x04) { ++ result = -1; ++ printk(KERN_ERR "i2c-sis630.o: Bus collision!\n"); ++ /* ++ TBD: Datasheet say: ++ the software should clear this bit and restart SMBUS operation. ++ Should we do it or user start request again? ++ */ ++ } ++ ++ return result; ++} ++ ++static void sis630_transaction_end(u8 oldclock) { ++ int temp = 0; ++ ++ /* clear all status "sticky" bits */ ++ sis630_write(SMB_STS, temp); ++ ++ DBG("SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); ++ ++ /* ++ * restore old Host Master Clock if high_clock is set ++ * and oldclock was not 56KHz ++ */ ++ if (high_clock > 0 && !(oldclock & 0x20)) ++ sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); ++ ++ DBG("SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); ++} ++ ++static int sis630_transaction(int size) { ++ int result = 0; ++ u8 oldclock = 0; ++ ++ if (!(result = sis630_transaction_start(size, &oldclock))) { ++ result = sis630_transaction_wait(size); ++ sis630_transaction_end(oldclock); ++ } ++ ++ return result; ++} ++ ++static int sis630_block_data(union i2c_smbus_data * data, int read_write) { ++ int i, len = 0, rc = 0; ++ u8 oldclock = 0; ++ ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) ++ len = 0; ++ else if (len > 32) ++ len = 32; ++ sis630_write(SMB_COUNT, len); ++ for (i=1; i <= len; i++) { ++ DBG("set data 0x%02x\n", data->block[i]); ++ /* set data */ ++ sis630_write(SMB_BYTE+(i-1)%8, data->block[i]); ++ if (i==8 || (len<8 && i==len)) { ++ DBG("start trans len=%d i=%d\n",len ,i); ++ /* first transaction */ ++ if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) ++ return -1; ++ } ++ else if ((i-1)%8 == 7 || i==len) { ++ DBG("trans_wait len=%d i=%d\n",len,i); ++ if (i>8) { ++ DBG("clear smbary_sts len=%d i=%d\n",len,i); ++ /* ++ If this is not first transaction, ++ we must clear sticky bit. ++ clear SMBARY_STS ++ */ ++ sis630_write(SMB_STS,0x10); ++ } ++ if (sis630_transaction_wait(SIS630_BLOCK_DATA)) { ++ DBG("trans_wait failed\n"); ++ rc = -1; ++ break; ++ } ++ ++ } ++ } ++ } ++ else { /* read request */ ++ data->block[0] = len = 0; ++ if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) { ++ return -1; ++ } ++ do { ++ if (sis630_transaction_wait(SIS630_BLOCK_DATA)) { ++ DBG("trans_wait failed\n"); ++ rc = -1; ++ break; ++ } ++ /* if this first transaction then read byte count */ ++ if (len == 0) ++ data->block[0] = sis630_read(SMB_COUNT); ++ ++ /* just to be sure */ ++ if (data->block[0] > 32) ++ data->block[0] = 32; ++ ++ DBG("block data read len=0x%x\n", data->block[0]); ++ ++ for (i=0; i < 8 && len < data->block[0]; i++,len++) { ++ DBG("read i=%d len=%d\n", i, len); ++ data->block[len+1] = sis630_read(SMB_BYTE+i); ++ } ++ ++ DBG("clear smbary_sts len=%d i=%d\n",len,i); ++ ++ /* clear SMBARY_STS */ ++ sis630_write(SMB_STS,0x10); ++ } while(len < data->block[0]); ++ } ++ ++ sis630_transaction_end(oldclock); ++ ++ return rc; ++} ++ ++/* Return -1 on error. */ ++static s32 sis630_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data * data) ++{ ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ size = SIS630_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis630_write(SMB_CMD, command); ++ size = SIS630_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis630_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis630_write(SMB_BYTE, data->byte); ++ size = SIS630_BYTE_DATA; ++ break; ++ case I2C_SMBUS_PROC_CALL: ++ case I2C_SMBUS_WORD_DATA: ++ sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis630_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) { ++ sis630_write(SMB_BYTE, data->word & 0xff); ++ sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); ++ } ++ size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis630_write(SMB_CMD, command); ++ size = SIS630_BLOCK_DATA; ++ return sis630_block_data(data, read_write); ++ default: ++ printk("Unsupported I2C size\n"); ++ return -1; ++ break; ++ } ++ ++ ++ if (sis630_transaction(size)) ++ return -1; ++ ++ if ((size != SIS630_PCALL) && ++ ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) { ++ return 0; ++ } ++ ++ switch(size) { ++ case SIS630_BYTE: ++ case SIS630_BYTE_DATA: ++ data->byte = sis630_read(SMB_BYTE); ++ break; ++ case SIS630_PCALL: ++ case SIS630_WORD_DATA: ++ data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8); ++ break; ++ default: ++ return -1; ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++static u32 sis630_func(struct i2c_adapter *adapter) { ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static int __devinit sis630_setup(struct pci_dev *sis630_dev) { ++ unsigned char b; ++ struct pci_dev *dummy = NULL; ++ int i; ++ ++ /* check for supported SiS devices */ ++ for (i=0; supported[i] > 0; i++) { ++ if ((dummy = pci_find_device(PCI_VENDOR_ID_SI, supported[i], dummy))) ++ break; /* found */ ++ } ++ ++ if (!dummy && force > 0) { ++ printk(KERN_ERR "i2c-sis630.o: WARNING: Can't detect SIS630 compatible device, but " ++ "loading because of force option enabled\n"); ++ } ++ else if (!dummy) { ++ return -ENODEV; ++ } ++ ++ /* ++ Enable ACPI first , so we can accsess reg 74-75 ++ in acpi io space and read acpi base addr ++ */ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { ++ printk(KERN_ERR "i2c-sis630.o: Error: Can't read bios ctl reg\n"); ++ return -ENODEV; ++ } ++ ++ /* if ACPI already enabled , do nothing */ ++ if (!(b & 0x80) && ++ PCIBIOS_SUCCESSFUL != ++ pci_write_config_byte(sis630_dev,SIS630_BIOS_CTL_REG,b|0x80)) { ++ printk(KERN_ERR "i2c-sis630.o: Error: Can't enable ACPI\n"); ++ return -ENODEV; ++ } ++ /* Determine the ACPI base address */ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { ++ printk(KERN_ERR "i2c-sis630.o: Error: Can't determine ACPI base address\n"); ++ return -ENODEV; ++ } ++ ++ DBG("ACPI base at 0x%04x\n", acpi_base); ++ ++ /* Everything is happy, let's grab the memory and set things up. */ ++ if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")){ ++ printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x " ++ "already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA); ++ acpi_base = 0; /* reset acpi_base */ ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = sis630_access, ++ .functionality = sis630_func, ++}; ++ ++static struct i2c_adapter sis630_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS630, ++ .algo = &smbus_algorithm, ++}; ++ ++ ++static struct pci_device_id sis630_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_SI, ++ .device = PCI_DEVICE_ID_SI_503, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ if (sis630_setup(dev)) { ++ printk(KERN_ERR "i2c-sis630.o: SIS630 comp. bus not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ ++ sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x", ++ acpi_base + SMB_STS); ++ ++ return i2c_add_adapter(&sis630_adapter); ++} ++ ++static void __devexit sis630_remove(struct pci_dev *dev) ++{ ++ if (acpi_base) { ++ i2c_del_adapter(&sis630_adapter); ++ release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); ++ acpi_base = 0; ++ } ++} ++ ++ ++static struct pci_driver sis630_driver = { ++ .name = "sis630 smbus", ++ .id_table = sis630_ids, ++ .probe = sis630_probe, ++ .remove = __devexit_p(sis630_remove), ++}; ++ ++static int __init i2c_sis630_init(void) ++{ ++ printk("i2c-sis630.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&sis630_driver); ++} ++ ++ ++static void __exit i2c_sis630_exit(void) ++{ ++ pci_unregister_driver(&sis630_driver); ++} ++ ++ ++ ++ ++MODULE_LICENSE("GPL"); ++ ++MODULE_AUTHOR("Alexander Malysh "); ++MODULE_DESCRIPTION("SIS630 SMBus driver"); ++ ++module_init(i2c_sis630_init); ++module_exit(i2c_sis630_exit); +--- linux-old/drivers/i2c/i2c-sis645.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-sis645.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,590 @@ ++/* ++ sis645.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ ++ Copyright (c) 2003 Mark M. Hoffman ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This module must be considered BETA unless and until ++ the chipset manufacturer releases a datasheet. ++ ++ The register definitions are based on the SiS630. ++ The method for *finding* the registers is based on trial and error. ++ ++ A history of changes to this file is available by anonymous CVS: ++ http://www2.lm-sensors.nu/~lm78/download.html ++*/ ++ ++/* 25th March 2004 ++ Support for Sis655 chipsets added by Ken Healy ++*/ ++ ++/* ++ Note: we assume there can only be one SiS645 with one SMBus interface ++*/ ++ ++/* #define DEBUG 1 */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++#define DRV_NAME "i2c-sis645" ++ ++/* SiS645DX north bridge (defined in 2.4.21) */ ++#ifndef PCI_DEVICE_ID_SI_646 ++#define PCI_DEVICE_ID_SI_646 0x0646 ++#endif ++ ++/* SiS648 north bridge (defined in 2.4.21) */ ++#ifndef PCI_DEVICE_ID_SI_648 ++#define PCI_DEVICE_ID_SI_648 0x0648 ++#endif ++ ++/* SiS650 north bridge (defined in 2.4.19) */ ++#ifndef PCI_DEVICE_ID_SI_650 ++#define PCI_DEVICE_ID_SI_650 0x0650 ++#endif ++ ++/* SiS651 north bridge (defined in 2.4.21)*/ ++#ifndef PCI_DEVICE_ID_SI_651 ++#define PCI_DEVICE_ID_SI_651 0x0651 ++#endif ++ ++/* SiS655 north bridge (defined in 2.4.22)*/ ++#ifndef PCI_DEVICE_ID_SI_655 ++#define PCI_DEVICE_ID_SI_655 0x0655 ++#endif ++ ++/* SiS746 north bridge (defined in 2.4.21) */ ++#ifndef PCI_DEVICE_ID_SI_746 ++#define PCI_DEVICE_ID_SI_746 0x0746 ++#endif ++ ++/* SiS85C503/5513 (LPC Bridge) */ ++#ifndef PCI_DEVICE_ID_SI_LPC ++#define PCI_DEVICE_ID_SI_LPC 0x0018 ++#endif ++ ++/* SiS961 south bridge */ ++#ifndef PCI_DEVICE_ID_SI_961 ++#define PCI_DEVICE_ID_SI_961 0x0961 ++#endif ++ ++/* SiS962 south bridge */ ++#ifndef PCI_DEVICE_ID_SI_962 ++#define PCI_DEVICE_ID_SI_962 0x0962 ++#endif ++ ++/* SiS963 south bridge */ ++#ifndef PCI_DEVICE_ID_SI_963 ++#define PCI_DEVICE_ID_SI_963 0x0963 ++#endif ++ ++/* SMBus ID */ ++#ifndef PCI_DEVICE_ID_SI_SMBUS ++#define PCI_DEVICE_ID_SI_SMBUS 0x16 ++#endif ++ ++/* base address register in PCI config space */ ++#define SIS645_BAR 0x04 ++ ++/* SiS645 SMBus registers */ ++#define SMB_STS 0x00 ++#define SMB_EN 0x01 ++#define SMB_CNT 0x02 ++#define SMB_HOST_CNT 0x03 ++#define SMB_ADDR 0x04 ++#define SMB_CMD 0x05 ++#define SMB_PCOUNT 0x06 ++#define SMB_COUNT 0x07 ++#define SMB_BYTE 0x08 ++#define SMB_DEV_ADDR 0x10 ++#define SMB_DB0 0x11 ++#define SMB_DB1 0x12 ++#define SMB_SAA 0x13 ++ ++/* register count for request_region */ ++#define SMB_IOSIZE 0x20 ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++ ++/* SiS645 SMBus constants */ ++#define SIS645_QUICK 0x00 ++#define SIS645_BYTE 0x01 ++#define SIS645_BYTE_DATA 0x02 ++#define SIS645_WORD_DATA 0x03 ++#define SIS645_PROC_CALL 0x04 ++#define SIS645_BLOCK_DATA 0x05 ++ ++static struct i2c_adapter sis645_adapter; ++static u16 sis645_smbus_base = 0; ++ ++static inline u8 sis645_read(u8 reg) ++{ ++ return inb(sis645_smbus_base + reg) ; ++} ++ ++static inline void sis645_write(u8 reg, u8 data) ++{ ++ outb(data, sis645_smbus_base + reg) ; ++} ++ ++#ifdef CONFIG_HOTPLUG ++ ++/* Turns on SMBus device if it is not; return 0 iff successful ++ */ ++static int __devinit sis645_enable_smbus(struct pci_dev *dev) ++{ ++ u8 val = 0; ++ ++ pci_read_config_byte(dev, 0x77, &val); ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Config byte was 0x%02x.\n", val); ++#endif ++ ++ pci_write_config_byte(dev, 0x77, val & ~0x10); ++ ++ pci_read_config_byte(dev, 0x77, &val); ++ ++ if (val & 0x10) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Config byte stuck!\n"); ++#endif ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Builds the basic pci_dev for SiS645 SMBus ++ */ ++static int __devinit sis645_build_dev(struct pci_dev **smbus_dev, ++ struct pci_dev *bridge_dev) ++{ ++ struct pci_dev temp_dev; ++ u16 vid = 0, did = 0; ++ int ret; ++ ++ /* fill in the device structure for search */ ++ memset(&temp_dev, 0, sizeof(temp_dev)); ++ temp_dev.bus = bridge_dev->bus; ++ temp_dev.sysdata = bridge_dev->bus->sysdata; ++ temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL; ++ ++ /* the SMBus device is function 1 on the same unit as the ISA bridge */ ++ temp_dev.devfn = bridge_dev->devfn + 1; ++ ++ /* query to make sure */ ++ ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid); ++ if (ret || PCI_VENDOR_ID_SI != vid) { ++ printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n"); ++ return ret; ++ } ++ temp_dev.vendor = vid; ++ ++ ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did); ++ if (ret || PCI_DEVICE_ID_SI_SMBUS != did) { ++ printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n"); ++ return ret; ++ } ++ temp_dev.device = did; ++ ++ /* ok, we've got it... request some memory and finish it off */ ++ *smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC); ++ if (NULL == *smbus_dev) { ++ printk(KERN_ERR DRV_NAME ": Out of memory!\n"); ++ return -ENOMEM; ++ } ++ ++ **smbus_dev = temp_dev; ++ ++ ret = pci_setup_device(*smbus_dev); ++ if (ret) { ++ printk(KERN_ERR DRV_NAME ": pci_setup_device failed (0x%08x)\n",ret); ++ } ++ return ret; ++} ++ ++/* See if a SMBus can be found, and enable it if possible. ++ */ ++static int __devinit sis645_hotplug_smbus(void) ++{ ++ int ret; ++ struct pci_dev *smbus_dev, *bridge_dev ; ++ ++ if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_961, NULL))) ++ printk(KERN_INFO DRV_NAME ": Found SiS961 south bridge.\n"); ++ ++ else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_962, NULL))) ++ printk(KERN_INFO DRV_NAME ": Found SiS962 [MuTIOL Media IO].\n"); ++ ++ else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_963, NULL))) ++ printk(KERN_INFO DRV_NAME ": Found SiS963 [MuTIOL Media IO].\n"); ++ ++ else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_503, NULL)) || ++ (bridge_dev = pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_LPC, NULL))) { ++ ++ printk(KERN_INFO DRV_NAME ": Found SiS south bridge in compatability mode(?)\n"); ++ ++ /* look for known compatible north bridges */ ++ if ((NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_645, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_646, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_648, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_650, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_651, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_655, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_735, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_745, NULL)) ++ && (NULL == pci_find_device(PCI_VENDOR_ID_SI, ++ PCI_DEVICE_ID_SI_746, NULL))) { ++ printk(KERN_ERR DRV_NAME ": Can't find suitable host bridge!\n"); ++ return -ENODEV; ++ } ++ } else { ++ printk(KERN_ERR DRV_NAME ": Can't find suitable south bridge!\n"); ++ return -ENODEV; ++ } ++ ++ /* if we get this far, we think the smbus device is present */ ++ ++ if ((ret = sis645_enable_smbus(bridge_dev))) ++ return ret; ++ ++ if ((ret = sis645_build_dev(&smbus_dev, bridge_dev))) ++ return ret; ++ ++ if ((ret = pci_enable_device(smbus_dev))) { ++ printk(KERN_ERR DRV_NAME ": Can't pci_enable SMBus device!" ++ " (0x%08x)\n", ret); ++ return ret; ++ } ++ ++ pci_insert_device(smbus_dev, smbus_dev->bus); ++ ++ return 0; ++} ++#endif /* CONFIG_HOTPLUG */ ++ ++/* Execute a SMBus transaction. ++ int size is from SIS645_QUICK to SIS645_BLOCK_DATA ++ */ ++static int sis645_transaction(int size) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": SMBus busy (0x%02x). Resetting...\n", ++ temp); ++#endif ++ ++ /* kill the transaction */ ++ sis645_write(SMB_HOST_CNT, 0x20); ++ ++ /* check it again */ ++ if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Failed! (0x%02x)\n", temp); ++#endif ++ return -1; ++ } else { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Successful!\n"); ++#endif ++ } ++ } ++ ++ /* Turn off timeout interrupts, set fast host clock */ ++ sis645_write(SMB_CNT, 0x20); ++ ++ /* clear all (sticky) status flags */ ++ temp = sis645_read(SMB_STS); ++ sis645_write(SMB_STS, temp & 0x1e); ++ ++ /* start the transaction by setting bit 4 and size bits */ ++ sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); ++ ++ /* We will always wait for a fraction of a second! */ ++ do { ++ i2c_delay(1); ++ temp = sis645_read(SMB_STS); ++ } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ printk(KERN_DEBUG DRV_NAME ": SMBus Timeout! (0x%02x)\n",temp); ++ result = -1; ++ } ++ ++ /* device error - probably missing ACK */ ++ if (temp & 0x02) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Failed bus transaction!\n"); ++#endif ++ result = -1; ++ } ++ ++ /* bus collision */ ++ if (temp & 0x04) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Bus collision!\n"); ++#endif ++ result = -1; ++ } ++ ++ /* Finish up by resetting the bus */ ++ sis645_write(SMB_STS, temp); ++ if ((temp = sis645_read(SMB_STS))) { ++#ifdef DEBUG ++ printk(KERN_DEBUG DRV_NAME ": Failed reset at end of transaction!" ++ " (0x%02x)\n", temp); ++#endif ++ } ++ ++ return result; ++} ++ ++/* Return -1 on error. */ ++static s32 sis645_access(struct i2c_adapter * adap, u16 addr, ++ unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data * data) ++{ ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ size = SIS645_QUICK; ++ break; ++ ++ case I2C_SMBUS_BYTE: ++ sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis645_write(SMB_CMD, command); ++ size = SIS645_BYTE; ++ break; ++ ++ case I2C_SMBUS_BYTE_DATA: ++ sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis645_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) ++ sis645_write(SMB_BYTE, data->byte); ++ size = SIS645_BYTE_DATA; ++ break; ++ ++ case I2C_SMBUS_PROC_CALL: ++ case I2C_SMBUS_WORD_DATA: ++ sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); ++ sis645_write(SMB_CMD, command); ++ if (read_write == I2C_SMBUS_WRITE) { ++ sis645_write(SMB_BYTE, data->word & 0xff); ++ sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); ++ } ++ size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA); ++ break; ++ ++ case I2C_SMBUS_BLOCK_DATA: ++ /* TO DO: */ ++ printk(KERN_INFO DRV_NAME ": SMBus block not implemented!\n"); ++ return -1; ++ break; ++ ++ default: ++ printk(KERN_INFO DRV_NAME ": Unsupported I2C size\n"); ++ return -1; ++ break; ++ } ++ ++ if (sis645_transaction(size)) ++ return -1; ++ ++ if ((size != SIS645_PROC_CALL) && ++ ((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK))) ++ return 0; ++ ++ switch (size) { ++ case SIS645_BYTE: ++ case SIS645_BYTE_DATA: ++ data->byte = sis645_read(SMB_BYTE); ++ break; ++ ++ case SIS645_WORD_DATA: ++ case SIS645_PROC_CALL: ++ data->word = sis645_read(SMB_BYTE) + ++ (sis645_read(SMB_BYTE + 1) << 8); ++ break; ++ } ++ return 0; ++} ++ ++static u32 sis645_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_PROC_CALL; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = sis645_access, ++ .functionality = sis645_func, ++}; ++ ++static struct i2c_adapter sis645_adapter = { ++ .owner = THIS_MODULE, ++ .name = "unset", ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645, ++ .algo = &smbus_algorithm, ++}; ++ ++static struct pci_device_id sis645_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_SI, ++ .device = PCI_DEVICE_ID_SI_SMBUS, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit sis645_probe(struct pci_dev *dev, ++ const struct pci_device_id *id) ++{ ++ u16 ww = 0; ++ int retval; ++ ++ if (sis645_smbus_base) { ++ dev_err(dev, "Only one device supported.\n"); ++ return -EBUSY; ++ } ++ ++ pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); ++ if (PCI_CLASS_SERIAL_SMBUS != ww) { ++ dev_err(dev, "Unsupported device class 0x%04x!\n", ww); ++ return -ENODEV; ++ } ++ ++ sis645_smbus_base = pci_resource_start(dev, SIS645_BAR); ++ if (!sis645_smbus_base) { ++ dev_err(dev, "SiS645 SMBus base address " ++ "not initialized!\n"); ++ return -EINVAL; ++ } ++ dev_info(dev, "SiS645 SMBus base address: 0x%04x\n", ++ sis645_smbus_base); ++ ++ /* Everything is happy, let's grab the memory and set things up. */ ++ if (!request_region(sis645_smbus_base, SMB_IOSIZE, "sis645-smbus")) { ++ dev_err(dev, "SMBus registers 0x%04x-0x%04x " ++ "already in use!\n", sis645_smbus_base, ++ sis645_smbus_base + SMB_IOSIZE - 1); ++ ++ sis645_smbus_base = 0; ++ return -EINVAL; ++ } ++ ++ sprintf(sis645_adapter.name, "SiS645 SMBus adapter at 0x%04x", ++ sis645_smbus_base); ++ ++ if ((retval = i2c_add_adapter(&sis645_adapter))) { ++ dev_err(dev, "Couldn't register adapter!\n"); ++ release_region(sis645_smbus_base, SMB_IOSIZE); ++ sis645_smbus_base = 0; ++ } ++ ++ return retval; ++} ++ ++static void __devexit sis645_remove(struct pci_dev *dev) ++{ ++ if (sis645_smbus_base) { ++ i2c_del_adapter(&sis645_adapter); ++ release_region(sis645_smbus_base, SMB_IOSIZE); ++ sis645_smbus_base = 0; ++ } ++} ++ ++static struct pci_driver sis645_driver = { ++ .name = "sis645 smbus", ++ .id_table = sis645_ids, ++ .probe = sis645_probe, ++ .remove = __devexit_p(sis645_remove), ++}; ++ ++static int __init i2c_sis645_init(void) ++{ ++ printk(KERN_INFO DRV_NAME ".o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ /* if the required device id is not present, try to HOTPLUG it first */ ++ if (!pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS, NULL)) { ++ ++ printk(KERN_INFO DRV_NAME ": " ++ "Attempting to enable SiS645 SMBus device\n"); ++ ++#ifdef CONFIG_HOTPLUG ++ sis645_hotplug_smbus(); ++#else ++ printk(KERN_INFO DRV_NAME ": " ++ "Requires kernel with CONFIG_HOTPLUG, sorry!\n"); ++#endif ++ } ++ ++ return pci_module_init(&sis645_driver); ++} ++ ++static void __exit i2c_sis645_exit(void) ++{ ++ pci_unregister_driver(&sis645_driver); ++} ++ ++MODULE_AUTHOR("Mark M. Hoffman "); ++MODULE_DESCRIPTION("SiS645 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++/* Register initialization functions using helper macros */ ++module_init(i2c_sis645_init); ++module_exit(i2c_sis645_exit); +--- linux-old/drivers/i2c/i2c-tsunami.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-tsunami.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,155 @@ ++/* ++ i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2001 Oleg Vdovikin ++ ++ Based on code written by Ralph Metzler and ++ Simon Vogl ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* This interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets ++ to gain access to the on-board I2C devices. ++ ++ For more information refer to Compaq's ++ "Tsunami/Typhoon 21272 Chipset Hardware Reference Manual" ++ Order Number: DS-0025-TE ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Memory Presence Detect Register (MPD-RW) bits ++ with except of reserved RAZ bits */ ++ ++#define MPD_DR 0x8 /* Data receive - RO */ ++#define MPD_CKR 0x4 /* Clock receive - RO */ ++#define MPD_DS 0x2 /* Data send - Must be a 1 to receive - WO */ ++#define MPD_CKS 0x1 /* Clock send - WO */ ++ ++static inline void writempd(unsigned long value) ++{ ++ TSUNAMI_cchip->mpd.csr = value; ++ mb(); ++} ++ ++static inline unsigned long readmpd(void) ++{ ++ return TSUNAMI_cchip->mpd.csr; ++} ++ ++static void bit_tsunami_setscl(void *data, int val) ++{ ++ /* read currently setted bits to modify them */ ++ unsigned long bits = readmpd() >> 2; /* assume output == input */ ++ ++ if (val) ++ bits |= MPD_CKS; ++ else ++ bits &= ~MPD_CKS; ++ ++ writempd(bits); ++} ++ ++static void bit_tsunami_setsda(void *data, int val) ++{ ++ /* read currently setted bits to modify them */ ++ unsigned long bits = readmpd() >> 2; /* assume output == input */ ++ ++ if (val) ++ bits |= MPD_DS; ++ else ++ bits &= ~MPD_DS; ++ ++ writempd(bits); ++} ++ ++/* The MPD pins are open drain, so the pins always remain outputs. ++ We rely on the i2c-algo-bit routines to set the pins high before ++ reading the input from other chips. */ ++ ++static int bit_tsunami_getscl(void *data) ++{ ++ return (0 != (readmpd() & MPD_CKR)); ++} ++ ++static int bit_tsunami_getsda(void *data) ++{ ++ return (0 != (readmpd() & MPD_DR)); ++} ++ ++static struct i2c_algo_bit_data tsunami_i2c_bit_data = { ++ .setsda = bit_tsunami_setsda, ++ .setscl = bit_tsunami_setscl, ++ .getsda = bit_tsunami_getsda, ++ .getscl = bit_tsunami_getscl, ++ .udelay = 10, ++ .mdelay = 10, ++ .timeout = HZ/2 ++}; ++ ++static struct i2c_adapter tsunami_i2c_adapter = { ++ .owner = THIS_MODULE, ++ .name = "I2C Tsunami/Typhoon adapter", ++ .id = I2C_HW_B_TSUNA, ++ .algo_data = &tsunami_i2c_bit_data, ++}; ++ ++ ++#if 0 ++static struct pci_driver tsunami_driver = { ++ .name = "tsunami smbus", ++ .id_table = tsunami_ids, ++ .probe = tsunami_probe, ++ .remove = __devexit_p(tsunami_remove), ++}; ++#endif ++ ++static int __init i2c_tsunami_init(void) ++{ ++ printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (hwrpb->sys_type != ST_DEC_TSUNAMI) { ++ printk("i2c-tsunami.o: not Tsunami based system (%ld), module not inserted.\n", hwrpb->sys_type); ++ return -ENXIO; ++ } else { ++ printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", (long) &TSUNAMI_cchip->mpd); ++ } ++ return i2c_bit_add_bus(&tsunami_i2c_adapter); ++} ++ ++ ++static void __exit i2c_tsunami_exit(void) ++{ ++ i2c_bit_del_bus(&tsunami_i2c_adapter); ++} ++ ++ ++ ++MODULE_AUTHOR("Oleg I. Vdovikin "); ++MODULE_DESCRIPTION("Tsunami I2C/SMBus driver"); ++ ++module_init(i2c_tsunami_init); ++module_exit(i2c_tsunami_exit); +--- linux-old/drivers/i2c/i2c-via.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-via.c Mon Dec 13 20:18:42 2004 +@@ -0,0 +1,207 @@ ++/* ++ i2c-via.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ i2c Support for Via Technologies 82C586B South Bridge ++ ++ Copyright (c) 1998, 1999 Kyösti Mälkki ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for HZ */ ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Power management registers */ ++ ++#define PM_CFG_REVID 0x08 /* silicon revision code */ ++#define PM_CFG_IOBASE0 0x20 ++#define PM_CFG_IOBASE1 0x48 ++ ++#define I2C_DIR (pm_io_base+0x40) ++#define I2C_OUT (pm_io_base+0x42) ++#define I2C_IN (pm_io_base+0x44) ++#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ ++#define I2C_SDA 0x04 ++ ++/* io-region reservation */ ++#define IOSPACE 0x06 ++#define IOTEXT "via-i2c" ++ ++static u16 pm_io_base = 0; ++ ++/* ++ It does not appear from the datasheet that the GPIO pins are ++ open drain. So a we set a low value by setting the direction to ++ output and a high value by setting the direction to input and ++ relying on the required I2C pullup. The data value is initialized ++ to 0 in via_init() and never changed. ++*/ ++ ++static void bit_via_setscl(void *data, int state) ++{ ++ outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, ++ I2C_DIR); ++} ++ ++static void bit_via_setsda(void *data, int state) ++{ ++ outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, ++ I2C_DIR); ++} ++ ++static int bit_via_getscl(void *data) ++{ ++ return (0 != (inb(I2C_IN) & I2C_SCL)); ++} ++ ++static int bit_via_getsda(void *data) ++{ ++ return (0 != (inb(I2C_IN) & I2C_SDA)); ++} ++ ++ ++static struct i2c_algo_bit_data bit_data = { ++ .setsda = bit_via_setsda, ++ .setscl = bit_via_setscl, ++ .getsda = bit_via_getsda, ++ .getscl = bit_via_getscl, ++ .udelay = 5, ++ .mdelay = 5, ++ .timeout = HZ ++}; ++ ++static struct i2c_adapter vt586b_adapter = { ++ .owner = THIS_MODULE, ++ .name = "VIA i2c", ++ .id = I2C_HW_B_VIA, ++ .algo_data = &bit_data, ++}; ++ ++ ++static struct pci_device_id vt586b_ids[] __devinitdata = { ++ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ { 0, } ++}; ++ ++static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ u16 base; ++ u8 rev; ++ int res; ++ ++ if (pm_io_base) { ++ printk(KERN_ERR "i2c-via.o: Will only support one host\n"); ++ return -EBUSY; ++ } ++ ++ pci_read_config_byte(dev, PM_CFG_REVID, &rev); ++ ++ switch (rev) { ++ case 0x00: ++ base = PM_CFG_IOBASE0; ++ break; ++ case 0x01: ++ case 0x10: ++ base = PM_CFG_IOBASE1; ++ break; ++ ++ default: ++ base = PM_CFG_IOBASE1; ++ /* later revision */ ++ } ++ ++ pci_read_config_word(dev, base, &pm_io_base); ++ pm_io_base &= (0xff << 8); ++ ++ if (! request_region(I2C_DIR, IOSPACE, IOTEXT)) { ++ printk("i2c-via.o: IO 0x%x-0x%x already in use\n", ++ I2C_DIR, I2C_DIR + IOSPACE); ++ return -EBUSY; ++ } ++ outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); ++ outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); ++ ++ res = i2c_bit_add_bus(&vt586b_adapter); ++ if ( res < 0 ) { ++ release_region(I2C_DIR, IOSPACE); ++ pm_io_base = 0; ++ return res; ++ } ++ return 0; ++} ++ ++static void __devexit vt586b_remove(struct pci_dev *dev) ++{ ++ i2c_bit_del_bus(&vt586b_adapter); ++ release_region(I2C_DIR, IOSPACE); ++ pm_io_base = 0; ++} ++ ++ ++/* Don't register driver to avoid driver conflicts */ ++/* ++static struct pci_driver vt586b_driver = { ++ .name = "vt586b smbus", ++ .id_table = vt586b_ids, ++ .probe = vt586b_probe, ++ .remove = __devexit_p(vt586b_remove), ++}; ++*/ ++ ++static int __init i2c_vt586b_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE); ++/* ++ return pci_module_init(&vt586b_driver); ++*/ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(vt586b_ids, dev); ++ if(id) ++ if(vt586b_probe(dev, id) >= 0) ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++ ++static void __exit i2c_vt586b_exit(void) ++{ ++/* ++ pci_unregister_driver(&vt586b_driver); ++*/ ++ vt586b_remove(NULL); ++} ++ ++ ++MODULE_AUTHOR("Kyösti Mälkki "); ++MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_vt586b_init); ++module_exit(i2c_vt586b_exit); +--- linux-old/drivers/i2c/i2c-viapro.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-viapro.c Mon Dec 13 20:18:43 2004 +@@ -0,0 +1,514 @@ ++/* ++ i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998 - 2002 Frodo Looijaard , ++ Philip Edelbrock , Kyösti Mälkki , ++ Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports Via devices: ++ 82C596A/B (0x3050) ++ 82C596B (0x3051) ++ 82C686A/B ++ 8231 ++ 8233 ++ 8233A (0x3147 and 0x3177) ++ 8235 ++ 8237 ++ Note: we assume there can only be one device, with one SMBus interface. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++#define SMBBA1 0x90 ++#define SMBBA2 0x80 ++#define SMBBA3 0xD0 ++ ++/* SMBus address offsets */ ++static unsigned short vt596_smba; ++#define SMBHSTSTS (vt596_smba + 0) ++#define SMBHSLVSTS (vt596_smba + 1) ++#define SMBHSTCNT (vt596_smba + 2) ++#define SMBHSTCMD (vt596_smba + 3) ++#define SMBHSTADD (vt596_smba + 4) ++#define SMBHSTDAT0 (vt596_smba + 5) ++#define SMBHSTDAT1 (vt596_smba + 6) ++#define SMBBLKDAT (vt596_smba + 7) ++#define SMBSLVCNT (vt596_smba + 8) ++#define SMBSHDWCMD (vt596_smba + 9) ++#define SMBSLVEVT (vt596_smba + 0xA) ++#define SMBSLVDAT (vt596_smba + 0xC) ++ ++/* PCI Address Constants */ ++ ++/* SMBus data in configuration space can be found in two places, ++ We try to select the better one*/ ++ ++static unsigned short smb_cf_hstcfg = 0xD2; ++ ++#define SMBHSTCFG (smb_cf_hstcfg) ++#define SMBSLVC (smb_cf_hstcfg + 1) ++#define SMBSHDW1 (smb_cf_hstcfg + 2) ++#define SMBSHDW2 (smb_cf_hstcfg + 3) ++#define SMBREV (smb_cf_hstcfg + 4) ++ ++/* Other settings */ ++#define MAX_TIMEOUT 500 ++#define ENABLE_INT9 0 ++ ++/* VT82C596 constants */ ++#define VT596_QUICK 0x00 ++#define VT596_BYTE 0x04 ++#define VT596_BYTE_DATA 0x08 ++#define VT596_WORD_DATA 0x0C ++#define VT596_BLOCK_DATA 0x14 ++ ++ ++/* If force is set to anything different from 0, we forcibly enable the ++ VT596. DANGEROUS! */ ++static int force; ++MODULE_PARM(force, "i"); ++MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the VT596 at the given address. VERY DANGEROUS! */ ++static int force_addr; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Forcibly enable the SMBus at the given address. " ++ "EXTREMELY DANGEROUS!"); ++ ++ ++static struct i2c_adapter vt596_adapter; ++ ++/* Another internally used function */ ++static int vt596_transaction(void) ++{ ++ int temp; ++ int result = 0; ++ int timeout = 0; ++ ++ dev_dbg(&vt596_adapter, "Transaction (pre): CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++ ++ /* Make sure the SMBus host is ready to start transmitting */ ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++ dev_dbg(&vt596_adapter, "SMBus busy (0x%02x). " ++ "Resetting...\n", temp); ++ ++ outb_p(temp, SMBHSTSTS); ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++ dev_dbg(&vt596_adapter, "Failed! (0x%02x)\n", temp); ++ ++ return -1; ++ } else { ++ dev_dbg(&vt596_adapter, "Successfull!\n"); ++ } ++ } ++ ++ /* start the transaction by setting bit 6 */ ++ outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); ++ ++ /* We will always wait for a fraction of a second! ++ I don't know if VIA needs this, Intel did */ ++ do { ++ i2c_delay(1); ++ temp = inb_p(SMBHSTSTS); ++ } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); ++ ++ /* If the SMBus is still busy, we give up */ ++ if (timeout >= MAX_TIMEOUT) { ++ result = -1; ++ dev_dbg(&vt596_adapter, "SMBus Timeout!\n"); ++ } ++ ++ if (temp & 0x10) { ++ result = -1; ++ dev_dbg(&vt596_adapter, "Error: Failed bus transaction\n"); ++ } ++ ++ if (temp & 0x08) { ++ result = -1; ++ dev_info(&vt596_adapter, "Bus collision! SMBus may be " ++ "locked until next hard\nreset. (sorry!)\n"); ++ /* Clock stops and slave is stuck in mid-transmission */ ++ } ++ ++ if (temp & 0x04) { ++ result = -1; ++ dev_dbg(&vt596_adapter, "Error: no response!\n"); ++ } ++ ++ if (inb_p(SMBHSTSTS) != 0x00) ++ outb_p(inb(SMBHSTSTS), SMBHSTSTS); ++ ++ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { ++ dev_dbg(&vt596_adapter, "Failed reset at end of " ++ "transaction (%02x)\n", temp); ++ } ++ dev_dbg(&vt596_adapter, "Transaction (post): CNT=%02x, CMD=%02x, " ++ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), ++ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), ++ inb_p(SMBHSTDAT1)); ++ ++ return result; ++} ++ ++/* Return -1 on error. */ ++static s32 vt596_access(struct i2c_adapter *adap, u16 addr, ++ unsigned short flags, char read_write, u8 command, ++ int size, union i2c_smbus_data *data) ++{ ++ int i, len; ++ ++ switch (size) { ++ case I2C_SMBUS_QUICK: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ size = VT596_QUICK; ++ break; ++ case I2C_SMBUS_BYTE: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(command, SMBHSTCMD); ++ size = VT596_BYTE; ++ break; ++ case I2C_SMBUS_BYTE_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) ++ outb_p(data->byte, SMBHSTDAT0); ++ size = VT596_BYTE_DATA; ++ break; ++ case I2C_SMBUS_WORD_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ outb_p(data->word & 0xff, SMBHSTDAT0); ++ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); ++ } ++ size = VT596_WORD_DATA; ++ break; ++ case I2C_SMBUS_BLOCK_DATA: ++ outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), ++ SMBHSTADD); ++ outb_p(command, SMBHSTCMD); ++ if (read_write == I2C_SMBUS_WRITE) { ++ len = data->block[0]; ++ if (len < 0) ++ len = 0; ++ if (len > 32) ++ len = 32; ++ outb_p(len, SMBHSTDAT0); ++ i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= len; i++) ++ outb_p(data->block[i], SMBBLKDAT); ++ } ++ size = VT596_BLOCK_DATA; ++ break; ++ default: ++ dev_warn(&vt596_adapter, "Unsupported transaction %d\n", size); ++ return -1; ++ } ++ ++ outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); ++ ++ if (vt596_transaction()) /* Error in transaction */ ++ return -1; ++ ++ if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) ++ return 0; ++ ++ switch (size) { ++ case VT596_BYTE: ++ /* Where is the result put? I assume here it is in ++ * SMBHSTDAT0 but it might just as well be in the ++ * SMBHSTCMD. No clue in the docs ++ */ ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case VT596_BYTE_DATA: ++ data->byte = inb_p(SMBHSTDAT0); ++ break; ++ case VT596_WORD_DATA: ++ data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); ++ break; ++ case VT596_BLOCK_DATA: ++ data->block[0] = inb_p(SMBHSTDAT0); ++ if (data->block[0] > 32) ++ data->block[0] = 32; ++ i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ ++ for (i = 1; i <= data->block[0]; i++) ++ data->block[i] = inb_p(SMBBLKDAT); ++ break; ++ } ++ return 0; ++} ++ ++static u32 vt596_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++static struct i2c_algorithm smbus_algorithm = { ++ .name = "Non-I2C SMBus adapter", ++ .id = I2C_ALGO_SMBUS, ++ .smbus_xfer = vt596_access, ++ .functionality = vt596_func, ++}; ++ ++static struct i2c_adapter vt596_adapter = { ++ .owner = THIS_MODULE, ++ .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2, ++ .algo = &smbus_algorithm, ++ .name = "unset", ++}; ++ ++static int __devinit vt596_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ unsigned char temp; ++ int error = -ENODEV; ++ ++ /* Determine the address of the SMBus areas */ ++ if (force_addr) { ++ vt596_smba = force_addr & 0xfff0; ++ force = 0; ++ goto found; ++ } ++ ++ if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) || ++ !(vt596_smba & 0x1)) { ++ /* try 2nd address and config reg. for 596 */ ++ if (id->device == PCI_DEVICE_ID_VIA_82C596_3 && ++ !pci_read_config_word(pdev, SMBBA2, &vt596_smba) && ++ (vt596_smba & 0x1)) { ++ smb_cf_hstcfg = 0x84; ++ } else { ++ /* no matches at all */ ++ dev_err(pdev, "Cannot configure " ++ "SMBus I/O Base address\n"); ++ return -ENODEV; ++ } ++ } ++ ++ vt596_smba &= 0xfff0; ++ if (vt596_smba == 0) { ++ dev_err(pdev, "SMBus base address " ++ "uninitialized - upgrade BIOS or use " ++ "force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ ++ found: ++ if (!request_region(vt596_smba, 8, "viapro-smbus")) { ++ dev_err(pdev, "SMBus region 0x%x already in use!\n", ++ vt596_smba); ++ return -ENODEV; ++ } ++ ++ pci_read_config_byte(pdev, SMBHSTCFG, &temp); ++ /* If force_addr is set, we program the new address here. Just to make ++ sure, we disable the VT596 first. */ ++ if (force_addr) { ++ pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe); ++ pci_write_config_word(pdev, id->driver_data, vt596_smba); ++ pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); ++ dev_warn(pdev, "WARNING: SMBus interface set to new " ++ "address 0x%04x!\n", vt596_smba); ++ } else if ((temp & 1) == 0) { ++ if (force) { ++ /* NOTE: This assumes I/O space and other allocations ++ * WERE done by the Bios! Don't complain if your ++ * hardware does weird things after enabling this. ++ * :') Check for Bios updates before resorting to ++ * this. ++ */ ++ pci_write_config_byte(pdev, SMBHSTCFG, temp | 1); ++ dev_info(pdev, "Enabling SMBus device\n"); ++ } else { ++ dev_err(pdev, "SMBUS: Error: Host SMBus " ++ "controller not enabled! - upgrade BIOS or " ++ "use force=1\n"); ++ goto release_region; ++ } ++ } ++ ++ if ((temp & 0x0E) == 8) ++ dev_dbg(pdev, "using Interrupt 9 for SMBus.\n"); ++ else if ((temp & 0x0E) == 0) ++ dev_dbg(pdev, "using Interrupt SMI# for SMBus.\n"); ++ else ++ dev_dbg(pdev, "Illegal Interrupt configuration " ++ "(or code out of date)!\n"); ++ ++ pci_read_config_byte(pdev, SMBREV, &temp); ++ dev_dbg(pdev, "SMBREV = 0x%X\n", temp); ++ dev_dbg(pdev, "VT596_smba = 0x%X\n", vt596_smba); ++ ++ snprintf(vt596_adapter.name, 32, ++ "SMBus Via Pro adapter at %04x", vt596_smba); ++ ++ return i2c_add_adapter(&vt596_adapter); ++ ++ release_region: ++ release_region(vt596_smba, 8); ++ return error; ++} ++ ++static void __devexit vt596_remove(struct pci_dev *pdev) ++{ ++ i2c_del_adapter(&vt596_adapter); ++ release_region(vt596_smba, 8); ++} ++ ++/* 8233A is undefined before kernel 2.4.19 */ ++#ifndef PCI_DEVICE_ID_VIA_8233A ++#define PCI_DEVICE_ID_VIA_8233A 0x3147 ++#endif ++/* 8235 is undefined before kernel 2.4.20 */ ++#ifndef PCI_DEVICE_ID_VIA_8235 ++#define PCI_DEVICE_ID_VIA_8235 0x3177 ++#endif ++/* 8237 is undefined before kernel 2.4.21 */ ++#ifndef PCI_DEVICE_ID_VIA_8237 ++#define PCI_DEVICE_ID_VIA_8237 0x3227 ++#endif ++static struct pci_device_id vt596_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_82C596_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA1, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_82C596B_3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA1, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_82C686_4, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA1, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_8233_0, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA3 ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_8233A, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA3, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_8235, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA3 ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_8237, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA3 ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_VIA, ++ .device = PCI_DEVICE_ID_VIA_8231_4, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SMBBA1, ++ }, ++ { 0, } ++}; ++ ++/* Don't register driver to avoid driver conflicts */ ++/* ++static struct pci_driver vt596_driver = { ++ .name = "vt596 smbus", ++ .id_table = vt596_ids, ++ .probe = vt596_probe, ++ .remove = __devexit_p(vt596_remove), ++}; ++*/ ++ ++static int __init i2c_vt596_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE); ++/* ++ return pci_module_init(&vt596_driver); ++*/ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(vt596_ids, dev); ++ if(id) ++ if(vt596_probe(dev, id) >= 0) ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++ ++static void __exit i2c_vt596_exit(void) ++{ ++/* ++ pci_unregister_driver(&vt596_driver); ++*/ ++ vt596_remove(NULL); ++} ++ ++MODULE_AUTHOR( ++ "Frodo Looijaard and " ++ "Philip Edelbrock "); ++MODULE_DESCRIPTION("vt82c596 SMBus driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_vt596_init); ++module_exit(i2c_vt596_exit); +--- linux-old/drivers/i2c/i2c-voodoo3.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/i2c/i2c-voodoo3.c Mon Dec 13 20:18:43 2004 +@@ -0,0 +1,281 @@ ++/* ++ voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard , ++ Philip Edelbrock , ++ Ralph Metzler , and ++ Mark D. Studebaker ++ ++ Based on code written by Ralph Metzler and ++ Simon Vogl ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* This interfaces to the I2C bus of the Voodoo3 to gain access to ++ the BT869 and possibly other I2C devices. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for HZ */ ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* the only registers we use */ ++#define REG 0x78 ++#define REG2 0x70 ++ ++/* bit locations in the register */ ++#define DDC_ENAB 0x00040000 ++#define DDC_SCL_OUT 0x00080000 ++#define DDC_SDA_OUT 0x00100000 ++#define DDC_SCL_IN 0x00200000 ++#define DDC_SDA_IN 0x00400000 ++#define I2C_ENAB 0x00800000 ++#define I2C_SCL_OUT 0x01000000 ++#define I2C_SDA_OUT 0x02000000 ++#define I2C_SCL_IN 0x04000000 ++#define I2C_SDA_IN 0x08000000 ++ ++/* initialization states */ ++#define INIT2 0x2 ++#define INIT3 0x4 ++ ++/* delays */ ++#define CYCLE_DELAY 10 ++#define TIMEOUT (HZ / 2) ++ ++ ++static void config_v3(struct pci_dev *dev); ++ ++static unsigned long ioaddr; ++ ++/* The voo GPIO registers don't have individual masks for each bit ++ so we always have to read before writing. */ ++ ++static void bit_vooi2c_setscl(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= I2C_SCL_OUT; ++ else ++ r &= ~I2C_SCL_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++static void bit_vooi2c_setsda(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= I2C_SDA_OUT; ++ else ++ r &= ~I2C_SDA_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++/* The GPIO pins are open drain, so the pins always remain outputs. ++ We rely on the i2c-algo-bit routines to set the pins high before ++ reading the input from other chips. */ ++ ++static int bit_vooi2c_getscl(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); ++} ++ ++static int bit_vooi2c_getsda(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); ++} ++ ++static void bit_vooddc_setscl(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= DDC_SCL_OUT; ++ else ++ r &= ~DDC_SCL_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++static void bit_vooddc_setsda(void *data, int val) ++{ ++ unsigned int r; ++ r = readl(ioaddr + REG); ++ if(val) ++ r |= DDC_SDA_OUT; ++ else ++ r &= ~DDC_SDA_OUT; ++ writel(r, ioaddr + REG); ++ readl(ioaddr + REG); /* flush posted write */ ++} ++ ++static int bit_vooddc_getscl(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & DDC_SCL_IN)); ++} ++ ++static int bit_vooddc_getsda(void *data) ++{ ++ return (0 != (readl(ioaddr + REG) & DDC_SDA_IN)); ++} ++ ++ ++/* Configures the chip */ ++ ++void config_v3(struct pci_dev *dev) ++{ ++ unsigned int cadr; ++ ++ /* map Voodoo3 memory */ ++ cadr = dev->resource[0].start; ++ cadr &= PCI_BASE_ADDRESS_MEM_MASK; ++ ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000); ++ if(ioaddr) { ++ writel(0x8160, ioaddr + REG2); ++ writel(0xcffc0020, ioaddr + REG); ++ printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%lx\n", ioaddr); ++ } ++} ++ ++static struct i2c_algo_bit_data voo_i2c_bit_data = { ++ .setsda = bit_vooi2c_setsda, ++ .setscl = bit_vooi2c_setscl, ++ .getsda = bit_vooi2c_getsda, ++ .getscl = bit_vooi2c_getscl, ++ .udelay = CYCLE_DELAY, ++ .mdelay = CYCLE_DELAY, ++ .timeout = TIMEOUT ++}; ++ ++static struct i2c_adapter voodoo3_i2c_adapter = { ++ .owner = THIS_MODULE, ++ .name = "I2C Voodoo3/Banshee adapter", ++ .id = I2C_HW_B_VOO, ++ .algo_data = &voo_i2c_bit_data, ++}; ++ ++static struct i2c_algo_bit_data voo_ddc_bit_data = { ++ .setsda = bit_vooddc_setsda, ++ .setscl = bit_vooddc_setscl, ++ .getsda = bit_vooddc_getsda, ++ .getscl = bit_vooddc_getscl, ++ .udelay = CYCLE_DELAY, ++ .mdelay = CYCLE_DELAY, ++ .timeout = TIMEOUT ++}; ++ ++static struct i2c_adapter voodoo3_ddc_adapter = { ++ .owner = THIS_MODULE, ++ .name = "DDC Voodoo3/Banshee adapter", ++ .id = I2C_HW_B_VOO, ++ .algo_data = &voo_ddc_bit_data, ++}; ++ ++ ++static struct pci_device_id voodoo3_ids[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_3DFX, ++ .device = PCI_DEVICE_ID_3DFX_VOODOO3, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_3DFX, ++ .device = PCI_DEVICE_ID_3DFX_BANSHEE, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, ++ { 0, } ++}; ++ ++static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ int retval; ++ ++ printk("voodoo3: in probe\n"); ++ config_v3(dev); ++ retval = i2c_bit_add_bus(&voodoo3_i2c_adapter); ++ if(retval) ++ return retval; ++ retval = i2c_bit_add_bus(&voodoo3_ddc_adapter); ++ if(retval) ++ i2c_bit_del_bus(&voodoo3_i2c_adapter); ++ return retval; ++} ++ ++static void __devexit voodoo3_remove(struct pci_dev *dev) ++{ ++ i2c_bit_del_bus(&voodoo3_i2c_adapter); ++ i2c_bit_del_bus(&voodoo3_ddc_adapter); ++} ++ ++ ++/* Don't register driver to avoid driver conflicts */ ++/* ++static struct pci_driver voodoo3_driver = { ++ .name = "voodoo3 smbus", ++ .id_table = voodoo3_ids, ++ .probe = voodoo3_probe, ++ .remove = __devexit_p(voodoo3_remove), ++}; ++*/ ++ ++static int __init i2c_voodoo3_init(void) ++{ ++ struct pci_dev *dev; ++ const struct pci_device_id *id; ++ ++ printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE); ++/* ++ return pci_module_init(&voodoo3_driver); ++*/ ++ pci_for_each_dev(dev) { ++ id = pci_match_device(voodoo3_ids, dev); ++ if(id) ++ if(voodoo3_probe(dev, id) >= 0) ++ return 0; ++ } ++ return -ENODEV; ++} ++ ++ ++static void __exit i2c_voodoo3_exit(void) ++{ ++/* ++ pci_unregister_driver(&voodoo3_driver); ++*/ ++ voodoo3_remove(NULL); ++ iounmap((void *)ioaddr); ++} ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); ++MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); ++ ++module_init(i2c_voodoo3_init); ++module_exit(i2c_voodoo3_exit); +--- linux-old/drivers/sensors/adm1021.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/adm1021.c Mon Dec 13 20:18:43 2004 +@@ -0,0 +1,594 @@ ++/* ++ adm1021.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard and ++ Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, ++ 0x4c, 0x4e, SENSORS_I2C_END ++}; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); ++ ++/* adm1021 constants specified below */ ++ ++/* The adm1021 registers */ ++/* Read-only */ ++#define ADM1021_REG_TEMP 0x00 ++#define ADM1021_REG_REMOTE_TEMP 0x01 ++#define ADM1021_REG_STATUS 0x02 ++#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = Analog Devices, 0x49 = TI, ++ 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ ++#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1021A/ADM1023 = 0x3X */ ++#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ ++/* These use different addresses for reading/writing */ ++#define ADM1021_REG_CONFIG_R 0x03 ++#define ADM1021_REG_CONFIG_W 0x09 ++#define ADM1021_REG_CONV_RATE_R 0x04 ++#define ADM1021_REG_CONV_RATE_W 0x0A ++/* These are for the ADM1023's additional precision on the remote temp sensor */ ++#define ADM1021_REG_REM_TEMP_PREC 0x010 ++#define ADM1021_REG_REM_OFFSET 0x011 ++#define ADM1021_REG_REM_OFFSET_PREC 0x012 ++#define ADM1021_REG_REM_TOS_PREC 0x013 ++#define ADM1021_REG_REM_THYST_PREC 0x014 ++/* limits */ ++#define ADM1021_REG_TOS_R 0x05 ++#define ADM1021_REG_TOS_W 0x0B ++#define ADM1021_REG_REMOTE_TOS_R 0x07 ++#define ADM1021_REG_REMOTE_TOS_W 0x0D ++#define ADM1021_REG_THYST_R 0x06 ++#define ADM1021_REG_THYST_W 0x0C ++#define ADM1021_REG_REMOTE_THYST_R 0x08 ++#define ADM1021_REG_REMOTE_THYST_W 0x0E ++/* write-only */ ++#define ADM1021_REG_ONESHOT 0x0F ++ ++#define ADM1021_ALARM_TEMP (ADM1021_ALARM_TEMP_HIGH | ADM1021_ALARM_TEMP_LOW) ++#define ADM1021_ALARM_RTEMP (ADM1021_ALARM_RTEMP_HIGH | ADM1021_ALARM_RTEMP_LOW\ ++ | ADM1021_ALARM_RTEMP_NA) ++#define ADM1021_ALARM_ALL (ADM1021_ALARM_TEMP | ADM1021_ALARM_RTEMP) ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++/* Conversions note: 1021 uses normal integer signed-byte format*/ ++#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) ++ ++/* Each client has this additional data */ ++struct adm1021_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 temp, temp_os, temp_hyst; /* Register values */ ++ u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code; ++ u8 fail; ++ /* Special values for ADM1023 only */ ++ u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, ++ remote_temp_offset, remote_temp_offset_prec; ++}; ++ ++static int adm1021_attach_adapter(struct i2c_adapter *adapter); ++static int adm1021_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void adm1021_init_client(struct i2c_client *client); ++static int adm1021_detach_client(struct i2c_client *client); ++static int adm1021_read_value(struct i2c_client *client, u8 reg); ++static int adm1021_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask); ++static int adm1021_write_value(struct i2c_client *client, u8 reg, ++ u16 value); ++static void adm1021_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1021_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void adm1021_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1021_die_code(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1021_update_client(struct i2c_client *client); ++ ++/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ ++static int read_only = 0; ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver adm1021_driver = { ++ .owner = THIS_MODULE, ++ .name = "ADM1021, MAX1617 sensor driver", ++ .id = I2C_DRIVERID_ADM1021, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = adm1021_attach_adapter, ++ .detach_client = adm1021_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define ADM1021_SYSCTL_TEMP 1200 ++#define ADM1021_SYSCTL_REMOTE_TEMP 1201 ++#define ADM1021_SYSCTL_DIE_CODE 1202 ++#define ADM1021_SYSCTL_ALARMS 1203 ++ ++#define ADM1021_ALARM_TEMP_HIGH 0x40 ++#define ADM1021_ALARM_TEMP_LOW 0x20 ++#define ADM1021_ALARM_RTEMP_HIGH 0x10 ++#define ADM1021_ALARM_RTEMP_LOW 0x08 ++#define ADM1021_ALARM_RTEMP_NA 0x04 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected adm1021. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table adm1021_dir_table_template[] = { ++ {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_temp}, ++ {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_remote_temp}, ++ {ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_die_code}, ++ {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_alarms}, ++ {0} ++}; ++ ++static ctl_table adm1021_max_dir_table_template[] = { ++ {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_temp}, ++ {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_remote_temp}, ++ {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1021_alarms}, ++ {0} ++}; ++ ++static int adm1021_id = 0; ++ ++static int adm1021_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, adm1021_detect); ++} ++ ++static int adm1021_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct adm1021_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto error0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access adm1021_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto error0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &adm1021_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00 ++ || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00 ++ || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) { ++ err = -ENODEV; ++ goto error1; ++ } ++ } ++ ++ /* Determine the chip type. */ ++ ++ if (kind <= 0) { ++ i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); ++ if (i == 0x41) ++ if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0xF0) == 0x30) ++ kind = adm1023; ++ else ++ kind = adm1021; ++ else if (i == 0x49) ++ kind = thmc10; ++ else if (i == 0x23) ++ kind = gl523sm; ++ else if ((i == 0x4d) && ++ (adm1021_read_value ++ (new_client, ADM1021_REG_DEV_ID) == 0x01)) ++ kind = max1617a; ++ else if (i == 0x54) ++ kind = mc1066; ++ /* LM84 Mfr ID in a different place, and it has more unused bits */ ++ else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00 ++ && (kind == 0 /* skip extra detection */ ++ || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00 ++ && (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00))) ++ kind = lm84; ++ else ++ kind = max1617; ++ } ++ ++ if (kind == max1617) { ++ type_name = "max1617"; ++ client_name = "MAX1617 chip"; ++ } else if (kind == max1617a) { ++ type_name = "max1617a"; ++ client_name = "MAX1617A chip"; ++ } else if (kind == adm1021) { ++ type_name = "adm1021"; ++ client_name = "ADM1021 chip"; ++ } else if (kind == adm1023) { ++ type_name = "adm1023"; ++ client_name = "ADM1023 chip"; ++ } else if (kind == thmc10) { ++ type_name = "thmc10"; ++ client_name = "THMC10 chip"; ++ } else if (kind == lm84) { ++ type_name = "lm84"; ++ client_name = "LM84 chip"; ++ } else if (kind == gl523sm) { ++ type_name = "gl523sm"; ++ client_name = "GL523SM chip"; ++ } else if (kind == mc1066) { ++ type_name = "mc1066"; ++ client_name = "MC1066 chip"; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = adm1021_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto error3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ data->type == adm1021 ? adm1021_dir_table_template : ++ adm1021_max_dir_table_template)) < 0) { ++ err = i; ++ goto error4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the ADM1021 chip */ ++ if (kind != lm84) ++ adm1021_init_client(new_client); ++ return 0; ++ ++ error4: ++ i2c_detach_client(new_client); ++ error3: ++ error1: ++ kfree(data); ++ error0: ++ return err; ++} ++ ++static void adm1021_init_client(struct i2c_client *client) ++{ ++ /* Enable ADC and disable suspend mode */ ++ adm1021_write_value(client, ADM1021_REG_CONFIG_W, ++ adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF); ++ /* Set Conversion rate to 1/sec (this can be tinkered with) */ ++ adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); ++} ++ ++static int adm1021_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct adm1021_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("adm1021.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* All registers are byte-sized */ ++static int adm1021_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* only update value if read succeeded; set fail bit if failed */ ++static int adm1021_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask) ++{ ++ int i; ++ struct adm1021_data *data = client->data; ++ ++ i = i2c_smbus_read_byte_data(client, reg); ++ if (i < 0) { ++ data->fail |= mask; ++ return i; ++ } ++ *val = i; ++ return 0; ++} ++ ++static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if (read_only > 0) ++ return 0; ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void adm1021_update_client(struct i2c_client *client) ++{ ++ struct adm1021_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting adm1021 update\n"); ++#endif ++ ++ data->fail = 0; ++ adm1021_rd_good(&(data->temp), client, ADM1021_REG_TEMP, ++ ADM1021_ALARM_TEMP); ++ adm1021_rd_good(&(data->temp_os), client, ADM1021_REG_TOS_R, ++ ADM1021_ALARM_TEMP); ++ adm1021_rd_good(&(data->temp_hyst), client, ++ ADM1021_REG_THYST_R, ADM1021_ALARM_TEMP); ++ adm1021_rd_good(&(data->remote_temp), client, ++ ADM1021_REG_REMOTE_TEMP, ADM1021_ALARM_RTEMP); ++ adm1021_rd_good(&(data->remote_temp_os), client, ++ ADM1021_REG_REMOTE_TOS_R, ADM1021_ALARM_RTEMP); ++ adm1021_rd_good(&(data->remote_temp_hyst), client, ++ ADM1021_REG_REMOTE_THYST_R, ++ ADM1021_ALARM_RTEMP); ++ data->alarms = ADM1021_ALARM_ALL; ++ if (!adm1021_rd_good(&(data->alarms), client, ++ ADM1021_REG_STATUS, 0)) ++ data->alarms &= ADM1021_ALARM_ALL; ++ if (data->type == adm1021) ++ adm1021_rd_good(&(data->die_code), client, ++ ADM1021_REG_DIE_CODE, 0); ++ if (data->type == adm1023) { ++ adm1021_rd_good(&(data->remote_temp_prec), client, ++ ADM1021_REG_REM_TEMP_PREC, ++ ADM1021_ALARM_TEMP); ++ adm1021_rd_good(&(data->remote_temp_os_prec), client, ++ ADM1021_REG_REM_TOS_PREC, ++ ADM1021_ALARM_RTEMP); ++ adm1021_rd_good(&(data->remote_temp_hyst_prec), client, ++ ADM1021_REG_REM_THYST_PREC, ++ ADM1021_ALARM_RTEMP); ++ adm1021_rd_good(&(data->remote_temp_offset), client, ++ ADM1021_REG_REM_OFFSET, ++ ADM1021_ALARM_RTEMP); ++ adm1021_rd_good(&(data->remote_temp_offset_prec), ++ client, ADM1021_REG_REM_OFFSET_PREC, ++ ADM1021_ALARM_RTEMP); ++ } ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void adm1021_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1021_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1021_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_os); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_os = TEMP_TO_REG(results[0]); ++ adm1021_write_value(client, ADM1021_REG_TOS_W, ++ data->temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ adm1021_write_value(client, ADM1021_REG_THYST_W, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void adm1021_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1021_data *data = client->data; ++ int prec = 0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ if (data->type == adm1023) { *nrels_mag = 3; } ++ else { *nrels_mag = 0; } ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1021_update_client(client); ++ results[0] = TEMP_FROM_REG(data->remote_temp_os); ++ results[1] = TEMP_FROM_REG(data->remote_temp_hyst); ++ results[2] = TEMP_FROM_REG(data->remote_temp); ++ if (data->type == adm1023) { ++ results[0]=results[0]*1000 + ++ ((data->remote_temp_os_prec >> 5) * 125); ++ results[1]=results[1]*1000 + ++ ((data->remote_temp_hyst_prec >> 5) * 125); ++ results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + ++ ((data->remote_temp_offset_prec >> 5) * 125); ++ results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + ++ ((data->remote_temp_prec >> 5) * 125); ++ *nrels_mag = 4; ++ } else { ++ *nrels_mag = 3; ++ } ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (data->type == adm1023) { ++ prec=((results[0]-((results[0]/1000)*1000))/125)<<5; ++ adm1021_write_value(client, ++ ADM1021_REG_REM_TOS_PREC, ++ prec); ++ results[0]=results[0]/1000; ++ data->remote_temp_os_prec=prec; ++ } ++ data->remote_temp_os = TEMP_TO_REG(results[0]); ++ adm1021_write_value(client, ++ ADM1021_REG_REMOTE_TOS_W, ++ data->remote_temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ if (data->type == adm1023) { ++ prec=((results[1]-((results[1]/1000)*1000))/125)<<5; ++ adm1021_write_value(client, ++ ADM1021_REG_REM_THYST_PREC, ++ prec); ++ results[1]=results[1]/1000; ++ data->remote_temp_hyst_prec=prec; ++ } ++ data->remote_temp_hyst = TEMP_TO_REG(results[1]); ++ adm1021_write_value(client, ++ ADM1021_REG_REMOTE_THYST_W, ++ data->remote_temp_hyst); ++ } ++ if (*nrels_mag >= 3) { ++ if (data->type == adm1023) { ++ prec=((results[2]-((results[2]/1000)*1000))/125)<<5; ++ adm1021_write_value(client, ++ ADM1021_REG_REM_OFFSET_PREC, ++ prec); ++ results[2]=results[2]/1000; ++ data->remote_temp_offset_prec=prec; ++ data->remote_temp_offset=results[2]; ++ adm1021_write_value(client, ++ ADM1021_REG_REM_OFFSET, ++ data->remote_temp_offset); ++ } ++ } ++ } ++} ++ ++void adm1021_die_code(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1021_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1021_update_client(client); ++ results[0] = data->die_code; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* Can't write to it */ ++ } ++} ++ ++void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1021_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1021_update_client(client); ++ results[0] = data->alarms | data->fail; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* Can't write to it */ ++ } ++} ++ ++static int __init sm_adm1021_init(void) ++{ ++ printk(KERN_INFO "adm1021.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&adm1021_driver); ++} ++ ++static void __exit sm_adm1021_exit(void) ++{ ++ i2c_del_driver(&adm1021_driver); ++} ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("adm1021 driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(read_only, "i"); ++MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); ++ ++module_init(sm_adm1021_init) ++module_exit(sm_adm1021_exit) +--- linux-old/drivers/sensors/adm1024.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/adm1024.c Mon Dec 13 20:18:43 2004 +@@ -0,0 +1,782 @@ ++/* ++ adm1024.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Add by Ken Bowley from the adm1025.c written by ++ Gordon Wu and from adm9240.c written by ++ Copyright (c) 1999 Frodo Looijaard ++ and Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports the Analog Devices ADM1024. See doc/chips/adm1024 for details */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(adm1024); ++ ++/* Many ADM1024 constants specified below */ ++ ++#define ADM1024_REG_IN_MAX(nr) (0x2b + (nr) * 2) ++#define ADM1024_REG_IN_MIN(nr) (0x2c + (nr) * 2) ++#define ADM1024_REG_IN(nr) (0x20 + (nr)) ++ ++/* The ADM1024 registers */ ++#define ADM1024_REG_INT_TEMP_TRIP_SET 0x13 ++#define ADM1024_REG_EXT_TEMP_TRIP_SET 0x14 ++#define ADM1024_REG_TEST 0x15 ++#define ADM1024_REG_CHANNEL_MODE 0x16 ++#define ADM1024_REG_INT_TEMP_TRIP 0x17 /* read only */ ++#define ADM1024_REG_EXT_TEMP_TRIP 0x18 /* read only */ ++#define ADM1024_REG_ANALOG_OUT 0x19 ++#define ADM1024_REG_AIN1_LOW_LIMIT 0x1A ++#define ADM1024_REG_AIN2_LOW_LIMIT 0x1B ++/* These are all read-only */ ++#define ADM1024_REG_2_5V 0x20 /* 2.5V Measured Value/EXT Temp 2 */ ++#define ADM1024_REG_VCCP1 0x21 ++#define ADM1024_REG_3_3V 0x22 /* VCC Measured Value */ ++#define ADM1024_REG_5V 0x23 ++#define ADM1024_REG_12V 0x24 ++#define ADM1024_REG_VCCP2 0x25 ++#define ADM1024_REG_EXT_TEMP1 0x26 ++#define ADM1024_REG_TEMP 0x27 ++#define ADM1024_REG_FAN1 0x28 /* FAN1/AIN1 Value */ ++#define ADM1024_REG_FAN2 0x29 /* FAN2/AIN2 Value */ ++#define ADM1024_REG_COMPANY_ID 0x3E /* 0x41 for ADM1024 */ ++#define ADM1024_REG_DIE_REV 0x3F ++/* These are read/write */ ++#define ADM1024_REG_2_5V_HIGH 0x2B /* 2.5V/Ext Temp2 High Limit */ ++#define ADM1024_REG_2_5V_LOW 0x2C /* 2.5V/Ext Temp2 Low Limit */ ++#define ADM1024_REG_VCCP1_HIGH 0x2D ++#define ADM1024_REG_VCCP1_LOW 0x2E ++#define ADM1024_REG_3_3V_HIGH 0x2F /* VCC High Limit */ ++#define ADM1024_REG_3_3V_LOW 0x30 /* VCC Low Limit */ ++#define ADM1024_REG_5V_HIGH 0x31 ++#define ADM1024_REG_5V_LOW 0x32 ++#define ADM1024_REG_12V_HIGH 0x33 ++#define ADM1024_REG_12V_LOW 0x34 ++#define ADM1024_REG_VCCP2_HIGH 0x35 ++#define ADM1024_REG_VCCP2_LOW 0x36 ++#define ADM1024_REG_EXT_TEMP1_HIGH 0x37 ++#define ADM1024_REG_EXT_TEMP1_LOW 0x38 ++#define ADM1024_REG_TOS 0x39 ++#define ADM1024_REG_THYST 0x3A ++#define ADM1024_REG_FAN1_MIN 0x3B ++#define ADM1024_REG_FAN2_MIN 0x3C ++ ++#define ADM1024_REG_CONFIG 0x40 ++#define ADM1024_REG_INT1_STAT 0x41 ++#define ADM1024_REG_INT2_STAT 0x42 ++#define ADM1024_REG_INT1_MASK 0x43 ++#define ADM1024_REG_INT2_MASK 0x44 ++ ++#define ADM1024_REG_CHASSIS_CLEAR 0x46 ++#define ADM1024_REG_VID_FAN_DIV 0x47 ++#define ADM1024_REG_I2C_ADDR 0x48 ++#define ADM1024_REG_VID4 0x49 ++#define ADM1024_REG_CONFIG2 0x4A ++#define ADM1024_REG_TEMP_CONFIG 0x4B ++#define ADM1024_REG_EXTMODE1 0x4C /* Interupt Status Register Mirror No. 1 */ ++#define ADM1024_REG_EXTMODE2 0x4D /* Interupt Status Register Mirror No. 2 */ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) ++#define IN_FROM_REG(val,nr) (val) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:\ ++ (val)==255?0:1350000/((div)*(val))) ++ ++#define TEMP_FROM_REG(temp) \ ++ ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ ++ ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ ++ ++#define EXT_TEMP_FROM_REG(temp) (((temp)>0x80?(temp)-0x100:(temp))*10) ++ ++ ++#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) ++ ++#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10), \ ++ 0,255) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) ++ ++#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ ++ 205-(val)*5) ++ ++/* For each registered ADM1024, we need to keep some data in memory. That ++ data is pointed to by adm1024_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new adm1024 client is ++ allocated. */ ++struct adm1024_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[6]; /* Register value */ ++ u8 in_max[6]; /* Register value */ ++ u8 in_min[6]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ int temp; /* Temp, shifted right */ ++ u8 temp_os_max; /* Register value */ ++ u8 temp_os_hyst; /* Register value */ ++ int temp1; /* Ext Temp 1 */ ++ u8 temp1_os_max; ++ u8 temp1_os_hyst; ++ int temp2; /* Ext Temp 2 */ ++ u8 temp2_os_max; ++ u8 temp2_os_hyst; ++ u16 alarms; /* Register encoding, combined */ ++ u8 analog_out; /* Register value */ ++ u8 vid; /* Register value combined */ ++}; ++ ++ ++ ++static int adm1024_attach_adapter(struct i2c_adapter *adapter); ++static int adm1024_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int adm1024_detach_client(struct i2c_client *client); ++ ++static int adm1024_read_value(struct i2c_client *client, u8 register); ++static int adm1024_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void adm1024_update_client(struct i2c_client *client); ++static void adm1024_init_client(struct i2c_client *client); ++ ++ ++static void adm1024_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_temp1(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_temp2(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1024_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void adm1024_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int adm1024_id = 0; ++ ++static struct i2c_driver adm1024_driver = { ++ .owner = THIS_MODULE, ++ .name = "ADM1024 sensor driver", ++ .id = I2C_DRIVERID_ADM1024, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = adm1024_attach_adapter, ++ .detach_client = adm1024_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define ADM1024_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define ADM1024_SYSCTL_IN1 1001 ++#define ADM1024_SYSCTL_IN2 1002 ++#define ADM1024_SYSCTL_IN3 1003 ++#define ADM1024_SYSCTL_IN4 1004 ++#define ADM1024_SYSCTL_IN5 1005 ++#define ADM1024_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define ADM1024_SYSCTL_FAN2 1102 ++#define ADM1024_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ ++#define ADM1024_SYSCTL_TEMP1 1290 /* Degrees Celcius */ ++#define ADM1024_SYSCTL_TEMP2 1295 /* Degrees Celcius */ ++#define ADM1024_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define ADM1024_SYSCTL_ALARMS 2001 /* bitvector */ ++#define ADM1024_SYSCTL_ANALOG_OUT 2002 ++#define ADM1024_SYSCTL_VID 2003 ++ ++#define ADM1024_ALARM_IN0 0x0001 ++#define ADM1024_ALARM_IN1 0x0002 ++#define ADM1024_ALARM_IN2 0x0004 ++#define ADM1024_ALARM_IN3 0x0008 ++#define ADM1024_ALARM_IN4 0x0100 ++#define ADM1024_ALARM_IN5 0x0200 ++#define ADM1024_ALARM_FAN1 0x0040 ++#define ADM1024_ALARM_FAN2 0x0080 ++#define ADM1024_ALARM_TEMP 0x0010 ++#define ADM1024_ALARM_TEMP1 0x0020 ++#define ADM1024_ALARM_TEMP2 0x0001 ++#define ADM1024_ALARM_CHAS 0x1000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected ADM1024. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table adm1024_dir_table_template[] = { ++ {ADM1024_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_in}, ++ {ADM1024_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_fan}, ++ {ADM1024_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_fan}, ++ {ADM1024_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_temp}, ++ {ADM1024_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_temp1}, ++ {ADM1024_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_temp2}, ++ {ADM1024_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_fan_div}, ++ {ADM1024_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_alarms}, ++ {ADM1024_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_analog_out}, ++ {ADM1024_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1024_vid}, ++ {0} ++}; ++ ++static int adm1024_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, adm1024_detect); ++} ++ ++static int adm1024_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct adm1024_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("adm1024.o: adm1024_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access adm1024_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct adm1024_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &adm1024_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if((adm1024_read_value(new_client, ADM1024_REG_CONFIG) & 0x80) != 0x00) ++ goto ERROR1; ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ i = adm1024_read_value(new_client, ADM1024_REG_COMPANY_ID); ++ if (i == 0x41) ++ kind = adm1024; ++ else { ++ if (kind == 0) ++ printk ++ ("adm1024.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ goto ERROR1; ++ } ++ } ++ ++ if (kind == adm1024) { ++ type_name = "adm1024"; ++ client_name = "ADM1024 chip"; ++ } else { ++#ifdef DEBUG ++ printk("adm1024.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = adm1024_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ adm1024_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the ADM1024 chip */ ++ adm1024_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int adm1024_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct adm1024_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("adm1024.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int adm1024_read_value(struct i2c_client *client, u8 reg) ++{ ++ return 0xFF & i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int adm1024_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void adm1024_init_client(struct i2c_client *client) ++{ ++ /* Enable temperature channel 2 */ ++ adm1024_write_value(client, ADM1024_REG_CHANNEL_MODE, adm1024_read_value(client, ADM1024_REG_CHANNEL_MODE) | 0x04); ++ ++ /* Start monitoring */ ++ adm1024_write_value(client, ADM1024_REG_CONFIG, 0x07); ++} ++ ++static void adm1024_update_client(struct i2c_client *client) ++{ ++ struct adm1024_data *data = client->data; ++ u8 i; ++ ++ down(&data->update_lock); ++ ++ if ( ++ (jiffies - data->last_updated > ++ (data->type == adm1024 ? HZ / 2 : HZ * 2)) ++ || (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting adm1024 update\n"); ++#endif ++ for (i = 0; i <= 5; i++) { ++ data->in[i] = ++ adm1024_read_value(client, ADM1024_REG_IN(i)); ++ data->in_min[i] = ++ adm1024_read_value(client, ++ ADM1024_REG_IN_MIN(i)); ++ data->in_max[i] = ++ adm1024_read_value(client, ++ ADM1024_REG_IN_MAX(i)); ++ } ++ data->fan[0] = ++ adm1024_read_value(client, ADM1024_REG_FAN1); ++ data->fan_min[0] = ++ adm1024_read_value(client, ADM1024_REG_FAN1_MIN); ++ data->fan[1] = ++ adm1024_read_value(client, ADM1024_REG_FAN2); ++ data->fan_min[1] = ++ adm1024_read_value(client, ADM1024_REG_FAN2_MIN); ++ data->temp = ++ (adm1024_read_value(client, ADM1024_REG_TEMP) << 1) + ++ ((adm1024_read_value ++ (client, ADM1024_REG_TEMP_CONFIG) & 0x80) >> 7); ++ data->temp_os_max = ++ adm1024_read_value(client, ADM1024_REG_TOS); ++ data->temp_os_hyst = ++ adm1024_read_value(client, ADM1024_REG_THYST); ++ data->temp1 = ++ adm1024_read_value(client, ADM1024_REG_EXT_TEMP1); ++ data->temp1_os_max = ++ adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_HIGH); ++ data->temp1_os_hyst = ++ adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_LOW); ++ data->temp2 = ++ adm1024_read_value(client, ADM1024_REG_2_5V); ++ data->temp2_os_max = ++ adm1024_read_value(client, ADM1024_REG_2_5V_HIGH); ++ data->temp2_os_hyst = ++ adm1024_read_value(client, ADM1024_REG_2_5V_LOW); ++ ++ i = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = (i >> 6) & 0x03; ++ data->vid = i & 0x0f; ++ data->vid |= ++ (adm1024_read_value(client, ADM1024_REG_VID4) & 0x01) ++ << 4; ++ ++ data->alarms = ++ adm1024_read_value(client, ++ ADM1024_REG_INT1_STAT) + ++ (adm1024_read_value(client, ADM1024_REG_INT2_STAT) << ++ 8); ++ data->analog_out = ++ adm1024_read_value(client, ADM1024_REG_ANALOG_OUT); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void adm1024_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ ++ int scales[6] = { 250, 225, 330, 500, 1200, 270 }; ++ ++ struct adm1024_data *data = client->data; ++ int nr = ctl_name - ADM1024_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = ++ IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; ++ results[1] = ++ IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; ++ results[2] = ++ IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = ++ IN_TO_REG((results[0] * 192) / scales[nr], nr); ++ adm1024_write_value(client, ADM1024_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = ++ IN_TO_REG((results[1] * 192) / scales[nr], nr); ++ adm1024_write_value(client, ADM1024_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void adm1024_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ int nr = ctl_name - ADM1024_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data-> ++ fan_div[nr - 1])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr - ++ 1])); ++ adm1024_write_value(client, ++ nr == ++ 1 ? ADM1024_REG_FAN1_MIN : ++ ADM1024_REG_FAN2_MIN, ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void adm1024_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); ++ adm1024_write_value(client, ADM1024_REG_TOS, ++ data->temp_os_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); ++ adm1024_write_value(client, ADM1024_REG_THYST, ++ data->temp_os_hyst); ++ } ++ } ++} ++ ++void adm1024_temp1(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = TEMP_LIMIT_FROM_REG(data->temp1_os_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->temp1_os_hyst); ++ results[2] = EXT_TEMP_FROM_REG(data->temp1); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp1_os_max = TEMP_LIMIT_TO_REG(results[0]); ++ adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH, ++ data->temp1_os_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp1_os_hyst = TEMP_LIMIT_TO_REG(results[1]); ++ adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW, ++ data->temp1_os_hyst); ++ } ++ } ++} ++ ++void adm1024_temp2(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = TEMP_LIMIT_FROM_REG(data->temp2_os_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->temp2_os_hyst); ++ results[2] = EXT_TEMP_FROM_REG(data->temp2); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp2_os_max = TEMP_LIMIT_TO_REG(results[0]); ++ adm1024_write_value(client, ADM1024_REG_2_5V_HIGH, ++ data->temp2_os_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp2_os_hyst = TEMP_LIMIT_TO_REG(results[1]); ++ adm1024_write_value(client, ADM1024_REG_2_5V_LOW, ++ data->temp2_os_hyst); ++ } ++ } ++} ++ ++void adm1024_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void adm1024_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ adm1024_write_value(client, ++ ADM1024_REG_VID_FAN_DIV, old); ++ } ++ } ++} ++ ++void adm1024_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = data->analog_out; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->analog_out = results[0]; ++ adm1024_write_value(client, ADM1024_REG_ANALOG_OUT, ++ data->analog_out); ++ } ++ } ++} ++ ++void adm1024_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1024_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1024_update_client(client); ++ results[0] = VID_FROM_REG(data->vid); ++ *nrels_mag = 1; ++ } ++} ++ ++static int __init sm_adm1024_init(void) ++{ ++ printk("adm1024.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&adm1024_driver); ++} ++ ++static void __exit sm_adm1024_exit(void) ++{ ++ i2c_del_driver(&adm1024_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("ADM1024 driver"); ++ ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_adm1024_init); ++module_exit(sm_adm1024_exit); +--- linux-old/drivers/sensors/adm1025.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/adm1025.c Mon Dec 13 20:18:43 2004 +@@ -0,0 +1,594 @@ ++/* ++ adm1025.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2000 Chen-Yuan Wu ++ Copyright (c) 2003-2004 Jean Delvare ++ ++ Based on the adm9240 driver. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports the Analog Devices ADM1025 and the Philips NE1619. ++ See doc/chips/adm1025 for details */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_2(adm1025, ne1619); ++ ++/* Many ADM1025 constants specified below */ ++ ++ ++/* The ADM1025 registers */ ++ ++/* These are all read-only */ ++#define ADM1025_REG_2_5V 0x20 /* not used directly, see */ ++#define ADM1025_REG_VCCP1 0x21 /* ADM1025_REG_IN(nr) below */ ++#define ADM1025_REG_3_3V 0x22 ++#define ADM1025_REG_5V 0x23 ++#define ADM1025_REG_12V 0x24 ++#define ADM1025_REG_VCC 0x25 ++ ++#define ADM1025_REG_RTEMP 0x26 /* not used directly, see */ ++#define ADM1025_REG_LTEMP 0x27 /* ADM1025_REG_TEMP(nr) below */ ++ ++#define ADM1025_REG_COMPANY_ID 0x3E /* 0x41 for Analog Devices, ++ 0xA1 for Philips */ ++#define ADM1025_REG_DIE_REV 0x3F /* 0x20-0x2F for ADM1025 and compatible */ ++ ++#define ADM1025_REG_STATUS1 0x41 ++#define ADM1025_REG_STATUS2 0x42 ++ ++#define ADM1025_REG_VID 0x47 ++#define ADM1025_REG_VID4 0x49 /* actually R/W ++ but we don't write to it */ ++ ++/* These are read/write */ ++#define ADM1025_REG_2_5V_HIGH 0x2B /* not used directly, see */ ++#define ADM1025_REG_2_5V_LOW 0x2C /* ADM1025_REG_IN_MAX(nr) and */ ++#define ADM1025_REG_VCCP1_HIGH 0x2D /* ADM1025_REG_IN_MIN(nr) below */ ++#define ADM1025_REG_VCCP1_LOW 0x2E ++#define ADM1025_REG_3_3V_HIGH 0x2F ++#define ADM1025_REG_3_3V_LOW 0x30 ++#define ADM1025_REG_5V_HIGH 0x31 ++#define ADM1025_REG_5V_LOW 0x32 ++#define ADM1025_REG_12V_HIGH 0x33 ++#define ADM1025_REG_12V_LOW 0x34 ++#define ADM1025_REG_VCC_HIGH 0x35 ++#define ADM1025_REG_VCC_LOW 0x36 ++ ++#define ADM1025_REG_RTEMP_HIGH 0x37 /* not used directly, see */ ++#define ADM1025_REG_RTEMP_LOW 0x38 /* ADM1025_REG_TEMP_MAX(nr) and */ ++#define ADM1025_REG_LTEMP_HIGH 0x39 /* ADM1025_REG_TEMP_MIN(nr) below */ ++#define ADM1025_REG_LTEMP_LOW 0x3A ++ ++#define ADM1025_REG_CONFIG 0x40 ++ ++/* Useful macros */ ++#define ADM1025_REG_IN(nr) (ADM1025_REG_2_5V + (nr)) ++#define ADM1025_REG_IN_MAX(nr) (ADM1025_REG_2_5V_HIGH + (nr) * 2) ++#define ADM1025_REG_IN_MIN(nr) (ADM1025_REG_2_5V_LOW + (nr) * 2) ++#define ADM1025_REG_TEMP(nr) (ADM1025_REG_RTEMP + (nr)) ++#define ADM1025_REG_TEMP_HIGH(nr) (ADM1025_REG_RTEMP_HIGH + (nr) * 2) ++#define ADM1025_REG_TEMP_LOW(nr) (ADM1025_REG_RTEMP_LOW + (nr) * 2) ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val) SENSORS_LIMIT(val, 0, 255) ++#define IN_FROM_REG(val) (val) ++ ++#define TEMP_FROM_REG(val) (((val)>=0x80?(val)-0x100:(val))*10) ++#define TEMP_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),-128,127) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++/* For each registered ADM1025, we need to keep some data in memory. That ++ data is pointed to by adm1025_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new adm1025 client is ++ allocated. */ ++struct adm1025_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[6]; /* Register value */ ++ u8 in_max[6]; /* Register value */ ++ u8 in_min[6]; /* Register value */ ++ u8 temp[2]; /* Register value */ ++ u8 temp_high[2]; /* Register value */ ++ u8 temp_low[2]; /* Register value */ ++ u16 alarms; /* Register encoding, combined */ ++ u8 vid; /* Register value combined */ ++ u8 vrm; ++}; ++ ++ ++static int adm1025_attach_adapter(struct i2c_adapter *adapter); ++static int adm1025_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int adm1025_detach_client(struct i2c_client *client); ++static void adm1025_update_client(struct i2c_client *client); ++static void adm1025_init_client(struct i2c_client *client); ++ ++ ++static void adm1025_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1025_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1025_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1025_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1025_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int adm1025_id = 0; ++ ++static struct i2c_driver adm1025_driver = { ++ .owner = THIS_MODULE, ++ .name = "ADM1025 sensor driver", ++ .id = I2C_DRIVERID_ADM1025, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = adm1025_attach_adapter, ++ .detach_client = adm1025_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define ADM1025_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define ADM1025_SYSCTL_IN1 1001 ++#define ADM1025_SYSCTL_IN2 1002 ++#define ADM1025_SYSCTL_IN3 1003 ++#define ADM1025_SYSCTL_IN4 1004 ++#define ADM1025_SYSCTL_IN5 1005 ++ ++#define ADM1025_SYSCTL_RTEMP 1250 /* Degrees Celcius * 10 */ ++#define ADM1025_SYSCTL_TEMP 1251 ++ ++#define ADM1025_SYSCTL_ALARMS 2001 /* bitvector */ ++#define ADM1025_SYSCTL_VID 2003 /* Volts * 1000 */ ++#define ADM1025_SYSCTL_VRM 2004 ++ ++#define ADM1025_ALARM_IN0 0x0001 ++#define ADM1025_ALARM_IN1 0x0002 ++#define ADM1025_ALARM_IN2 0x0004 ++#define ADM1025_ALARM_IN3 0x0008 ++#define ADM1025_ALARM_IN4 0x0100 ++#define ADM1025_ALARM_IN5 0x0200 ++#define ADM1025_ALARM_RTEMP 0x0020 ++#define ADM1025_ALARM_TEMP 0x0010 ++#define ADM1025_ALARM_RFAULT 0x4000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected ADM1025. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table adm1025_dir_table_template[] = { ++ {ADM1025_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_in}, ++ {ADM1025_SYSCTL_RTEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_temp}, ++ {ADM1025_SYSCTL_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_temp}, ++ {ADM1025_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_alarms}, ++ {ADM1025_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_vid}, ++ {ADM1025_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1025_vrm}, ++ {0} ++}; ++ ++static int adm1025_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, adm1025_detect); ++} ++ ++static int adm1025_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct adm1025_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("adm1025.o: adm1025_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access adm1025_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &adm1025_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ((i2c_smbus_read_byte_data(new_client, ++ ADM1025_REG_CONFIG) & 0x80) != 0x00 ++ || (i2c_smbus_read_byte_data(new_client, ++ ADM1025_REG_STATUS1) & 0xC0) != 0x00 ++ || (i2c_smbus_read_byte_data(new_client, ++ ADM1025_REG_STATUS2) & 0xBC) != 0x00) ++ goto ERROR1; ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ u8 man_id, chip_id; ++ ++ man_id = i2c_smbus_read_byte_data(new_client, ++ ADM1025_REG_COMPANY_ID); ++ chip_id = i2c_smbus_read_byte_data(new_client, ++ ADM1025_REG_DIE_REV); ++ ++ if (man_id == 0x41) { /* Analog Devices */ ++ if ((chip_id & 0xF0) == 0x20) /* ADM1025 */ ++ kind = adm1025; ++ } else if (man_id == 0xA1) { /* Philips */ ++ if (address != 0x2E ++ && (chip_id & 0xF0) == 0x20) /* NE1619 */ ++ kind = ne1619; ++ } ++ } ++ ++ if (kind <= 0) { /* Identification failed */ ++ printk("adm1025.o: Unsupported chip.\n"); ++ goto ERROR1; ++ } ++ ++ if (kind == adm1025) { ++ type_name = "adm1025"; ++ client_name = "ADM1025 chip"; ++ } else if (kind == ne1619) { ++ type_name = "ne1619"; ++ client_name = "NE1619 chip"; ++ } else { ++#ifdef DEBUG ++ printk("adm1025.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = adm1025_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ adm1025_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the ADM1025 chip */ ++ adm1025_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int adm1025_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct adm1025_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("adm1025.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* Called when we have found a new ADM1025. */ ++static void adm1025_init_client(struct i2c_client *client) ++{ ++ struct adm1025_data *data = client->data; ++ u8 reg; ++ int i; ++ ++ data->vrm = 82; ++ ++ /* Set high limits ++ Usually we avoid setting limits on driver init, but it happens ++ that the ADM1025 comes with stupid default limits (all registers ++ set to 0). In case the chip has not gone through any limit ++ setting yet, we better set the high limits to the max so that ++ no alarm triggers. */ ++ for (i=0; i<6; i++) { ++ reg = i2c_smbus_read_byte_data(client, ++ ADM1025_REG_IN_MAX(i)); ++ if (reg == 0) ++ i2c_smbus_write_byte_data(client, ++ ADM1025_REG_IN_MAX(i), ++ 0xFF); ++ } ++ for (i=0; i<2; i++) { ++ reg = i2c_smbus_read_byte_data(client, ++ ADM1025_REG_TEMP_HIGH(i)); ++ if (reg == 0) ++ i2c_smbus_write_byte_data(client, ++ ADM1025_REG_TEMP_HIGH(i), ++ 0x7F); ++ } ++ ++ /* Start monitoring */ ++ reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); ++ i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG, (reg|0x01)&0x7F); ++} ++ ++static void adm1025_update_client(struct i2c_client *client) ++{ ++ struct adm1025_data *data = client->data; ++ u8 nr; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > 2 * HZ) ++ || (jiffies < data->last_updated) || !data->valid) { ++#ifdef DEBUG ++ printk("Starting adm1025 update\n"); ++#endif ++ ++ /* Voltages */ ++ for (nr = 0; nr < 6; nr++) { ++ data->in[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN(nr)); ++ data->in_min[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MIN(nr)); ++ data->in_max[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MAX(nr)); ++ } ++ ++ /* Temperatures */ ++ for (nr = 0; nr < 2; nr++) { ++ data->temp[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP(nr)); ++ data->temp_high[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP_HIGH(nr)); ++ data->temp_low[nr] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP_LOW(nr)); ++ } ++ ++ /* VID */ ++ data->vid = (i2c_smbus_read_byte_data(client, ADM1025_REG_VID) & 0x0f) ++ + ((i2c_smbus_read_byte_data(client, ADM1025_REG_VID4) & 0x01) << 4); ++ ++ /* Alarms */ ++ data->alarms = (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0x3f) ++ + ((i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0x43) << 8); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the data ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void adm1025_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int scales[6] = { 250, 225, 330, 500, 1200, 330 }; ++ ++ struct adm1025_data *data = client->data; ++ int nr = ctl_name - ADM1025_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1025_update_client(client); ++ results[0] = (IN_FROM_REG(data->in_min[nr]) * scales[nr] + 96) / 192; ++ results[1] = (IN_FROM_REG(data->in_max[nr]) * scales[nr] + 96) / 192; ++ results[2] = (IN_FROM_REG(data->in[nr]) * scales[nr] + 96) / 192; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG((results[0] * 192 + scales[nr] / 2) ++ / scales[nr]); ++ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG((results[1] * 192 + scales[nr] / 2) ++ / scales[nr]); ++ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void adm1025_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1025_data *data = client->data; ++ int nr = ctl_name - ADM1025_SYSCTL_RTEMP; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1025_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_high[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_low[nr]); ++ results[2] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_high[nr] = TEMP_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(nr), ++ data->temp_high[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_low[nr] = TEMP_TO_REG(results[1]); ++ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(nr), ++ data->temp_low[nr]); ++ } ++ } ++} ++ ++void adm1025_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1025_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1025_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void adm1025_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1025_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1025_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void adm1025_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1025_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++static int __init sm_adm1025_init(void) ++{ ++ printk("adm1025.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&adm1025_driver); ++} ++ ++static void __exit sm_adm1025_exit(void) ++{ ++ i2c_del_driver(&adm1025_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Chen-Yuan Wu " ++ " and Jean Delvare "); ++MODULE_DESCRIPTION("ADM1025 driver"); ++ ++module_init(sm_adm1025_init); ++module_exit(sm_adm1025_exit); +--- linux-old/drivers/sensors/adm1026.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/adm1026.c Mon Dec 13 20:18:44 2004 +@@ -0,0 +1,1745 @@ ++/* ++ adm1026.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2002, 2003 Philip Pokorny ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ CHANGELOG ++ ++ 2003-03-13 Initial development ++ 2003-05-07 First Release. Includes GPIO fixup and full ++ functionality. ++ 2003-05-18 Minor fixups and tweaks. ++ Print GPIO config after fixup. ++ Adjust fan MIN if DIV changes. ++ 2003-05-21 Fix printing of FAN/GPIO config ++ Fix silly bug in fan_div logic ++ Fix fan_min handling so that 0xff is 0 is 0xff ++ 2003-05-25 Fix more silly typos... ++ 2003-06-11 Change FAN_xx_REG macros to use different scaling ++ Most (all?) drivers assume two pulses per rev fans ++ and the old scaling was producing double the RPM's ++ Thanks to Jerome Hsiao @ Arima for pointing this out. ++ 2004-01-27 Remove use of temporary ID. ++ Define addresses as a range. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++#ifndef I2C_DRIVERID_ADM1026 ++#define I2C_DRIVERID_ADM1026 1048 ++#endif ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(adm1026); ++ ++static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; ++MODULE_PARM(gpio_input,"1-17i"); ++MODULE_PARM_DESC(gpio_input,"List of GPIO pins (0-16) to program as inputs"); ++MODULE_PARM(gpio_output,"1-17i"); ++MODULE_PARM_DESC(gpio_output,"List of GPIO pins (0-16) to program as outputs"); ++MODULE_PARM(gpio_inverted,"1-17i"); ++MODULE_PARM_DESC(gpio_inverted,"List of GPIO pins (0-16) to program as inverted"); ++MODULE_PARM(gpio_normal,"1-17i"); ++MODULE_PARM_DESC(gpio_normal,"List of GPIO pins (0-16) to program as normal/non-inverted"); ++MODULE_PARM(gpio_fan,"1-8i"); ++MODULE_PARM_DESC(gpio_fan,"List of GPIO pins (0-7) to program as fan tachs"); ++ ++/* Many ADM1026 constants specified below */ ++ ++/* The ADM1026 registers */ ++#define ADM1026_REG_CONFIG1 (0x00) ++#define CFG1_MONITOR (0x01) ++#define CFG1_INT_ENABLE (0x02) ++#define CFG1_INT_CLEAR (0x04) ++#define CFG1_AIN8_9 (0x08) ++#define CFG1_THERM_HOT (0x10) ++#define CFG1_DAC_AFC (0x20) ++#define CFG1_PWM_AFC (0x40) ++#define CFG1_RESET (0x80) ++#define ADM1026_REG_CONFIG2 (0x01) ++/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */ ++#define ADM1026_REG_CONFIG3 (0x07) ++#define CFG3_GPIO16_ENABLE (0x01) ++#define CFG3_CI_CLEAR (0x02) ++#define CFG3_VREF_250 (0x04) ++#define CFG3_GPIO16_DIR (0x40) ++#define CFG3_GPIO16_POL (0x80) ++#define ADM1026_REG_E2CONFIG (0x13) ++#define E2CFG_READ (0x01) ++#define E2CFG_WRITE (0x02) ++#define E2CFG_ERASE (0x04) ++#define E2CFG_ROM (0x08) ++#define E2CFG_CLK_EXT (0x80) ++ ++/* There are 10 general analog inputs and 7 dedicated inputs ++ * They are: ++ * 0 - 9 = AIN0 - AIN9 ++ * 10 = Vbat ++ * 11 = 3.3V Standby ++ * 12 = 3.3V Main ++ * 13 = +5V ++ * 14 = Vccp (CPU core voltage) ++ * 15 = +12V ++ * 16 = -12V ++ */ ++static u16 REG_IN[] = { ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, ++ 0x36, 0x37, 0x27, 0x29, 0x26, 0x2a, ++ 0x2b, 0x2c, 0x2d, 0x2e, 0x2f ++ }; ++static u16 REG_IN_MIN[] = { ++ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, ++ 0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a, ++ 0x4b, 0x4c, 0x4d, 0x4e, 0x4f ++ }; ++static u16 REG_IN_MAX[] = { ++ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, ++ 0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42, ++ 0x43, 0x44, 0x45, 0x46, 0x47 ++ }; ++#define ADM1026_REG_IN(nr) (REG_IN[(nr)]) ++#define ADM1026_REG_IN_MIN(nr) (REG_IN_MIN[(nr)]) ++#define ADM1026_REG_IN_MAX(nr) (REG_IN_MAX[(nr)]) ++ ++/* Temperatures are: ++ * 0 - Internal ++ * 1 - External 1 ++ * 2 - External 2 ++ */ ++static u16 REG_TEMP[] = { 0x1f, 0x28, 0x29 }; ++static u16 REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 }; ++static u16 REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 }; ++static u16 REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 }; ++static u16 REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f }; ++static u16 REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f }; ++#define ADM1026_REG_TEMP(nr) (REG_TEMP[(nr)]) ++#define ADM1026_REG_TEMP_MIN(nr) (REG_TEMP_MIN[(nr)]) ++#define ADM1026_REG_TEMP_MAX(nr) (REG_TEMP_MAX[(nr)]) ++#define ADM1026_REG_TEMP_TMIN(nr) (REG_TEMP_TMIN[(nr)]) ++#define ADM1026_REG_TEMP_THERM(nr) (REG_TEMP_THERM[(nr)]) ++#define ADM1026_REG_TEMP_OFFSET(nr) (REG_TEMP_OFFSET[(nr)]) ++ ++#define ADM1026_REG_FAN(nr) (0x38 + (nr)) ++#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr)) ++#define ADM1026_REG_FAN_DIV_0_3 (0x02) ++#define ADM1026_REG_FAN_DIV_4_7 (0x03) ++ ++#define ADM1026_REG_DAC (0x04) ++#define ADM1026_REG_PWM (0x05) ++ ++#define ADM1026_REG_GPIO_CFG_0_3 (0x08) ++#define ADM1026_REG_GPIO_CFG_4_7 (0x09) ++#define ADM1026_REG_GPIO_CFG_8_11 (0x0a) ++#define ADM1026_REG_GPIO_CFG_12_15 (0x0b) ++/* CFG_16 in REG_CFG3 */ ++#define ADM1026_REG_GPIO_STATUS_0_7 (0x24) ++#define ADM1026_REG_GPIO_STATUS_8_15 (0x25) ++/* STATUS_16 in REG_STATUS4 */ ++#define ADM1026_REG_GPIO_MASK_0_7 (0x1c) ++#define ADM1026_REG_GPIO_MASK_8_15 (0x1d) ++/* MASK_16 in REG_MASK4 */ ++ ++#define ADM1026_REG_COMPANY 0x16 ++#define ADM1026_REG_VERSTEP 0x17 ++/* These are the recognized values for the above regs */ ++#define ADM1026_COMPANY_ANALOG_DEV 0x41 ++#define ADM1026_VERSTEP_GENERIC 0x40 ++#define ADM1026_VERSTEP_ADM1026 0x44 ++ ++#define ADM1026_REG_MASK1 0x18 ++#define ADM1026_REG_MASK2 0x19 ++#define ADM1026_REG_MASK3 0x1a ++#define ADM1026_REG_MASK4 0x1b ++ ++#define ADM1026_REG_STATUS1 0x20 ++#define ADM1026_REG_STATUS2 0x21 ++#define ADM1026_REG_STATUS3 0x22 ++#define ADM1026_REG_STATUS4 0x23 ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ */ ++ ++/* IN are scaled acording to built-in resistors. These are the ++ * voltages corresponding to 3/4 of full scale (192 or 0xc0) ++ * NOTE: The -12V input needs an additional factor to account ++ * for the Vref pullup resistor. ++ * NEG12_OFFSET = SCALE * Vref / V-192 - Vref ++ * = 13875 * 2.50 / 1.875 - 2500 ++ * = 16000 ++ */ ++#if 1 ++/* The values in this table are based on Table II, page 15 of the ++ * datasheet. ++ */ ++static int adm1026_scaling[] = { /* .001 Volts */ ++ 2250, 2250, 2250, 2250, 2250, 2250, ++ 1875, 1875, 1875, 1875, 3000, 3330, ++ 3330, 4995, 2250, 12000, 13875 ++ }; ++#define NEG12_OFFSET 16000 ++#else ++/* The values in this table are based on the resistors in ++ * Figure 5 on page 16. But the 3.3V inputs are not in ++ * the figure and the values for the 5V input are wrong. ++ * For 5V, I'm guessing that R2 at 55.2k is right, but ++ * the total resistance should be 1400 or 1449 like the ++ * other inputs. Using 1449, gives 4.922V at 192. ++ */ ++static int adm1026_scaling[] = { /* .001 Volts */ ++ 2249, 2249, 2249, 2249, 2249, 2249, ++ 1875, 1875, 1875, 1875, 3329, 3329, ++ 3329, 4922, 2249, 11969, 13889 ++ }; ++#define NEG12_OFFSET 16019 ++#endif ++ ++#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) ++#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,adm1026_scaling[n],192),0,255)) ++#if 0 /* If we have extended A/D bits */ ++#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,adm1026_scaling[n])) ++#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0)) ++#else ++#define INS_FROM_REG(n,val) (SCALE(val,192,adm1026_scaling[n])) ++#endif ++ ++/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses ++ * and we assume a 2 pulse-per-rev fan tach signal ++ * 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000 ++ */ ++#define FAN_TO_REG(val,div) ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*(div)),1,254)) ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*(div))) ++#define DIV_FROM_REG(val) (1<<(val)) ++#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0) ++ ++/* Temperature is reported in 1 degC increments */ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(val,-127,127)) ++#define TEMP_FROM_REG(val) (val) ++#define OFFSET_TO_REG(val) (SENSORS_LIMIT(val,-127,127)) ++#define OFFSET_FROM_REG(val) (val) ++ ++#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) ++#define PWM_FROM_REG(val) (val) ++ ++/* Analog output is a voltage, but it's used like a PWM ++ * Seems like this should be scaled, but to be consistent ++ * with other drivers, we do it this way. ++ */ ++#define DAC_TO_REG(val) (SENSORS_LIMIT(val,0,255)) ++#define DAC_FROM_REG(val) (val) ++ ++/* sensors_vid.h defines vid_from_reg() */ ++#define VID_FROM_REG(val,vrm) (vid_from_reg(val,vrm)) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++/* Unlike some other drivers we DO NOT set initial limits. Use ++ * the config file to set limits. ++ */ ++ ++/* Typically used with systems using a v9.1 VRM spec ? */ ++#define ADM1026_INIT_VRM 91 ++#define ADM1026_INIT_VID -1 ++ ++/* Chip sampling rates ++ * ++ * Some sensors are not updated more frequently than once per second ++ * so it doesn't make sense to read them more often than that. ++ * We cache the results and return the saved data if the driver ++ * is called again before a second has elapsed. ++ * ++ * Also, there is significant configuration data for this chip ++ * So, we keep the config data up to date in the cache ++ * when it is written and only sample it once every 5 *minutes* ++ */ ++#define ADM1026_DATA_INTERVAL (1 * HZ) ++#define ADM1026_CONFIG_INTERVAL (5 * 60 * HZ) ++ ++/* We allow for multiple chips in a single system. ++ * ++ * For each registered ADM1026, we need to keep state information ++ * at client->data. The adm1026_data structure is dynamically ++ * allocated, when a new client structure is allocated. */ ++ ++struct adm1026_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ int valid; /* !=0 if following fields are valid */ ++ unsigned long last_reading; /* In jiffies */ ++ unsigned long last_config; /* In jiffies */ ++ ++ u8 in[17]; /* Register value */ ++ u8 in_max[17]; /* Register value */ ++ u8 in_min[17]; /* Register value */ ++ s8 temp[3]; /* Register value */ ++ s8 temp_min[3]; /* Register value */ ++ s8 temp_max[3]; /* Register value */ ++ s8 temp_tmin[3]; /* Register value */ ++ s8 temp_therm[3]; /* Register value */ ++ s8 temp_offset[3]; /* Register value */ ++ u8 fan[8]; /* Register value */ ++ u8 fan_min[8]; /* Register value */ ++ u8 fan_div[8]; /* Decoded value */ ++ u8 pwm; /* Register value */ ++ u8 analog_out; /* Register value */ ++ int vid; /* Decoded value */ ++ u8 vrm; /* VRM version */ ++ long alarms; /* Register encoding, combined */ ++ long alarm_mask; /* Register encoding, combined */ ++ long gpio; /* Register encoding, combined */ ++ long gpio_mask; /* Register encoding, combined */ ++ u8 gpio_config[17]; /* Decoded value */ ++ u8 config1; /* Register value */ ++ u8 config2; /* Register value */ ++ u8 config3; /* Register value */ ++}; ++ ++static int adm1026_attach_adapter(struct i2c_adapter *adapter); ++static int adm1026_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int adm1026_detach_client(struct i2c_client *client); ++ ++static int adm1026_read_value(struct i2c_client *client, u8 register); ++static int adm1026_write_value(struct i2c_client *client, u8 register, int value); ++static void adm1026_print_gpio(struct i2c_client *client); ++static void adm1026_fixup_gpio(struct i2c_client *client); ++static void adm1026_update_client(struct i2c_client *client); ++static void adm1026_init_client(struct i2c_client *client); ++ ++ ++static void adm1026_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void adm1026_in16(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void adm1026_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_fixup_fan_min(struct i2c_client *client, ++ int fan, int old_div); ++static void adm1026_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_temp_offset(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_temp_tmin(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_temp_therm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_alarm_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_gpio(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_gpio_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1026_afc(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static struct i2c_driver adm1026_driver = { ++ .owner = THIS_MODULE, ++ .name = "ADM1026 compatible sensor driver", ++ .id = I2C_DRIVERID_ADM1026, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = &adm1026_attach_adapter, ++ .detach_client = &adm1026_detach_client, ++}; ++ ++/* Unique ID assigned to each ADM1026 detected */ ++static int adm1026_id = 0; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define ADM1026_SYSCTL_FAN0 1000 ++#define ADM1026_SYSCTL_FAN1 1001 ++#define ADM1026_SYSCTL_FAN2 1002 ++#define ADM1026_SYSCTL_FAN3 1003 ++#define ADM1026_SYSCTL_FAN4 1004 ++#define ADM1026_SYSCTL_FAN5 1005 ++#define ADM1026_SYSCTL_FAN6 1006 ++#define ADM1026_SYSCTL_FAN7 1007 ++#define ADM1026_SYSCTL_FAN_DIV 1008 ++#define ADM1026_SYSCTL_GPIO 1009 ++#define ADM1026_SYSCTL_GPIO_MASK 1010 ++#define ADM1026_SYSCTL_ALARMS 1011 ++#define ADM1026_SYSCTL_ALARM_MASK 1012 ++#define ADM1026_SYSCTL_IN0 1013 ++#define ADM1026_SYSCTL_IN1 1014 ++#define ADM1026_SYSCTL_IN2 1015 ++#define ADM1026_SYSCTL_IN3 1016 ++#define ADM1026_SYSCTL_IN4 1017 ++#define ADM1026_SYSCTL_IN5 1018 ++#define ADM1026_SYSCTL_IN6 1019 ++#define ADM1026_SYSCTL_IN7 1020 ++#define ADM1026_SYSCTL_IN8 1021 ++#define ADM1026_SYSCTL_IN9 1022 ++#define ADM1026_SYSCTL_IN10 1023 ++#define ADM1026_SYSCTL_IN11 1024 ++#define ADM1026_SYSCTL_IN12 1025 ++#define ADM1026_SYSCTL_IN13 1026 ++#define ADM1026_SYSCTL_IN14 1027 ++#define ADM1026_SYSCTL_IN15 1028 ++#define ADM1026_SYSCTL_IN16 1029 ++#define ADM1026_SYSCTL_PWM 1030 ++#define ADM1026_SYSCTL_ANALOG_OUT 1031 ++#define ADM1026_SYSCTL_AFC 1032 ++#define ADM1026_SYSCTL_TEMP1 1033 ++#define ADM1026_SYSCTL_TEMP2 1034 ++#define ADM1026_SYSCTL_TEMP3 1035 ++#define ADM1026_SYSCTL_TEMP_OFFSET1 1036 ++#define ADM1026_SYSCTL_TEMP_OFFSET2 1037 ++#define ADM1026_SYSCTL_TEMP_OFFSET3 1038 ++#define ADM1026_SYSCTL_TEMP_THERM1 1039 ++#define ADM1026_SYSCTL_TEMP_THERM2 1040 ++#define ADM1026_SYSCTL_TEMP_THERM3 1041 ++#define ADM1026_SYSCTL_TEMP_TMIN1 1042 ++#define ADM1026_SYSCTL_TEMP_TMIN2 1043 ++#define ADM1026_SYSCTL_TEMP_TMIN3 1044 ++#define ADM1026_SYSCTL_VID 1045 ++#define ADM1026_SYSCTL_VRM 1046 ++ ++#define ADM1026_ALARM_TEMP2 (1L << 0) ++#define ADM1026_ALARM_TEMP3 (1L << 1) ++#define ADM1026_ALARM_IN9 (1L << 1) ++#define ADM1026_ALARM_IN11 (1L << 2) ++#define ADM1026_ALARM_IN12 (1L << 3) ++#define ADM1026_ALARM_IN13 (1L << 4) ++#define ADM1026_ALARM_IN14 (1L << 5) ++#define ADM1026_ALARM_IN15 (1L << 6) ++#define ADM1026_ALARM_IN16 (1L << 7) ++#define ADM1026_ALARM_IN0 (1L << 8) ++#define ADM1026_ALARM_IN1 (1L << 9) ++#define ADM1026_ALARM_IN2 (1L << 10) ++#define ADM1026_ALARM_IN3 (1L << 11) ++#define ADM1026_ALARM_IN4 (1L << 12) ++#define ADM1026_ALARM_IN5 (1L << 13) ++#define ADM1026_ALARM_IN6 (1L << 14) ++#define ADM1026_ALARM_IN7 (1L << 15) ++#define ADM1026_ALARM_FAN0 (1L << 16) ++#define ADM1026_ALARM_FAN1 (1L << 17) ++#define ADM1026_ALARM_FAN2 (1L << 18) ++#define ADM1026_ALARM_FAN3 (1L << 19) ++#define ADM1026_ALARM_FAN4 (1L << 20) ++#define ADM1026_ALARM_FAN5 (1L << 21) ++#define ADM1026_ALARM_FAN6 (1L << 22) ++#define ADM1026_ALARM_FAN7 (1L << 23) ++#define ADM1026_ALARM_TEMP1 (1L << 24) ++#define ADM1026_ALARM_IN10 (1L << 25) ++#define ADM1026_ALARM_IN8 (1L << 26) ++#define ADM1026_ALARM_THERM (1L << 27) ++#define ADM1026_ALARM_AFC_FAN (1L << 28) ++#define ADM1026_ALARM_UNUSED (1L << 29) ++#define ADM1026_ALARM_CI (1L << 30) ++/* -- SENSORS SYSCTL END -- */ ++ ++/* The /proc/sys entries */ ++/* These files are created for each detected ADM1026. This is just a template; ++ * The actual list is built from this and additional per-chip ++ * custom lists below. Note the XXX_LEN macros. These must be ++ * compile time constants because they will be used to allocate ++ * space for the final template passed to i2c_register_entry. ++ * We depend on the ability of GCC to evaluate expressions at ++ * compile time to turn these expressions into compile time ++ * constants, but this can generate a warning. ++ */ ++static ctl_table adm1026_common[] = { ++ {ADM1026_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN10, "in10", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN11, "in11", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN12, "in12", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN13, "in13", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN14, "in14", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN15, "in15", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in}, ++ {ADM1026_SYSCTL_IN16, "in16", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_in16}, ++ ++ {ADM1026_SYSCTL_FAN0, "fan0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN6, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN7, "fan7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_fan}, ++ {ADM1026_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_fan_div}, ++ ++ {ADM1026_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_temp}, ++ {ADM1026_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_temp}, ++ {ADM1026_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_temp}, ++ {ADM1026_SYSCTL_TEMP_OFFSET1, "temp1_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset}, ++ {ADM1026_SYSCTL_TEMP_OFFSET2, "temp2_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset}, ++ {ADM1026_SYSCTL_TEMP_OFFSET3, "temp3_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_offset}, ++ {ADM1026_SYSCTL_TEMP_TMIN1, "temp1_tmin", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin}, ++ {ADM1026_SYSCTL_TEMP_TMIN2, "temp2_tmin", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin}, ++ {ADM1026_SYSCTL_TEMP_TMIN3, "temp3_tmin", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_tmin}, ++ {ADM1026_SYSCTL_TEMP_THERM1, "temp1_therm", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm}, ++ {ADM1026_SYSCTL_TEMP_THERM2, "temp2_therm", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm}, ++ {ADM1026_SYSCTL_TEMP_THERM3, "temp3_therm", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_temp_therm}, ++ ++ {ADM1026_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_vid}, ++ {ADM1026_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_vrm}, ++ ++ {ADM1026_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_alarms}, ++ {ADM1026_SYSCTL_ALARM_MASK, "alarm_mask", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_alarm_mask}, ++ ++ {ADM1026_SYSCTL_GPIO, "gpio", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_gpio}, ++ {ADM1026_SYSCTL_GPIO_MASK, "gpio_mask", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_gpio_mask}, ++ ++ {ADM1026_SYSCTL_PWM, "pwm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_pwm}, ++ {ADM1026_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1026_analog_out}, ++ {ADM1026_SYSCTL_AFC, "afc", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm1026_afc}, ++ ++ {0} ++}; ++#define CTLTBL_COMMON (sizeof(adm1026_common)/sizeof(adm1026_common[0])) ++ ++#define MAX2(a,b) ((a)>(b)?(a):(b)) ++#define MAX3(a,b,c) ((a)>(b)?MAX2((a),(c)):MAX2((b),(c))) ++#define MAX4(a,b,c,d) ((a)>(b)?MAX3((a),(c),(d)):MAX3((b),(c),(d))) ++ ++#define CTLTBL_MAX (CTLTBL_COMMON) ++ ++/* This function is called when: ++ * the module is loaded ++ * a new adapter is loaded ++ */ ++int adm1026_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, adm1026_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int adm1026_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ int company, verstep ; ++ struct i2c_client *new_client; ++ struct adm1026_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ struct ctl_table template[CTLTBL_MAX] ; ++ struct ctl_table * template_next = template ; ++ ++ if (i2c_is_isa_adapter(adapter)) { ++ /* This chip has no ISA interface */ ++ goto ERROR0 ; ++ } ++ ++ if (!i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) { ++ /* We need to be able to do byte I/O */ ++ goto ERROR0 ; ++ } ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access adm1026_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct adm1026_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &adm1026_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ company = adm1026_read_value(new_client, ADM1026_REG_COMPANY); ++ verstep = adm1026_read_value(new_client, ADM1026_REG_VERSTEP); ++ ++#ifdef DEBUG ++ printk("adm1026: Detecting device at %d,0x%02x with" ++ " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", ++ i2c_adapter_id(new_client->adapter), new_client->addr, ++ company, verstep ++ ); ++#endif ++ ++ /* If auto-detecting, Determine the chip type. */ ++ if (kind <= 0) { ++#ifdef DEBUG ++ printk("adm1026: Autodetecting device at %d,0x%02x ...\n", ++ i2c_adapter_id(adapter), address ); ++#endif ++ if( company == ADM1026_COMPANY_ANALOG_DEV ++ && verstep == ADM1026_VERSTEP_ADM1026 ) { ++ kind = adm1026 ; ++ } else if( company == ADM1026_COMPANY_ANALOG_DEV ++ && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC ) { ++ printk("adm1026: Unrecgonized stepping 0x%02x" ++ " Defaulting to ADM1026.\n", verstep ); ++ kind = adm1026 ; ++ } else if( (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC ) { ++ printk("adm1026: Found version/stepping 0x%02x" ++ " Assuming generic ADM1026.\n", verstep ); ++ kind = any_chip ; ++ } else { ++#ifdef DEBUG ++ printk("adm1026: Autodetection failed\n"); ++#endif ++ /* Not an ADM1026 ... */ ++ if( kind == 0 ) { /* User used force=x,y */ ++ printk("adm1026: Generic ADM1026 Version 6 not" ++ " found at %d,0x%02x. Try force_adm1026.\n", ++ i2c_adapter_id(adapter), address ); ++ } ++ err = 0 ; ++ goto ERROR1; ++ } ++ } ++ ++ /* Fill in the chip specific driver values */ ++ switch (kind) { ++ case any_chip : ++ type_name = "adm1026"; ++ strcpy(new_client->name, "Generic ADM1026"); ++ template_next = template ; /* None used */ ++ break ; ++ case adm1026 : ++ type_name = "adm1026"; ++ strcpy(new_client->name, "Analog Devices ADM1026"); ++ template_next = template ; ++ break ; ++#if 0 ++ /* Example of another adm1026 "compatible" device */ ++ case adx1000 : ++ type_name = "adx1000"; ++ strcpy(new_client->name, "Compatible ADX1000"); ++ memcpy( template, adx_specific, sizeof(adx_specific) ); ++ template_next = template + CTLTBL_ADX1000 ; ++ break ; ++#endif ++ default : ++ printk("adm1026: Internal error, invalid kind (%d)!", kind); ++ err = -EFAULT ; ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields */ ++ new_client->id = adm1026_id++; ++ printk("adm1026(%d): Assigning ID %d to %s at %d,0x%02x\n", ++ new_client->id, new_client->id, new_client->name, ++ i2c_adapter_id(new_client->adapter), ++ new_client->addr ++ ); ++ ++ /* Housekeeping values */ ++ data->type = kind; ++ data->valid = 0; ++ ++ /* Set the VRM version */ ++ data->vrm = ADM1026_INIT_VRM ; ++ data->vid = ADM1026_INIT_VID ; ++ ++ init_MUTEX(&data->update_lock); ++ ++ /* Initialize the ADM1026 chip */ ++ adm1026_init_client(new_client); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* Finish out the template */ ++ memcpy(template_next, adm1026_common, sizeof(adm1026_common)); ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ template)) < 0) { ++ err = i; ++ goto ERROR2; ++ } ++ data->sysctl_id = i; ++ ++ return 0; ++ ++ /* Error out and cleanup code */ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++int adm1026_detach_client(struct i2c_client *client) ++{ ++ int err; ++ int id ; ++ ++ id = client->id; ++ i2c_deregister_entry(((struct adm1026_data *)(client->data))->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk("adm1026(%d): Client deregistration failed," ++ " client not detached.\n", id ); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++int adm1026_read_value(struct i2c_client *client, u8 reg) ++{ ++ int res; ++ ++ if( reg < 0x80 ) { ++ /* "RAM" locations */ ++ res = i2c_smbus_read_byte_data(client, reg) & 0xff ; ++ } else { ++ /* EEPROM, do nothing */ ++ res = 0 ; ++ } ++ ++ return res ; ++} ++ ++int adm1026_write_value(struct i2c_client *client, u8 reg, int value) ++{ ++ int res ; ++ ++ if( reg < 0x80 ) { ++ /* "RAM" locations */ ++ res = i2c_smbus_write_byte_data(client, reg, value); ++ } else { ++ /* EEPROM, do nothing */ ++ res = 0 ; ++ } ++ ++ return res ; ++} ++ ++/* Called when we have found a new ADM1026. */ ++void adm1026_init_client(struct i2c_client *client) ++{ ++ int value ; ++ int i; ++ struct adm1026_data *data = client->data; ++ ++#ifdef DEBUG ++ printk("adm1026(%d): Initializing device\n", client->id); ++#endif ++ ++ /* Read chip config */ ++ data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1); ++ data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2); ++ data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3); ++ ++ /* Inform user of chip config */ ++#ifdef DEBUG ++ printk("adm1026(%d): ADM1026_REG_CONFIG1 is: 0x%02x\n", ++ client->id, data->config1 ); ++#endif ++ if( (data->config1 & CFG1_MONITOR) == 0 ) { ++ printk("adm1026(%d): Monitoring not currently enabled.\n", ++ client->id ); ++ } ++ if( data->config1 & CFG1_INT_ENABLE ) { ++ printk("adm1026(%d): SMBALERT interrupts are enabled.\n", ++ client->id ); ++ } ++ if( data->config1 & CFG1_AIN8_9 ) { ++ printk("adm1026(%d): in8 and in9 enabled. temp3 disabled.\n", ++ client->id ); ++ } else { ++ printk("adm1026(%d): temp3 enabled. in8 and in9 disabled.\n", ++ client->id ); ++ } ++ if( data->config1 & CFG1_THERM_HOT ) { ++ printk("adm1026(%d): Automatic THERM, PWM, and temp limits enabled.\n", ++ client->id ); ++ } ++ ++ value = data->config3 ; ++ if( data->config3 & CFG3_GPIO16_ENABLE ) { ++ printk("adm1026(%d): GPIO16 enabled. THERM pin disabled.\n", ++ client->id ); ++ } else { ++ printk("adm1026(%d): THERM pin enabled. GPIO16 disabled.\n", ++ client->id ); ++ } ++ if( data->config3 & CFG3_VREF_250 ) { ++ printk("adm1026(%d): Vref is 2.50 Volts.\n", client->id ); ++ } else { ++ printk("adm1026(%d): Vref is 1.82 Volts.\n", client->id ); ++ } ++ ++ /* Read and pick apart the existing GPIO configuration */ ++ value = 0 ; ++ for( i = 0 ; i <= 15 ; ++i ) { ++ if( (i & 0x03) == 0 ) { ++ value = adm1026_read_value(client, ++ ADM1026_REG_GPIO_CFG_0_3 + i/4 ); ++ } ++ data->gpio_config[i] = value & 0x03 ; ++ value >>= 2 ; ++ } ++ data->gpio_config[16] = (data->config3 >> 6) & 0x03 ; ++ ++ /* ... and then print it */ ++ adm1026_print_gpio(client); ++ ++ /* If the user asks us to reprogram the GPIO config, then ++ * do it now. But only if this is the first ADM1026. ++ */ ++ if( client->id == 0 ++ && (gpio_input[0] != -1 || gpio_output[0] != -1 ++ || gpio_inverted[0] != -1 || gpio_normal[0] != -1 ++ || gpio_fan[0] != -1 ) ) { ++ adm1026_fixup_gpio(client); ++ } ++ ++ /* WE INTENTIONALLY make no changes to the limits, ++ * offsets, pwms and fans. If they were ++ * configured, we don't want to mess with them. ++ * If they weren't, the default is generally safe ++ * and will suffice until 'sensors -s' can be run. ++ */ ++ ++ /* Start monitoring */ ++ value = adm1026_read_value(client, ADM1026_REG_CONFIG1); ++ ++ /* Set MONITOR, clear interrupt acknowledge and s/w reset */ ++ value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET) ; ++#ifdef DEBUG ++ printk("adm1026(%d): Setting CONFIG to: 0x%02x\n", client->id, value ); ++#endif ++ data->config1 = value ; ++ adm1026_write_value(client, ADM1026_REG_CONFIG1, value); ++ ++} ++ ++void adm1026_print_gpio(struct i2c_client *client) ++{ ++ struct adm1026_data *data = client->data; ++ int i ; ++ ++ printk("adm1026(%d): GPIO config is:\nadm1026(%d):", ++ client->id, client->id ); ++ for( i = 0 ; i <= 7 ; ++i ) { ++ if( data->config2 & (1 << i) ) { ++ printk( " %sGP%s%d", ++ data->gpio_config[i] & 0x02 ? "" : "!", ++ data->gpio_config[i] & 0x01 ? "OUT" : "IN", ++ i ); ++ } else { ++ printk( " FAN%d", i ); ++ } ++ } ++ printk( "\nadm1026(%d):", client->id ); ++ for( i = 8 ; i <= 15 ; ++i ) { ++ printk( " %sGP%s%d", ++ data->gpio_config[i] & 0x02 ? "" : "!", ++ data->gpio_config[i] & 0x01 ? "OUT" : "IN", ++ i ); ++ } ++ if( data->config3 & CFG3_GPIO16_ENABLE ) { ++ printk( " %sGP%s16\n", ++ data->gpio_config[16] & 0x02 ? "" : "!", ++ data->gpio_config[16] & 0x01 ? "OUT" : "IN" ); ++ } else { ++ /* GPIO16 is THERM */ ++ printk( " THERM\n" ); ++ } ++} ++ ++void adm1026_fixup_gpio(struct i2c_client *client) ++{ ++ struct adm1026_data *data = client->data; ++ int i ; ++ int value ; ++ ++ /* Make the changes requested. */ ++ /* We may need to unlock/stop monitoring or soft-reset the ++ * chip before we can make changes. This hasn't been ++ * tested much. FIXME ++ */ ++ ++ /* Make outputs */ ++ for( i = 0 ; i <= 16 ; ++i ) { ++ if( gpio_output[i] >= 0 && gpio_output[i] <= 16 ) { ++ data->gpio_config[gpio_output[i]] |= 0x01 ; ++ } ++ /* if GPIO0-7 is output, it isn't a FAN tach */ ++ if( gpio_output[i] >= 0 && gpio_output[i] <= 7 ) { ++ data->config2 |= 1 << gpio_output[i] ; ++ } ++ } ++ ++ /* Input overrides output */ ++ for( i = 0 ; i <= 16 ; ++i ) { ++ if( gpio_input[i] >= 0 && gpio_input[i] <= 16 ) { ++ data->gpio_config[gpio_input[i]] &= ~ 0x01 ; ++ } ++ /* if GPIO0-7 is input, it isn't a FAN tach */ ++ if( gpio_input[i] >= 0 && gpio_input[i] <= 7 ) { ++ data->config2 |= 1 << gpio_input[i] ; ++ } ++ } ++ ++ /* Inverted */ ++ for( i = 0 ; i <= 16 ; ++i ) { ++ if( gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16 ) { ++ data->gpio_config[gpio_inverted[i]] &= ~ 0x02 ; ++ } ++ } ++ ++ /* Normal overrides inverted */ ++ for( i = 0 ; i <= 16 ; ++i ) { ++ if( gpio_normal[i] >= 0 && gpio_normal[i] <= 16 ) { ++ data->gpio_config[gpio_normal[i]] |= 0x02 ; ++ } ++ } ++ ++ /* Fan overrides input and output */ ++ for( i = 0 ; i <= 7 ; ++i ) { ++ if( gpio_fan[i] >= 0 && gpio_fan[i] <= 7 ) { ++ data->config2 &= ~( 1 << gpio_fan[i] ); ++ } ++ } ++ ++ /* Write new configs to registers */ ++ adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2); ++ data->config3 = (data->config3 & 0x3f) ++ | ((data->gpio_config[16] & 0x03) << 6) ; ++ adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3); ++ for( i = 15, value = 0 ; i >= 0 ; --i ) { ++ value <<= 2 ; ++ value |= data->gpio_config[i] & 0x03 ; ++ if( (i & 0x03) == 0 ) { ++ adm1026_write_value(client, ++ ADM1026_REG_GPIO_CFG_0_3 + i/4, ++ value ); ++ value = 0 ; ++ } ++ } ++ ++ /* Print the new config */ ++ adm1026_print_gpio(client); ++} ++ ++void adm1026_update_client(struct i2c_client *client) ++{ ++ struct adm1026_data *data = client->data; ++ int i; ++ long value, alarms, gpio ; ++ ++ down(&data->update_lock); ++ ++ if (!data->valid ++ || (jiffies - data->last_reading > ADM1026_DATA_INTERVAL )) { ++ /* Things that change quickly */ ++ ++#ifdef DEBUG ++ printk("adm1026(%d): Reading sensor values\n", client->id); ++#endif ++ for (i = 0 ; i <= 16 ; ++i) { ++ data->in[i] = ++ adm1026_read_value(client, ADM1026_REG_IN(i)); ++ } ++ ++ for (i = 0 ; i <= 7 ; ++i) { ++ data->fan[i] = ++ adm1026_read_value(client, ADM1026_REG_FAN(i)); ++ } ++ ++ for (i = 0 ; i <= 2 ; ++i) { ++ /* NOTE: temp[] is s8 and we assume 2's complement ++ * "conversion" in the assignment */ ++ data->temp[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP(i)); ++ } ++ ++ data->pwm = adm1026_read_value(client, ADM1026_REG_PWM); ++ data->analog_out = adm1026_read_value(client, ADM1026_REG_DAC); ++ ++ /* GPIO16 is MSbit of alarms, move it to gpio */ ++ alarms = adm1026_read_value(client, ADM1026_REG_STATUS4); ++ gpio = alarms & 0x80 ? 0x0100 : 0 ; /* GPIO16 */ ++ alarms &= 0x7f ; ++ alarms <<= 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3); ++ alarms <<= 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2); ++ alarms <<= 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1); ++ data->alarms = alarms ; ++ ++ /* Read the GPIO values */ ++ gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_STATUS_8_15); ++ gpio <<= 8 ; ++ gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_STATUS_0_7); ++ data->gpio = gpio ; ++ ++ data->last_reading = jiffies ; ++ }; /* last_reading */ ++ ++ if (!data->valid ++ || (jiffies - data->last_config > ADM1026_CONFIG_INTERVAL) ) { ++ /* Things that don't change often */ ++ ++#ifdef DEBUG ++ printk("adm1026(%d): Reading config values\n", client->id); ++#endif ++ for (i = 0 ; i <= 16 ; ++i) { ++ data->in_min[i] = ++ adm1026_read_value(client, ADM1026_REG_IN_MIN(i)); ++ data->in_max[i] = ++ adm1026_read_value(client, ADM1026_REG_IN_MAX(i)); ++ } ++ ++ value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) ++ | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8); ++ for (i = 0 ; i <= 7 ; ++i) { ++ data->fan_min[i] = ++ adm1026_read_value(client, ADM1026_REG_FAN_MIN(i)); ++ data->fan_div[i] = DIV_FROM_REG(value & 0x03); ++ value >>= 2 ; ++ } ++ ++ for (i = 0; i <= 2; ++i) { ++ /* NOTE: temp_xxx[] are s8 and we assume 2's complement ++ * "conversion" in the assignment */ ++ data->temp_min[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP_MIN(i)); ++ data->temp_max[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP_MAX(i)); ++ data->temp_tmin[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP_TMIN(i)); ++ data->temp_therm[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP_THERM(i)); ++ data->temp_offset[i] = ++ adm1026_read_value(client, ADM1026_REG_TEMP_OFFSET(i)); ++ } ++ ++ /* Read the STATUS/alarm masks */ ++ alarms = adm1026_read_value(client, ADM1026_REG_MASK4); ++ gpio = alarms & 0x80 ? 0x0100 : 0 ; /* GPIO16 */ ++ alarms = (alarms & 0x7f) << 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_MASK3); ++ alarms <<= 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_MASK2); ++ alarms <<= 8 ; ++ alarms |= adm1026_read_value(client, ADM1026_REG_MASK1); ++ data->alarm_mask = alarms ; ++ ++ /* Read the GPIO values */ ++ gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_8_15); ++ gpio <<= 8 ; ++ gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7); ++ data->gpio_mask = gpio ; ++ ++ /* Read the GPIO config */ ++ data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2); ++ data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3); ++ data->gpio_config[16] = (data->config3 >> 6) & 0x03 ; ++ ++ value = 0 ; ++ for( i = 0 ; i <= 15 ; ++i ) { ++ if( (i & 0x03) == 0 ) { ++ value = adm1026_read_value(client, ++ ADM1026_REG_GPIO_CFG_0_3 + i/4 ); ++ } ++ data->gpio_config[i] = value & 0x03 ; ++ value >>= 2 ; ++ } ++ ++ data->last_config = jiffies; ++ }; /* last_config */ ++ ++ /* We don't know where or even _if_ the VID might be on the GPIO ++ * pins. But the datasheet gives an example config showing ++ * GPIO11-15 being used to monitor VID0-4, so we go with that ++ * but make the vid WRITEABLE so if it's wrong, the user can ++ * set it in /etc/sensors.conf perhaps using an expression or ++ * 0 to trigger a re-read from the GPIO pins. ++ */ ++ if( data->vid == ADM1026_INIT_VID ) { ++ /* Hasn't been set yet, make a bold assumption */ ++ printk("adm1026(%d): Setting VID from GPIO11-15.\n", ++ client->id ); ++ data->vid = (data->gpio >> 11) & 0x1f ; ++ } ++ ++ data->valid = 1; ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The following functions are the call-back functions of the /proc/sys and ++ sysctl files. The appropriate function is referenced in the ctl_table ++ extra1 field. ++ ++ Each function must return the magnitude (power of 10 to divide the ++ data with) if it is called with operation set to SENSORS_PROC_REAL_INFO. ++ It must put a maximum of *nrels elements in results reflecting the ++ data of this file, and set *nrels to the number it actually put in ++ it, if operation is SENSORS_PROC_REAL_READ. Finally, it must get ++ up to *nrels elements from results and write them to the chip, if ++ operations is SENSORS_PROC_REAL_WRITE. ++ */ ++void adm1026_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_IN0; ++ ++ /* We handle in0 - in15 here. in16 (-12V) is handled below */ ++ if (nr < 0 || nr > 15) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; /* 1.000 */ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = INS_FROM_REG(nr,data->in_min[nr]); ++ results[1] = INS_FROM_REG(nr,data->in_max[nr]); ++ results[2] = INS_FROM_REG(nr,data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->in_max[nr] = INS_TO_REG(nr,results[1]); ++ adm1026_write_value(client, ADM1026_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->in_min[nr] = INS_TO_REG(nr,results[0]); ++ adm1026_write_value(client, ADM1026_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_in16(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_IN0; ++ ++ /* We handle in16 (-12V) here */ ++ if (nr != 16) ++ return ; /* ERROR */ ++ ++ /* Apply offset and swap min/max so that min is 90% of ++ * target and max is 110% of target. ++ */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; /* 1.000 */ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = INS_FROM_REG(nr,data->in_max[nr])-NEG12_OFFSET ; ++ results[1] = INS_FROM_REG(nr,data->in_min[nr])-NEG12_OFFSET ; ++ results[2] = INS_FROM_REG(nr,data->in[nr])-NEG12_OFFSET ; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->in_min[nr] = INS_TO_REG(nr,results[1]+NEG12_OFFSET); ++ adm1026_write_value(client, ADM1026_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->in_max[nr] = INS_TO_REG(nr,results[0]+NEG12_OFFSET); ++ adm1026_write_value(client, ADM1026_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_FAN0 ; ++ ++ if (nr < 0 || nr > 7) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr], data->fan_div[nr]); ++ results[1] = FAN_FROM_REG(data->fan[nr], data->fan_div[nr]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->fan_min[nr] = FAN_TO_REG(results[0], ++ data->fan_div[nr]); ++ adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr), ++ data->fan_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++/* Adjust fan_min to account for new fan divisor */ ++void adm1026_fixup_fan_min(struct i2c_client *client, int fan, int old_div) ++{ ++ struct adm1026_data *data = client->data; ++ int new_div = data->fan_div[fan] ; ++ int new_min; ++ ++ /* 0 and 0xff are special. Don't adjust them */ ++ if( data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff ) { ++ return ; ++ } ++ ++ new_min = data->fan_min[fan] * old_div / new_div ; ++ new_min = SENSORS_LIMIT(new_min, 1, 254); ++ data->fan_min[fan] = new_min ; ++ adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min); ++} ++ ++void adm1026_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int i ; ++ int value, div, old ; ++ ++ if (ctl_name != ADM1026_SYSCTL_FAN_DIV) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ for( i = 0 ; i <= 7 ; ++i ) { ++ results[i] = data->fan_div[i] ; ++ } ++ *nrels_mag = 8; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ value = 0 ; ++ for( i = 7 ; i >= 0 ; --i ) { ++ value <<= 2 ; ++ if (*nrels_mag > i) { ++ old = data->fan_div[i] ; ++ div = DIV_TO_REG(results[i]) ; ++ data->fan_div[i] = DIV_FROM_REG(div) ; ++ if( data->fan_div[i] != old ) { ++ adm1026_fixup_fan_min(client,i,old); ++ } ++ } else { ++ div = DIV_TO_REG(data->fan_div[i]) ; ++ } ++ value |= div ; ++ } ++ adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3, ++ value & 0xff); ++ adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7, ++ (value >> 8) & 0xff); ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_TEMP1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_min[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_max[nr]); ++ results[2] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->temp_max[nr] = TEMP_TO_REG(results[1]); ++ adm1026_write_value(client, ADM1026_REG_TEMP_MAX(nr), ++ data->temp_max[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->temp_min[nr] = TEMP_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_TEMP_MIN(nr), ++ data->temp_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_temp_offset(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_TEMP_OFFSET1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_offset[nr]); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->temp_offset[nr] = TEMP_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET(nr), ++ data->temp_offset[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_temp_tmin(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_TEMP_TMIN1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_tmin[nr]); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->temp_tmin[nr] = TEMP_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_TEMP_TMIN(nr), ++ data->temp_tmin[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_temp_therm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ int nr = ctl_name - ADM1026_SYSCTL_TEMP_THERM1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_therm[nr]); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->temp_therm[nr] = TEMP_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_TEMP_THERM(nr), ++ data->temp_therm[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if (ctl_name != ADM1026_SYSCTL_PWM) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm); ++ results[1] = 1 ; /* Always enabled */ ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ /* PWM enable is read-only */ ++ if (*nrels_mag > 0) { ++ data->pwm = PWM_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_PWM, ++ data->pwm); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_analog_out(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if (ctl_name != ADM1026_SYSCTL_ANALOG_OUT) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* 0 - 255 */ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = DAC_FROM_REG(data->analog_out); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->analog_out = DAC_TO_REG(results[0]); ++ adm1026_write_value(client, ADM1026_REG_DAC, ++ data->analog_out); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_afc(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if (ctl_name != ADM1026_SYSCTL_AFC) ++ return ; /* ERROR */ ++ ++ /* PWM auto fan control, DAC auto fan control */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = (data->config1 & CFG1_PWM_AFC) != 0 ; ++ results[1] = (data->config1 & CFG1_DAC_AFC) != 0 ; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->config1 = (data->config1 & ~CFG1_DAC_AFC) ++ | (results[1] ? CFG1_DAC_AFC : 0) ; ++ } ++ if (*nrels_mag > 0) { ++ data->config1 = (data->config1 & ~CFG1_PWM_AFC) ++ | (results[0] ? CFG1_PWM_AFC : 0) ; ++ adm1026_write_value(client, ADM1026_REG_CONFIG1, ++ data->config1); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if( ctl_name != ADM1026_SYSCTL_VID ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = VID_FROM_REG((data->vid)&0x3f,data->vrm); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ /* Hmmm... There isn't a VID_TO_REG mapping */ ++ if (*nrels_mag > 0) { ++ if( results[0] >= 0 ) { ++ data->vid = results[0] & 0x3f ; ++ } else { ++ data->vid = ADM1026_INIT_VID ; ++ } ++ } ++ up(&data->update_lock); ++ } ++ ++} ++ ++void adm1026_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if( ctl_name != ADM1026_SYSCTL_VRM ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm ; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag > 0) { ++ data->vrm = results[0] ; ++ } ++ } ++} ++ ++void adm1026_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ ++ if( ctl_name != ADM1026_SYSCTL_ALARMS ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = data->alarms ; ++ *nrels_mag = 1; ++ } ++ /* FIXME: Perhaps we should implement a write function ++ * to clear an alarm? ++ */ ++} ++ ++void adm1026_alarm_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ unsigned long mask ; ++ ++ if( ctl_name != ADM1026_SYSCTL_ALARM_MASK ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = data->alarm_mask ; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->alarm_mask = results[0] & 0x7fffffff ; ++ mask = data->alarm_mask ++ | (data->gpio_mask & 0x10000 ? 0x80000000 : 0) ; ++ adm1026_write_value(client, ADM1026_REG_MASK1, ++ mask & 0xff); ++ mask >>= 8 ; ++ adm1026_write_value(client, ADM1026_REG_MASK2, ++ mask & 0xff); ++ mask >>= 8 ; ++ adm1026_write_value(client, ADM1026_REG_MASK3, ++ mask & 0xff); ++ mask >>= 8 ; ++ adm1026_write_value(client, ADM1026_REG_MASK4, ++ mask & 0xff); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_gpio(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ long gpio ; ++ ++ if( ctl_name != ADM1026_SYSCTL_GPIO ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = data->gpio ; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->gpio = results[0] & 0x1ffff ; ++ gpio = data->gpio ; ++ adm1026_write_value(client, ++ ADM1026_REG_GPIO_STATUS_0_7, ++ gpio & 0xff ); ++ gpio >>= 8 ; ++ adm1026_write_value(client, ++ ADM1026_REG_GPIO_STATUS_8_15, ++ gpio & 0xff ); ++ gpio = ((gpio >> 1) & 0x80) ++ | (data->alarms >> 24 & 0x7f); ++ adm1026_write_value(client, ++ ADM1026_REG_STATUS4, ++ gpio & 0xff ); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1026_gpio_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm1026_data *data = client->data; ++ long mask ; ++ ++ if( ctl_name != ADM1026_SYSCTL_GPIO_MASK ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm1026_update_client(client); ++ results[0] = data->gpio_mask ; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->gpio_mask = results[0] & 0x1ffff ; ++ mask = data->gpio_mask ; ++ adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7, ++ mask & 0xff); ++ mask >>= 8 ; ++ adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15, ++ mask & 0xff); ++ mask = ((mask >> 1) & 0x80) ++ | (data->alarm_mask >> 24 & 0x7f); ++ adm1026_write_value(client, ADM1026_REG_MASK1, ++ mask & 0xff); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++static int __init sm_adm1026_init(void) ++{ ++ printk("adm1026: Version %s (%s)\n", LM_VERSION, LM_DATE); ++ printk("adm1026: See http://www.penguincomputing.com/lm_sensors for more info.\n" ); ++ return i2c_add_driver(&adm1026_driver); ++} ++ ++static void __exit sm_adm1026_exit(void) ++{ ++ i2c_del_driver(&adm1026_driver); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Philip Pokorny ++ and Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports ADM9240, DS1780, and LM81. See doc/chips/adm9240 for details */ ++ ++/* ++ A couple notes about the ADM9240: ++ ++* It claims to be 'LM7x' register compatible. This must be in reference ++ to only the LM78, because it is missing stuff to emulate LM75's as well. ++ (like the Winbond W83781 does) ++ ++* This driver was written from rev. 0 of the PDF, but it seems well ++ written and complete (unlike the W83781 which is horrible and has ++ supposidly gone through a few revisions.. rev 0 of that one must ++ have been in crayon on construction paper...) ++ ++* All analog inputs can range from 0 to 2.5, eventhough some inputs are ++ marked as being 5V, 12V, etc. I don't have any real voltages going ++ into my prototype, so I'm not sure that things are computed right, ++ but at least the limits seem to be working OK. ++ ++* Another curiousity is that the fan_div seems to be read-only. I.e., ++ any written value to it doesn't seem to make any difference. The ++ fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases). ++ ++ ++ --Phil ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_3(adm9240, ds1780, lm81); ++ ++/* Many ADM9240 constants specified below */ ++ ++#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2) ++#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2) ++#define ADM9240_REG_IN(nr) (0x20 + (nr)) ++ ++/* The ADM9240 registers */ ++#define ADM9240_REG_TEST 0x15 ++#define ADM9240_REG_ANALOG_OUT 0x19 ++/* These are all read-only */ ++#define ADM9240_REG_2_5V 0x20 ++#define ADM9240_REG_VCCP1 0x21 ++#define ADM9240_REG_3_3V 0x22 ++#define ADM9240_REG_5V 0x23 ++#define ADM9240_REG_12V 0x24 ++#define ADM9240_REG_VCCP2 0x25 ++#define ADM9240_REG_TEMP 0x27 ++#define ADM9240_REG_FAN1 0x28 ++#define ADM9240_REG_FAN2 0x29 ++#define ADM9240_REG_COMPANY_ID 0x3E /* 0x23 for ADM9240; 0xDA for DS1780 */ ++ /* 0x01 for LM81 */ ++#define ADM9240_REG_DIE_REV 0x3F ++/* These are read/write */ ++#define ADM9240_REG_2_5V_HIGH 0x2B ++#define ADM9240_REG_2_5V_LOW 0x2C ++#define ADM9240_REG_VCCP1_HIGH 0x2D ++#define ADM9240_REG_VCCP1_LOW 0x2E ++#define ADM9240_REG_3_3V_HIGH 0x2F ++#define ADM9240_REG_3_3V_LOW 0x30 ++#define ADM9240_REG_5V_HIGH 0x31 ++#define ADM9240_REG_5V_LOW 0x32 ++#define ADM9240_REG_12V_HIGH 0x33 ++#define ADM9240_REG_12V_LOW 0x34 ++#define ADM9240_REG_VCCP2_HIGH 0x35 ++#define ADM9240_REG_VCCP2_LOW 0x36 ++#define ADM9240_REG_TCRIT_LIMIT 0x37 /* LM81 only - not supported */ ++#define ADM9240_REG_LOW_LIMIT 0x38 /* LM81 only - not supported */ ++#define ADM9240_REG_TOS 0x39 ++#define ADM9240_REG_THYST 0x3A ++#define ADM9240_REG_FAN1_MIN 0x3B ++#define ADM9240_REG_FAN2_MIN 0x3C ++ ++#define ADM9240_REG_CONFIG 0x40 ++#define ADM9240_REG_INT1_STAT 0x41 ++#define ADM9240_REG_INT2_STAT 0x42 ++#define ADM9240_REG_INT1_MASK 0x43 ++#define ADM9240_REG_INT2_MASK 0x44 ++ ++#define ADM9240_REG_COMPAT 0x45 /* dummy compat. register for other drivers? */ ++#define ADM9240_REG_CHASSIS_CLEAR 0x46 ++#define ADM9240_REG_VID_FAN_DIV 0x47 ++#define ADM9240_REG_I2C_ADDR 0x48 ++#define ADM9240_REG_VID4 0x49 ++#define ADM9240_REG_TEMP_CONFIG 0x4B ++#define ADM9240_REG_EXTMODE1 0x4C /* LM81 only - not supported */ ++#define ADM9240_REG_EXTMODE2 0x4D /* LM81 only - not supported */ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val), 0, 255)) ++#define IN_FROM_REG(val,nr) (val) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:\ ++ (val)==255?0:1350000/((div)*(val))) ++ ++#define TEMP_FROM_REG(temp) ((temp)<256 ? (temp) * 5 : \ ++ ((temp) - 512) * 5) ++ ++#define TEMP_LIMIT_FROM_REG(val) (((val)>=0x80?(val)-0x100:(val))*10) ++ ++#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10)+256:\ ++ ((val)+5)/10), \ ++ 0,255) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) ++ ++#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ ++ 205-(val)*5) ++ ++/* For each registered ADM9240, we need to keep some data in memory. */ ++struct adm9240_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[6]; /* Register value */ ++ u8 in_max[6]; /* Register value */ ++ u8 in_min[6]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ int temp; /* Temp, shifted right */ ++ u8 temp_os_max; /* Register value */ ++ u8 temp_os_hyst; /* Register value */ ++ u16 alarms; /* Register encoding, combined */ ++ u8 analog_out; /* Register value */ ++ u8 vid; /* Register value combined */ ++}; ++ ++ ++static int adm9240_attach_adapter(struct i2c_adapter *adapter); ++static int adm9240_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int adm9240_detach_client(struct i2c_client *client); ++ ++static int adm9240_read_value(struct i2c_client *client, u8 reg); ++static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value); ++static void adm9240_update_client(struct i2c_client *client); ++static void adm9240_init_client(struct i2c_client *client); ++ ++ ++static void adm9240_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm9240_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm9240_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm9240_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm9240_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm9240_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void adm9240_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int adm9240_id = 0; ++ ++static struct i2c_driver adm9240_driver = { ++ .owner = THIS_MODULE, ++ .name = "ADM9240 sensor driver", ++ .id = I2C_DRIVERID_ADM9240, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = adm9240_attach_adapter, ++ .detach_client = adm9240_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define ADM9240_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define ADM9240_SYSCTL_IN1 1001 ++#define ADM9240_SYSCTL_IN2 1002 ++#define ADM9240_SYSCTL_IN3 1003 ++#define ADM9240_SYSCTL_IN4 1004 ++#define ADM9240_SYSCTL_IN5 1005 ++#define ADM9240_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define ADM9240_SYSCTL_FAN2 1102 ++#define ADM9240_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ ++#define ADM9240_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define ADM9240_SYSCTL_ALARMS 2001 /* bitvector */ ++#define ADM9240_SYSCTL_ANALOG_OUT 2002 ++#define ADM9240_SYSCTL_VID 2003 ++ ++#define ADM9240_ALARM_IN0 0x0001 ++#define ADM9240_ALARM_IN1 0x0002 ++#define ADM9240_ALARM_IN2 0x0004 ++#define ADM9240_ALARM_IN3 0x0008 ++#define ADM9240_ALARM_IN4 0x0100 ++#define ADM9240_ALARM_IN5 0x0200 ++#define ADM9240_ALARM_FAN1 0x0040 ++#define ADM9240_ALARM_FAN2 0x0080 ++#define ADM9240_ALARM_TEMP 0x0010 ++#define ADM9240_ALARM_CHAS 0x1000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected ADM9240. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table adm9240_dir_table_template[] = { ++ {ADM9240_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_in}, ++ {ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_fan}, ++ {ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_fan}, ++ {ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_temp}, ++ {ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_fan_div}, ++ {ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_alarms}, ++ {ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_analog_out}, ++ {ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &adm9240_vid}, ++ {0} ++}; ++ ++static int adm9240_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, adm9240_detect); ++} ++ ++static int adm9240_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct adm9240_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access adm9240_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct adm9240_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &adm9240_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ( ++ ((adm9240_read_value ++ (new_client, ADM9240_REG_CONFIG) & 0x80) != 0x00) ++ || ++ (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR) ++ != address)) ++ goto ERROR1; ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ i = adm9240_read_value(new_client, ADM9240_REG_COMPANY_ID); ++ if (i == 0x23) ++ kind = adm9240; ++ else if (i == 0xda) ++ kind = ds1780; ++ else if (i == 0x01) ++ kind = lm81; ++ else { ++ if (kind == 0) ++ printk ++ ("adm9240.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ goto ERROR1; ++ } ++ } ++ ++ if (kind == adm9240) { ++ type_name = "adm9240"; ++ client_name = "ADM9240 chip"; ++ } else if (kind == ds1780) { ++ type_name = "ds1780"; ++ client_name = "DS1780 chip"; ++ } else if (kind == lm81) { ++ type_name = "lm81"; ++ client_name = "LM81 chip"; ++ } else { ++#ifdef DEBUG ++ printk("adm9240.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = adm9240_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ adm9240_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the ADM9240 chip */ ++ adm9240_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int adm9240_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct adm9240_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("adm9240.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++static int adm9240_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new ADM9240. */ ++static void adm9240_init_client(struct i2c_client *client) ++{ ++ /* Start monitoring */ ++ adm9240_write_value(client, ADM9240_REG_CONFIG, 0x01); ++} ++ ++static void adm9240_update_client(struct i2c_client *client) ++{ ++ struct adm9240_data *data = client->data; ++ u8 i; ++ ++ down(&data->update_lock); ++ ++ if ( ++ (jiffies - data->last_updated > ++ (data->type == adm9240 ? HZ / 2 : HZ * 2)) ++ || (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting adm9240 update\n"); ++#endif ++ for (i = 0; i <= 5; i++) { ++ data->in[i] = ++ adm9240_read_value(client, ADM9240_REG_IN(i)); ++ data->in_min[i] = ++ adm9240_read_value(client, ++ ADM9240_REG_IN_MIN(i)); ++ data->in_max[i] = ++ adm9240_read_value(client, ++ ADM9240_REG_IN_MAX(i)); ++ } ++ data->fan[0] = ++ adm9240_read_value(client, ADM9240_REG_FAN1); ++ data->fan_min[0] = ++ adm9240_read_value(client, ADM9240_REG_FAN1_MIN); ++ data->fan[1] = ++ adm9240_read_value(client, ADM9240_REG_FAN2); ++ data->fan_min[1] = ++ adm9240_read_value(client, ADM9240_REG_FAN2_MIN); ++ data->temp = ++ (adm9240_read_value(client, ADM9240_REG_TEMP) << 1) + ++ ((adm9240_read_value ++ (client, ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7); ++ data->temp_os_max = ++ adm9240_read_value(client, ADM9240_REG_TOS); ++ data->temp_os_hyst = ++ adm9240_read_value(client, ADM9240_REG_THYST); ++ ++ i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = (i >> 6) & 0x03; ++ data->vid = i & 0x0f; ++ data->vid |= ++ (adm9240_read_value(client, ADM9240_REG_VID4) & 0x01) ++ << 4; ++ ++ data->alarms = ++ adm9240_read_value(client, ++ ADM9240_REG_INT1_STAT) + ++ (adm9240_read_value(client, ADM9240_REG_INT2_STAT) << ++ 8); ++ data->analog_out = ++ adm9240_read_value(client, ADM9240_REG_ANALOG_OUT); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void adm9240_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ ++ int scales[6] = { 250, 270, 330, 500, 1200, 270 }; ++ ++ struct adm9240_data *data = client->data; ++ int nr = ctl_name - ADM9240_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = ++ IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; ++ results[1] = ++ IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; ++ results[2] = ++ IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = ++ IN_TO_REG((results[0] * 192) / scales[nr], nr); ++ adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = ++ IN_TO_REG((results[1] * 192) / scales[nr], nr); ++ adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void adm9240_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data-> ++ fan_div[nr - 1])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr - ++ 1])); ++ adm9240_write_value(client, ++ nr == ++ 1 ? ADM9240_REG_FAN1_MIN : ++ ADM9240_REG_FAN2_MIN, ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void adm9240_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); ++ adm9240_write_value(client, ADM9240_REG_TOS, ++ data->temp_os_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); ++ adm9240_write_value(client, ADM9240_REG_THYST, ++ data->temp_os_hyst); ++ } ++ } ++} ++ ++void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void adm9240_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ adm9240_write_value(client, ++ ADM9240_REG_VID_FAN_DIV, old); ++ } ++ } ++} ++ ++void adm9240_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = data->analog_out; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->analog_out = results[0]; ++ adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, ++ data->analog_out); ++ } ++ } ++} ++ ++void adm9240_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct adm9240_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ adm9240_update_client(client); ++ results[0] = VID_FROM_REG(data->vid); ++ *nrels_mag = 1; ++ } ++} ++ ++static int __init sm_adm9240_init(void) ++{ ++ printk("adm9240.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&adm9240_driver); ++} ++ ++static void __exit sm_adm9240_exit(void) ++{ ++ i2c_del_driver(&adm9240_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ADM9240 driver"); ++ ++module_init(sm_adm9240_init); ++module_exit(sm_adm9240_exit); +--- linux-old/drivers/sensors/asb100.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/asb100.c Mon Dec 13 20:18:45 2004 +@@ -0,0 +1,1025 @@ ++/* ++ asb100.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ ++ Copyright (c) 2003 Mark M. Hoffman ++ ++ (derived from w83781d.c) ++ ++ Copyright (c) 1998 - 2003 Frodo Looijaard , ++ Philip Edelbrock , and ++ Mark Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This driver supports the hardware sensor chips: Asus ASB100 and ++ ASB100-A "BACH". ++ ++ ASB100-A supports pwm1, while plain ASB100 does not. There is no known ++ way for the driver to tell which one is there. ++ ++ Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA ++ asb100 7 3 1 4 0x31 0x0694 yes no ++*/ ++ ++//#define DEBUG 1 ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++#include "lm75.h" ++ ++#ifndef I2C_DRIVERID_ASB100 ++#define I2C_DRIVERID_ASB100 1043 ++#endif ++ ++/* I2C addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x28, 0x2f, SENSORS_I2C_END }; ++ ++/* ISA addresses to scan (none) */ ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* default VRM to 9.0 instead of 8.2 */ ++#define ASB100_DEFAULT_VRM 90 ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(asb100); ++SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \ ++ "{bus, clientaddr, subclientaddr1, subclientaddr2}"); ++ ++/* Voltage IN registers 0-6 */ ++#define ASB100_REG_IN(nr) (0x20 + (nr)) ++#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2)) ++#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2)) ++ ++/* FAN IN registers 1-3 */ ++#define ASB100_REG_FAN(nr) (0x27 + (nr)) ++#define ASB100_REG_FAN_MIN(nr) (0x3a + (nr)) ++ ++/* TEMPERATURE registers 1-4 */ ++static const u16 asb100_reg_temp[] = {0, 0x27, 0x150, 0x250, 0x17}; ++static const u16 asb100_reg_temp_max[] = {0, 0x39, 0x155, 0x255, 0x18}; ++static const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19}; ++ ++#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr]) ++#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr]) ++#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr]) ++ ++#define ASB100_REG_TEMP2_CONFIG 0x0152 ++#define ASB100_REG_TEMP3_CONFIG 0x0252 ++ ++ ++#define ASB100_REG_CONFIG 0x40 ++#define ASB100_REG_ALARM1 0x41 ++#define ASB100_REG_ALARM2 0x42 ++#define ASB100_REG_SMIM1 0x43 ++#define ASB100_REG_SMIM2 0x44 ++#define ASB100_REG_VID_FANDIV 0x47 ++#define ASB100_REG_I2C_ADDR 0x48 ++#define ASB100_REG_CHIPID 0x49 ++#define ASB100_REG_I2C_SUBADDR 0x4a ++#define ASB100_REG_PIN 0x4b ++#define ASB100_REG_IRQ 0x4c ++#define ASB100_REG_BANK 0x4e ++#define ASB100_REG_CHIPMAN 0x4f ++ ++#define ASB100_REG_WCHIPID 0x58 ++ ++/* bit 7 -> enable, bits 0-3 -> duty cycle */ ++#define ASB100_REG_PWM1 0x59 ++ ++/* CONVERSIONS ++ Rounding and limit checking is only done on the TO_REG variants. */ ++ ++/* These constants are a guess, consistent w/ w83781d */ ++#define ASB100_IN_MIN ( 0) ++#define ASB100_IN_MAX (408) ++ ++/* IN: 1/100 V (0V to 4.08V) ++ REG: 16mV/bit */ ++static u8 IN_TO_REG(unsigned val) ++{ ++ unsigned nval = SENSORS_LIMIT(val, ASB100_IN_MIN, ASB100_IN_MAX); ++ return (nval * 10 + 8) / 16; ++} ++ ++static unsigned IN_FROM_REG(u8 reg) ++{ ++ return (reg * 16 + 5) / 10; ++} ++ ++static u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); ++} ++ ++static int FAN_FROM_REG(u8 val, int div) ++{ ++ return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); ++} ++ ++/* These constants are a guess, consistent w/ w83781d */ ++#define ASB100_TEMP_MIN (-1280) ++#define ASB100_TEMP_MAX ( 1270) ++ ++/* TEMP: 1/10 degrees C (-128C to +127C) ++ REG: 1C/bit, two's complement */ ++static u8 TEMP_TO_REG(int temp) ++{ ++ int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); ++ ntemp += (ntemp<0 ? -5 : 5); ++ return (u8)(ntemp / 10); ++} ++ ++static int TEMP_FROM_REG(u8 reg) ++{ ++ return (s8)reg * 10; ++} ++ ++/* PWM: 0 - 255 per sensors documentation ++ REG: (6.25% duty cycle per bit) */ ++static u8 ASB100_PWM_TO_REG(int pwm) ++{ ++ pwm = SENSORS_LIMIT(pwm, 0, 255); ++ return (u8)(pwm / 16); ++} ++ ++static int ASB100_PWM_FROM_REG(u8 reg) ++{ ++ return reg * 16; ++} ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) ++ REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ ++static u8 DIV_TO_REG(long val) ++{ ++ return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; ++} ++ ++/* For each registered client, we need to keep some data in memory. That ++ data is pointed to by client->data. The structure itself is ++ dynamically allocated, at the same time the client itself is allocated. */ ++struct asb100_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ unsigned long last_updated; /* In jiffies */ ++ ++ /* array of 2 pointers to subclients */ ++ struct i2c_client *lm75[2]; ++ ++ char valid; /* !=0 if following fields are valid */ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u16 temp[4]; /* Register value (0 and 3 are u8 only) */ ++ u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */ ++ u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */ ++ u8 fan_div[3]; /* Register encoding, right justified */ ++ u8 pwm; /* Register encoding */ ++ u8 vid; /* Register encoding, combined */ ++ u32 alarms; /* Register encoding, combined */ ++ u8 vrm; ++}; ++ ++static int asb100_attach_adapter(struct i2c_adapter *adapter); ++static int asb100_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int asb100_detach_client(struct i2c_client *client); ++ ++static int asb100_read_value(struct i2c_client *client, u16 reg); ++static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); ++static void asb100_update_client(struct i2c_client *client); ++static void asb100_init_client(struct i2c_client *client); ++ ++static void asb100_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void asb100_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static struct i2c_driver asb100_driver = { ++ .owner = THIS_MODULE, ++ .name = "asb100", ++ .id = I2C_DRIVERID_ASB100, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = asb100_attach_adapter, ++ .detach_client = asb100_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define ASB100_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define ASB100_SYSCTL_IN1 1001 ++#define ASB100_SYSCTL_IN2 1002 ++#define ASB100_SYSCTL_IN3 1003 ++#define ASB100_SYSCTL_IN4 1004 ++#define ASB100_SYSCTL_IN5 1005 ++#define ASB100_SYSCTL_IN6 1006 ++ ++#define ASB100_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define ASB100_SYSCTL_FAN2 1102 ++#define ASB100_SYSCTL_FAN3 1103 ++ ++#define ASB100_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define ASB100_SYSCTL_TEMP2 1201 ++#define ASB100_SYSCTL_TEMP3 1202 ++#define ASB100_SYSCTL_TEMP4 1203 ++ ++#define ASB100_SYSCTL_VID 1300 /* Volts * 1000 */ ++#define ASB100_SYSCTL_VRM 1301 ++ ++#define ASB100_SYSCTL_PWM1 1401 /* 0-255 => 0-100% duty cycle */ ++ ++#define ASB100_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define ASB100_SYSCTL_ALARMS 2001 /* bitvector */ ++ ++#define ASB100_ALARM_IN0 0x0001 /* ? */ ++#define ASB100_ALARM_IN1 0x0002 /* ? */ ++#define ASB100_ALARM_IN2 0x0004 ++#define ASB100_ALARM_IN3 0x0008 ++#define ASB100_ALARM_TEMP1 0x0010 ++#define ASB100_ALARM_TEMP2 0x0020 ++#define ASB100_ALARM_FAN1 0x0040 ++#define ASB100_ALARM_FAN2 0x0080 ++#define ASB100_ALARM_IN4 0x0100 ++#define ASB100_ALARM_IN5 0x0200 /* ? */ ++#define ASB100_ALARM_IN6 0x0400 /* ? */ ++#define ASB100_ALARM_FAN3 0x0800 ++#define ASB100_ALARM_CHAS 0x1000 ++#define ASB100_ALARM_TEMP3 0x2000 ++ ++#define ASB100_ALARM_IN7 0x10000 /* ? */ ++#define ASB100_ALARM_IN8 0x20000 /* ? */ ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected chip. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++ ++/* no datasheet - but we did get some hints from someone who ++ claimed to have the datasheet */ ++#define ASB100_SYSCTL_IN(nr) {ASB100_SYSCTL_IN##nr, "in" #nr, NULL, 0, \ ++ 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &asb100_in} ++#define ASB100_SYSCTL_FAN(nr) {ASB100_SYSCTL_FAN##nr, "fan" #nr, NULL, 0, \ ++ 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &asb100_fan} ++#define ASB100_SYSCTL_TEMP(nr, func) {ASB100_SYSCTL_TEMP##nr, "temp" #nr, \ ++ NULL, 0, 0644, NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, func} ++static ctl_table asb100_dir_table_template[] = { ++ ASB100_SYSCTL_IN(0), ++ ASB100_SYSCTL_IN(1), ++ ASB100_SYSCTL_IN(2), ++ ASB100_SYSCTL_IN(3), ++ ASB100_SYSCTL_IN(4), ++ ASB100_SYSCTL_IN(5), ++ ASB100_SYSCTL_IN(6), ++ ++ ASB100_SYSCTL_FAN(1), ++ ASB100_SYSCTL_FAN(2), ++ ASB100_SYSCTL_FAN(3), ++ ++ ASB100_SYSCTL_TEMP(1, &asb100_temp), ++ ASB100_SYSCTL_TEMP(2, &asb100_temp_add), ++ ASB100_SYSCTL_TEMP(3, &asb100_temp_add), ++ ASB100_SYSCTL_TEMP(4, &asb100_temp), ++ ++ {ASB100_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &asb100_vid}, ++ {ASB100_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &asb100_vrm}, ++ {ASB100_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &asb100_fan_div}, ++ {ASB100_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &asb100_alarms}, ++ {ASB100_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &asb100_pwm}, ++ {0} ++}; ++ ++/* This function is called when: ++ asb100_driver is inserted (when this module is loaded), for each ++ available adapter ++ when a new adapter is inserted (and asb100_driver is still present) ++ */ ++static int asb100_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, asb100_detect); ++} ++ ++static int asb100_detect_subclients(struct i2c_adapter *adapter, int address, ++ int kind, struct i2c_client *new_client) ++{ ++ int i, id, err; ++ struct asb100_data *data = new_client->data; ++ ++ data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); ++ if (!(data->lm75[0])) { ++ err = -ENOMEM; ++ goto ERROR_SC_0; ++ } ++ memset(data->lm75[0], 0x00, sizeof(struct i2c_client)); ++ ++ data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); ++ if (!(data->lm75[1])) { ++ err = -ENOMEM; ++ goto ERROR_SC_1; ++ } ++ memset(data->lm75[1], 0x00, sizeof(struct i2c_client)); ++ ++ id = i2c_adapter_id(adapter); ++ ++ if (force_subclients[0] == id && force_subclients[1] == address) { ++ for (i = 2; i <= 3; i++) { ++ if (force_subclients[i] < 0x48 || ++ force_subclients[i] > 0x4f) { ++ printk(KERN_ERR "asb100.o: invalid subclient " ++ "address %d; must be 0x48-0x4f\n", ++ force_subclients[i]); ++ err = -ENODEV; ++ goto ERROR_SC_2; ++ } ++ } ++ asb100_write_value(new_client, ASB100_REG_I2C_SUBADDR, ++ (force_subclients[2] & 0x07) | ++ ((force_subclients[3] & 0x07) <<4)); ++ data->lm75[0]->addr = force_subclients[2]; ++ data->lm75[1]->addr = force_subclients[3]; ++ } else { ++ int val = asb100_read_value(new_client, ASB100_REG_I2C_SUBADDR); ++ data->lm75[0]->addr = 0x48 + (val & 0x07); ++ data->lm75[1]->addr = 0x48 + ((val >> 4) & 0x07); ++ } ++ ++ if(data->lm75[0]->addr == data->lm75[1]->addr) { ++ printk(KERN_ERR "asb100.o: duplicate addresses 0x%x " ++ "for subclients\n", data->lm75[0]->addr); ++ err = -ENODEV; ++ goto ERROR_SC_2; ++ } ++ ++ for (i = 0; i <= 1; i++) { ++ data->lm75[i]->data = NULL; ++ data->lm75[i]->adapter = adapter; ++ data->lm75[i]->driver = &asb100_driver; ++ data->lm75[i]->flags = 0; ++ strcpy(data->lm75[i]->name, "asb100 subclient"); ++ } ++ ++ if ((err = i2c_attach_client(data->lm75[0]))) { ++ printk(KERN_ERR "asb100.o: Subclient %d registration " ++ "at address 0x%x failed.\n", i, data->lm75[0]->addr); ++ goto ERROR_SC_2; ++ } ++ ++ if ((err = i2c_attach_client(data->lm75[1]))) { ++ printk(KERN_ERR "asb100.o: Subclient %d registration " ++ "at address 0x%x failed.\n", i, data->lm75[1]->addr); ++ goto ERROR_SC_3; ++ } ++ ++ return 0; ++ ++/* Undo inits in case of errors */ ++ERROR_SC_3: ++ i2c_detach_client(data->lm75[0]); ++ERROR_SC_2: ++ kfree(data->lm75[1]); ++ERROR_SC_1: ++ kfree(data->lm75[0]); ++ERROR_SC_0: ++ return err; ++} ++ ++static int asb100_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int err; ++ struct i2c_client *new_client; ++ struct asb100_data *data; ++ ++ /* asb100 is SMBus only */ ++ if (i2c_is_isa_adapter(adapter)) { ++ pr_debug("asb100.o: detect failed, " ++ "cannot attach to legacy adapter!\n"); ++ err = -ENODEV; ++ goto ERROR0; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ pr_debug("asb100.o: detect failed, " ++ "smbus byte data not supported!\n"); ++ err = -ENODEV; ++ goto ERROR0; ++ } ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access asb100_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct asb100_data), GFP_KERNEL))) { ++ pr_debug("asb100.o: detect failed, kmalloc failed!\n"); ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &asb100_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ /* The chip may be stuck in some other bank than bank 0. This may ++ make reading other information impossible. Specify a force=... or ++ force_*=... parameter, and the chip will be reset to the right ++ bank. */ ++ if (kind < 0) { ++ ++ int val1 = asb100_read_value(new_client, ASB100_REG_BANK); ++ int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN); ++ ++ /* If we're in bank 0 */ ++ if ( (!(val1 & 0x07)) && ++ /* Check for ASB100 ID (low byte) */ ++ ( ((!(val1 & 0x80)) && (val2 != 0x94)) || ++ /* Check for ASB100 ID (high byte ) */ ++ ((val1 & 0x80) && (val2 != 0x06)) ) ) { ++ pr_debug("asb100.o: detect failed, " ++ "bad chip id 0x%02x!\n", val2); ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ ++ } /* kind < 0 */ ++ ++ /* We have either had a force parameter, or we have already detected ++ Winbond. Put it now into bank 0 and Vendor ID High Byte */ ++ asb100_write_value(new_client, ASB100_REG_BANK, ++ (asb100_read_value(new_client, ASB100_REG_BANK) & 0x78) | 0x80); ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ int val1 = asb100_read_value(new_client, ASB100_REG_WCHIPID); ++ int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN); ++ ++ if ((val1 == 0x31) && (val2 == 0x06)) ++ kind = asb100; ++ else { ++ if (kind == 0) ++ printk (KERN_WARNING "asb100.o: Ignoring " ++ "'force' parameter for unknown chip " ++ "at adapter %d, address 0x%02x.\n", ++ i2c_adapter_id(adapter), address); ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ } ++ ++ /* Fill in remaining client fields and put it into the global list */ ++ strcpy(new_client->name, "ASB100 chip"); ++ data->type = kind; ++ ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* Attach secondary lm75 clients */ ++ if ((err = asb100_detect_subclients(adapter, address, kind, ++ new_client))) ++ goto ERROR2; ++ ++ /* Initialize the chip */ ++ asb100_init_client(new_client); ++ ++ /* Register a new directory entry with module sensors */ ++ if ((data->sysctl_id = i2c_register_entry(new_client, "asb100", ++ asb100_dir_table_template)) < 0) { ++ err = data->sysctl_id; ++ goto ERROR3; ++ } ++ ++ return 0; ++ ++ERROR3: ++ i2c_detach_client(data->lm75[0]); ++ kfree(data->lm75[1]); ++ kfree(data->lm75[0]); ++ERROR2: ++ i2c_detach_client(new_client); ++ERROR1: ++ kfree(data); ++ERROR0: ++ return err; ++} ++ ++static int asb100_detach_client(struct i2c_client *client) ++{ ++ int err; ++ struct asb100_data *data = client->data; ++ ++ /* remove sysctl table (primary client only) */ ++ if ((data)) ++ i2c_deregister_entry(data->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk (KERN_ERR "asb100.o: Client deregistration failed; " ++ "client not detached.\n"); ++ return err; ++ } ++ ++ if (data) { ++ /* primary client */ ++ kfree(data); ++ } else { ++ /* subclients */ ++ kfree(client); ++ } ++ ++ return 0; ++} ++ ++/* The SMBus locks itself, usually, but nothing may access the Winbond between ++ bank switches. ISA access must always be locked explicitly! ++ We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the W83781D access and should not be necessary. ++ There are some ugly typecasts here, but the good news is - they should ++ nowhere else be necessary! */ ++static int asb100_read_value(struct i2c_client *client, u16 reg) ++{ ++ struct asb100_data *data = client->data; ++ struct i2c_client *cl; ++ int res, bank; ++ ++ down(&data->lock); ++ ++ bank = (reg >> 8) & 0x0f; ++ if (bank > 2) ++ /* switch banks */ ++ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); ++ ++ if (bank == 0 || bank > 2) { ++ res = i2c_smbus_read_byte_data(client, reg & 0xff); ++ } else { ++ /* switch to subclient */ ++ cl = data->lm75[bank - 1]; ++ ++ /* convert from ISA to LM75 I2C addresses */ ++ switch (reg & 0xff) { ++ case 0x50: /* TEMP */ ++ res = swab16(i2c_smbus_read_word_data (cl, 0)); ++ break; ++ case 0x52: /* CONFIG */ ++ res = i2c_smbus_read_byte_data(cl, 1); ++ break; ++ case 0x53: /* HYST */ ++ res = swab16(i2c_smbus_read_word_data (cl, 2)); ++ break; ++ case 0x55: /* MAX */ ++ default: ++ res = swab16(i2c_smbus_read_word_data (cl, 3)); ++ break; ++ } ++ } ++ ++ if (bank > 2) ++ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); ++ ++ up(&data->lock); ++ ++ return res; ++} ++ ++static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) ++{ ++ struct asb100_data *data = client->data; ++ struct i2c_client *cl; ++ int bank; ++ ++ down(&data->lock); ++ ++ bank = (reg >> 8) & 0x0f; ++ if (bank > 2) ++ /* switch banks */ ++ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); ++ ++ if (bank == 0 || bank > 2) { ++ i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff); ++ } else { ++ /* switch to subclient */ ++ cl = data->lm75[bank - 1]; ++ ++ /* convert from ISA to LM75 I2C addresses */ ++ switch (reg & 0xff) { ++ case 0x52: /* CONFIG */ ++ i2c_smbus_write_byte_data(cl, 1, value & 0xff); ++ break; ++ case 0x53: /* HYST */ ++ i2c_smbus_write_word_data(cl, 2, swab16(value)); ++ break; ++ case 0x55: /* MAX */ ++ i2c_smbus_write_word_data(cl, 3, swab16(value)); ++ break; ++ } ++ } ++ ++ if (bank > 2) ++ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); ++ ++ up(&data->lock); ++} ++ ++static void asb100_init_client(struct i2c_client *client) ++{ ++ struct asb100_data *data = client->data; ++ int vid = 0; ++ ++ vid = asb100_read_value(client, ASB100_REG_VID_FANDIV) & 0x0f; ++ vid |= (asb100_read_value(client, ASB100_REG_CHIPID) & 0x01) << 4; ++ data->vrm = ASB100_DEFAULT_VRM; ++ vid = vid_from_reg(vid, data->vrm); ++ ++ /* Start monitoring */ ++ asb100_write_value(client, ASB100_REG_CONFIG, ++ (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01); ++} ++ ++static void asb100_update_client(struct i2c_client *client) ++{ ++ struct asb100_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if (time_after(jiffies - data->last_updated, HZ + HZ / 2) || ++ time_before(jiffies, data->last_updated) || !data->valid) { ++ ++ pr_debug("asb100.o: starting device update...\n"); ++ ++ /* 7 voltage inputs */ ++ for (i = 0; i < 7; i++) { ++ data->in[i] = asb100_read_value(client, ++ ASB100_REG_IN(i)); ++ data->in_min[i] = asb100_read_value(client, ++ ASB100_REG_IN_MIN(i)); ++ data->in_max[i] = asb100_read_value(client, ++ ASB100_REG_IN_MAX(i)); ++ } ++ ++ /* 3 fan inputs */ ++ for (i = 1; i <= 3; i++) { ++ data->fan[i-1] = asb100_read_value(client, ++ ASB100_REG_FAN(i)); ++ data->fan_min[i-1] = asb100_read_value(client, ++ ASB100_REG_FAN_MIN(i)); ++ } ++ ++ /* 4 temperature inputs */ ++ for (i = 1; i <= 4; i++) { ++ data->temp[i-1] = asb100_read_value(client, ++ ASB100_REG_TEMP(i)); ++ data->temp_max[i-1] = asb100_read_value(client, ++ ASB100_REG_TEMP_MAX(i)); ++ data->temp_hyst[i-1] = asb100_read_value(client, ++ ASB100_REG_TEMP_HYST(i)); ++ } ++ ++ /* VID and fan divisors */ ++ i = asb100_read_value(client, ASB100_REG_VID_FANDIV); ++ data->vid = i & 0x0f; ++ data->vid |= (asb100_read_value(client, ++ ASB100_REG_CHIPID) & 0x01) << 4; ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = (i >> 6) & 0x03; ++ data->fan_div[2] = (asb100_read_value(client, ++ ASB100_REG_PIN) >> 6) & 0x03; ++ ++ /* PWM */ ++ data->pwm = asb100_read_value(client, ASB100_REG_PWM1); ++ ++ /* alarms */ ++ data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) + ++ (asb100_read_value(client, ASB100_REG_ALARM2) << 8); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ ++ pr_debug("asb100.o: ... update complete.\n"); ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++static void asb100_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ int nr = ctl_name - ASB100_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ asb100_write_value(client, ASB100_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ asb100_write_value(client, ASB100_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void asb100_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ int nr = ctl_name - ASB100_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = ++ FAN_TO_REG(results[0], ++ DIV_FROM_REG(data->fan_div[nr-1])); ++ asb100_write_value(client, ++ ASB100_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++void asb100_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ int nr = ctl_name - ASB100_SYSCTL_TEMP1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_max[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 3; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_max[nr] = TEMP_TO_REG(results[0]); ++ asb100_write_value(client, ASB100_REG_TEMP_MAX(nr+1), ++ data->temp_max[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = TEMP_TO_REG(results[1]); ++ asb100_write_value(client, ASB100_REG_TEMP_HYST(nr+1), ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void asb100_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ int nr = ctl_name - ASB100_SYSCTL_TEMP1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ ++ results[0] = LM75_TEMP_FROM_REG(data->temp_max[nr]); ++ results[1] = LM75_TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = LM75_TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 3; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_max[nr] = ++ LM75_TEMP_TO_REG(results[0]); ++ asb100_write_value(client, ASB100_REG_TEMP_MAX(nr+1), ++ data->temp_max[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = ++ LM75_TEMP_TO_REG(results[1]); ++ asb100_write_value(client, ASB100_REG_TEMP_HYST(nr+1), ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void asb100_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void asb100_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void asb100_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void asb100_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ int old, old2; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ results[2] = DIV_FROM_REG(data->fan_div[2]); ++ *nrels_mag = 3; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = asb100_read_value(client, ASB100_REG_VID_FANDIV); ++ if (*nrels_mag >= 3) { ++ data->fan_div[2] = DIV_TO_REG(results[2]); ++ old2 = asb100_read_value(client, ASB100_REG_PIN); ++ old2 = (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); ++ asb100_write_value(client, ASB100_REG_PIN, old2); ++ } ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); ++ asb100_write_value(client, ASB100_REG_VID_FANDIV, old); ++ } ++ } ++} ++ ++void asb100_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct asb100_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ asb100_update_client(client); ++ results[0] = ASB100_PWM_FROM_REG(data->pwm & 0x0f); ++ results[1] = (data->pwm & 0x80) ? 1 : 0; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ u8 val = data->pwm; ++ if (*nrels_mag >= 1) { ++ val = 0x0f & ASB100_PWM_TO_REG(results[0]); ++ if (*nrels_mag >= 2) { ++ if (results[1]) ++ val |= 0x80; ++ else ++ val &= ~0x80; ++ } ++ asb100_write_value(client, ASB100_REG_PWM1, val); ++ } ++ } ++} ++ ++static int __init asb100_init(void) ++{ ++ printk(KERN_INFO "asb100.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&asb100_driver); ++} ++ ++static void __exit asb100_exit(void) ++{ ++ i2c_del_driver(&asb100_driver); ++} ++ ++MODULE_AUTHOR( "Mark M. Hoffman , " ++ "Frodo Looijaard , " ++ "Philip Edelbrock , and" ++ "Mark Studebaker "); ++ ++MODULE_DESCRIPTION("ASB100 'Bach' driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(asb100_init); ++module_exit(asb100_exit); ++ +--- linux-old/drivers/sensors/bt869.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/bt869.c Mon Dec 13 20:18:45 2004 +@@ -0,0 +1,891 @@ ++/* ++ bt869.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ Copyright (c) 2001, 2002 Stephen Davies ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++ ++#define DEBUG 1 ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++ ++/* found only at 0x44 or 0x45 */ ++static unsigned short normal_i2c_range[] = { 0x44, 0x45, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(bt869); ++ ++/* Many bt869 constants specified below */ ++ ++/* The bt869 registers */ ++/* Coming soon: Many, many registers */ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++ /*none */ ++ ++/* Initial values */ ++/*none*/ ++ ++/* Each client has this additional data */ ++struct bt869_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 status[3]; /* Register values */ ++ u16 res[2]; /* Resolution XxY */ ++ u8 ntsc; /* 1=NTSC, 0=PAL */ ++ u8 half; /* go half res */ ++ u8 depth; /* screen depth */ ++ u8 colorbars; /* turn on/off colorbar calibration screen */ ++ u8 svideo; /* output format: (2=RGB) 1=SVIDEO, 0=Composite */ ++}; ++ ++static int bt869_attach_adapter(struct i2c_adapter *adapter); ++static int bt869_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void bt869_init_client(struct i2c_client *client); ++static int bt869_detach_client(struct i2c_client *client); ++static int bt869_read_value(struct i2c_client *client, u8 reg); ++static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value); ++static void bt869_write_values(struct i2c_client *client, u16 *values); ++static void bt869_status(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_ntsc(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_res(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_half(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_colorbars(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_svideo(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_depth(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void bt869_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver bt869_driver = { ++ .owner = THIS_MODULE, ++ .name = "BT869 video-output chip driver", ++ .id = I2C_DRIVERID_BT869, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = bt869_attach_adapter, ++ .detach_client = bt869_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define BT869_SYSCTL_STATUS 1000 ++#define BT869_SYSCTL_NTSC 1001 ++#define BT869_SYSCTL_HALF 1002 ++#define BT869_SYSCTL_RES 1003 ++#define BT869_SYSCTL_COLORBARS 1004 ++#define BT869_SYSCTL_DEPTH 1005 ++#define BT869_SYSCTL_SVIDEO 1006 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected bt869. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table bt869_dir_table_template[] = { ++ {BT869_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_status}, ++ {BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_ntsc}, ++ {BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_res}, ++ {BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_half}, ++ {BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_colorbars}, ++ {BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_depth}, ++ {BT869_SYSCTL_SVIDEO, "svideo", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &bt869_svideo}, ++ {0} ++}; ++ ++/* ****************** ++ ++720x576, 27.5MHz, PAL, no overscan compensation. ++ ++This mode should be use for digital video, DVD playback etc. ++ ++NOTE: This mode for PAL, see 720x480 for an equivalent NTSC mode ++NOTE: -- Steve Davies ++ ++ ++Compatible X modeline: ++ ++ Mode "720x576-BT869" ++ DotClock 27.5 ++ HTimings 720 744 800 880 ++ VTimings 576 581 583 625 ++ EndMode ++ ++ ++625LINE=1 625 line output format ++BST_AMP[7:0]=x57 87 Burst ampl. multiplication factor (PAL std??) ++BY_PLL=0 Use the PLL ++CATTENUATE[2:0]=0 No chroma attenuation ++CCF1B1[7:0]=0 close caption stuff ++CCF1B2[7:0]=0 close caption stuff ++CCF2B1[7:0]=0 close caption stuff ++CCF2B2[7:0]=0 close caption stuff ++CCORING[2:0]=0 Bypass chroma coring ++CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0] Close-caption clock runin start from hsync ++CC_ADD[11:0]=xD2 210 [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2] Close-caption DTO increment ++CHECK_STAT=0 Don't check monitor status ++CLPF[1:0]=0 Hoz chroma lowpass filter=Bypass ++DACDISA=1 Disable DACA ++DACDISB=0 Don't disable DACB ++DACDISC=0 Don't disable DACC ++DACOFF=0 Don't disable the DACs ++DATDLY = 0 normal ++DATSWP=0 normal ++DCHROMA=0 Don't blank chroma ++DIS_FFILT=1 Disable flickerfilter ++DIS_GMSHC=1 Disable chroma psuedo-gamma removal ++DIS_GMSHY=1 Disable luma pseudo gamma removal ++DIS_GMUSHC=1 Disable chroma anti-pseudo gamma removal ++DIS_GMUSHY=1 Disable luma anti-pseudo gamma removal ++DIS_SCRESET=0 Normal subcarrier phase resets ++DIS_YFLPF=0 Disable Luma initial hoz low pass filter ++DIV2=0 Input pixel rate not divided by 2 ++ECBAR=0 No colour bars ++ECCF1=0 Disable closed caption ++ECCF2=0 Disable closed caption ++ECCGATE=0 Normal close caption encoding ++ECLIP=0 0=disable clipping ++EN_ASYNC=0 set to 0 for normal operation ++EN_BLANKO=0 BLANK is an input ++EN_DOT=0 Disables dot clock sync on BLANK pin ++EN_OUT=1 Allows outputs to be enabled ++EN_XCLK=1 Use CLKI pin as clock source ++ESTATUS[1:0]=0 Used to select readback register ++FIELDI=0 Logical 1 on FIELD indicates even field ++F_SELC[2:0]=0 5 line chroma flickerfilter ++F_SELY[2:0]=0 5 line luma flickerfilter ++HBURST_BEGIN[7:0]=x98 152 Chroma burst start point in clocks ++HBURST_END[7:0]=x58 88 Chroma burst end point in clocks - 128 ++HSYNCI=0 Active low HSYNC ++HSYNC_WIDTH[7:0]=x80 128 Analogue sync width in clocks ++HSYNOFFSET[9:0]=0 [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0] hsync in "standard position" ++HSYNWIDTH[5:0]=2 2 pixel hsync width ++H_ACTIVE[9:0]=x2D0 720 [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0] Active pixels per line ++H_BLANKI[8:0]=x84 132 [H_BLANKI[8]=0; H_BLANKI[7:0]=x84] End of blanking of input video ++H_BLANKO[9:0]=x120 288 [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x20] End of blanking from hoz sync leading edge ++H_CLKI[10:0]=x378 888 [H_CLKI[10:8]=3; H_CLKI[7:0]=x78] Input line length total in clocks ++H_CLKO[11:0]=x6e0 1760 [H_CLKO[11:8]=6; H_CLKO[7:0]=xe0] Output clocks per line ++H_FRACT[7:0]=0 0 fractional input clocks per line ++IN_MODE[2:0]=0 24Bit RGB muxed ++LUMADLY[1:0]=0 0 pixel delay on Y_DLY luma ++MCB[7:0]=x49 73 Mult factor for CB prior to subcarrier mod. ++MCR[7:0]=x82 130 Mult factor for CR prior to subcarrier mod. ++MODE2X=0 Don't divide clock input by 2 ++MSC[31:0]=x2945E0B4 692445365 [MSC[31:24]=x29; MSC[23:16]=x45; MSC[15:8]=xE0; MSC[7:0]=xB4] Subcarrier incr. ++MY[7:0]=x8C 140 Mult factor for Y ++NI_OUT=0 Normal interlaced output ++OUT_MODE[1:0]=0 video0-3 is CVBS, Y, C, Y_DLY ++OUT_MUXA[1:0]=0 Don't care as DACA is disabled ++OUT_MUXB[1:0]=1 Output video[1] (Y) on DACB ++OUT_MUXC[1:0]=2 Output video[2] (C) on DACC ++PAL_MD=1 Video output in PAL mode ++PHASE_OFF[7:0]=0 Subcarrier phase offset ++PLL_FRACT[15:0]=x30 48 [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier ++PLL_INT[5:0]=0x0C 12 Int portion of pll multiplier ++SETUP=0 7.5-IRE setup disabled ++SLAVER=1 ++SRESET=0 Don't do a software reset ++SYNC_AMP[7:0]=xF0 240 Sync amp mult. factor (PAL std???) ++VBLANKDLY=0 Extra line of blanking in 2nd field? ++VSYNCI=0 Active low VSYNC ++VSYNC_DUR=0 2.5line VSYNC duration on output ++VSYNCOFFSET[10:0]=0 [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0] VSYNC in standard position ++VSYNWIDTH[2:0]=1 1 line of vsync width ++V_ACTIVEI[9:0]=x240 576 [V_ACTIVEI[9:0]=2; V_ACTIVEI[7:0]=x40] Active input lines ++V_ACTIVEO[8:0]=x122 290 [V_ACTIVE0[8]=1; V_ACTIVEO[7:0]=x22] ++V_BLANKI[7:0]=x2A 42 Input lines from vsync to first active line ++V_BLANKO[7:0]=x16 22 ++V_LINESI[9:0]=x271 625 [V_LINESI[9:8]=2; V_LINESI[7:0]=x71] Number of input lines ++V_SCALE[13:0]=x1000 4096 [V_SCALE[13:8]=x10; V_SCALE[7:0]=0] Vert scale coefficient="none"? ++YATTENUATE[2:0]=0 no luma attenuation ++YCORING[2:0]=0 Luma-coring bypass ++YLPF[1:0]=0 Luma hoz low pass filter=bypass ++ ++***************** */ ++ ++static u16 registers_720_576[] = ++ { ++ 0x6e, 0x00, /* HSYNOFFSET[7:0]=0 */ ++ 0x70, 0x02, /* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */ ++ 0x72, 0x00, /* VSYNOFFSET[7:0]=0 */ ++ 0x74, 0x01, /* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */ ++ 0x76, 0xe0, /* H_CLKO[7:0]=xe0 */ ++ 0x78, 0xd0, /* H_ACTIVE[7:0]=xD0 */ ++ 0x7a, 0x80, /* HSYNC_WIDTH[7:0]=x80 */ ++ 0x7c, 0x98, /* HBURST_BEGIN[7:0]=x98 */ ++ 0x7e, 0x58, /* HBURST_END[7:0]=x58 */ ++ 0x80, 0x20, /* H_BLANKO[7:0]=x20 */ ++ 0x82, 0x16, /* V_BLANKO[7:0]=x16 */ ++ 0x84, 0x22, /* V_ACTIVEO[7:0]=x22 */ ++ 0x86, 0xa6, /* V_ACTIVE0[8]=1; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */ ++ 0x88, 0x00, /* H_FRACT[7:0]=0 */ ++ 0x8a, 0x78, /* H_CLKI[7:0]=x78 */ ++ 0x8c, 0x80, /* H_BLANKI[7:0]=x84 */ ++ 0x8e, 0x03, /* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */ ++ 0x90, 0x71, /* V_LINESI[7:0]=x71 */ ++ 0x92, 0x2a, /* V_BLANKI[7:0]=x2A */ ++ 0x94, 0x40, /* V_ACTIVEI[7:0]=x40 */ ++ 0x96, 0x0a, /* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:0]=2; V_LINESI[9:8]=2 */ ++ 0x98, 0x00, /* V_SCALE[7:0]=0 */ ++ 0x9a, 0x50, /* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */ ++ 0x9c, 0x30, /* PLL_FRACT[7:0]=x30 */ ++ 0x9e, 0x0, /* PLL_FRACT[15:8]=0x0 */ ++ 0xa0, 0x8c, /* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */ ++ 0xa2, 0x24, /* ECLIP=0; PAL_MD=1; DIS_SCRESET=0; VSYNC_DUR=0; 625LINE=1; SETUP=0; NI_OUT=0 */ ++ 0xa4, 0xf0, /* SYNC_AMP[7:0]=xF0 */ ++ 0xa6, 0x57, /* BST_AMP[7:0]=x57 */ ++ 0xa8, 0x82, /* MCR[7:0]=x82 */ ++ 0xaa, 0x49, /* MCB[7:0]=x49 */ ++ 0xac, 0x8c, /* MY[7:0]=x8C */ ++ 0xae, 0xb4, /* MSC[7:0]=xb4 */ ++ 0xb0, 0xe0, /* MSC[15:8]=xe0 */ ++ 0xb2, 0x45, /* MSC[23:16]=x45 */ ++ 0xb4, 0x29, /* MSC[31:24]=x29 */ ++ 0xb6, 0x00, /* PHASE_OFF[7:0]=0 */ ++ //0xba, 0x21, /* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */ ++ 0xc4, 0x01, /* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */ ++ 0xc6, 0x00, /* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */ ++ 0xc8, 0x40, /* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */ ++ 0xca, 0xc0, /* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */ ++ 0xcc, 0xc0, /* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */ ++ //0xce, 0x24, /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ ++ //0xce, 0x04, /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ ++ 0xd6, 0x00, /* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */ ++ 0, 0 ++ }; ++ ++ ++/* ****************** ++ ++720x480, 27.5MHz, NTSC no overscan compensation. ++ ++This mode should be use for digital video, DVD playback etc. ++ ++NOTE: This mode for NTSC, see 720x576 for an equivalent PAL mode ++NOTE: -- Steve Davies ++ ++Compatible X modeline: ++ ++ Mode "720x480-BT869" ++ DotClock 27.5 ++ HTimings 720 744 800 872 ++ VTimings 480 483 485 525 ++ EndMode ++ ++ ++625LINE=0 not 625 line output format ++BST_AMP[7:0]=x74 116 Burst ampl. multiplication factor (NTSC std??) ++BY_PLL=0 Use the PLL ++CATTENUATE[2:0]=0 No chroma attenuation ++CCF1B1[7:0]=0 close caption stuff ++CCF1B2[7:0]=0 close caption stuff ++CCF2B1[7:0]=0 close caption stuff ++CCF2B2[7:0]=0 close caption stuff ++CCORING[2:0]=0 Bypass chroma coring ++CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0] Close-caption clock runin start from hsync ++CC_ADD[11:0]=xD2 210 [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2] Close-caption DTO increment ++CHECK_STAT=0 Don't check monitor status ++CLPF[1:0]=0 Hoz chroma lowpass filter=Bypass ++DACDISA=1 Disable DACA ++DACDISB=0 Don't disable DACB ++DACDISC=0 Don't disable DACC ++DACOFF=0 Don't disable the DACs ++DATDLY = 0 normal ++DATSWP=0 normal ++DCHROMA=0 Don't blank chroma ++DIS_FFILT=1 Disable flickerfilter ++DIS_GMSHC=1 Disable chroma psuedo-gamma removal ++DIS_GMSHY=1 Disable luma pseudo gamma removal ++DIS_GMUSHC=1 Disable chroma anti-pseudo gamma removal ++DIS_GMUSHY=1 Disable luma anti-pseudo gamma removal ++DIS_SCRESET=0 Normal subcarrier phase resets ++DIS_YFLPF=0 Disable Luma initial hoz low pass filter ++DIV2=0 Input pixel rate not divided by 2 ++ECBAR=0 No colour bars ++ECCF1=0 Disable closed caption ++ECCF2=0 Disable closed caption ++ECCGATE=0 Normal close caption encoding ++ECLIP=0 0=disable clipping ++EN_ASYNC=0 set to 0 for normal operation ++EN_BLANKO=0 BLANK is an input ++EN_DOT=0 Disables dot clock sync on BLANK pin ++EN_OUT=1 Allows outputs to be enabled ++EN_XCLK=1 Use CLKI pin as clock source ++ESTATUS[1:0]=0 Used to select readback register ++FIELDI=0 Logical 1 on FIELD indicates even field ++F_SELC[2:0]=0 5 line chroma flickerfilter ++F_SELY[2:0]=0 5 line luma flickerfilter ++HBURST_BEGIN[7:0]=x92 146 Chroma burst start point in clocks ++HBURST_END[7:0]=x57 87 Chroma burst end point in clocks - 128 ++HSYNCI=0 Active low HSYNC ++HSYNC_WIDTH[7:0]=x80 128 Analogue sync width in clocks ++HSYNOFFSET[9:0]=0 [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0] hsync in "standard position" ++HSYNWIDTH[5:0]=2 2 pixel hsync width ++H_ACTIVE[9:0]=x2D0 720 [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0] Active pixels per line ++H_BLANKI[8:0]=x80 128 [H_BLANKI[8]=0; H_BLANKI[7:0]=x80] End of blanking of input video ++H_BLANKO[9:0]=x102 258 [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x2] End of blanking from hoz sync leading edge ++H_CLKI[10:0]=x368 872 [H_CLKI[10:8]=3; H_CLKI[7:0]=x68] Input line length total in clocks ++H_CLKO[11:0]=x6d0 1744 [H_CLKO[11:8]=6; H_CLKO[7:0]=xD0] Output clocks per line ++H_FRACT[7:0]=0 0 fractional input clocks per line ++IN_MODE[2:0]=0 24Bit RGB muxed ++LUMADLY[1:0]=0 0 pixel delay on Y_DLY luma ++MCB[7:0]=x43 67 Mult factor for CB prior to subcarrier mod. ++MCR[7:0]=x77 119 Mult factor for CR prior to subcarrier mod. ++MODE2X=0 Don't divide clock input by 2 ++MSC[31:0]=x215282E5 559055589 [MSC[31:24]=x21; MSC[23:16]=x52; MSC[15:8]=x82; MSC[7:0]=xE5] Subcarrier incr. ++MY[7:0]=x85 133 Mult factor for Y ++NI_OUT=0 Normal interlaced output ++OUT_MODE[1:0]=0 video0-3 is CVBS, Y, C, Y_DLY ++OUT_MUXA[1:0]=0 Don't care as DACA is disabled ++OUT_MUXB[1:0]=1 Output video[1] (Y) on DACB ++OUT_MUXC[1:0]=2 Output video[2] (C) on DACC ++PAL_MD=0 Video output in PAL mode? No. ++PHASE_OFF[7:0]=0 Subcarrier phase offset ++PLL_FRACT[15:0]=x30 48 [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier ++PLL_INT[5:0]=0x0C 12 Int portion of pll multiplier ++SETUP=1 7.5-IRE enabled for NTSC ++SLAVER=1 ++SRESET=0 Don't do a software reset ++SYNC_AMP[7:0]=xE5 229 Sync amp mult. factor (PAL std???) ++VBLANKDLY=0 Extra line of blanking in 2nd field? ++VSYNCI=0 Active low VSYNC ++VSYNC_DUR=1 2.5line VSYNC duration on output (Yes for NTSC) ++VSYNCOFFSET[10:0]=0 [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0] VSYNC in standard position ++VSYNWIDTH[2:0]=1 1 line of vsync width ++V_ACTIVEI[9:0]=x1E0 480 [V_ACTIVEI[9:0]=1; V_ACTIVEI[7:0]=xE0] Active input lines ++V_ACTIVEO[8:0]=xF0 240 [V_ACTIVE0[8]=0; V_ACTIVEO[7:0]=xF0] ++V_BLANKI[7:0]=x2A 42 Input lines from vsync to first active line ++V_BLANKO[7:0]=x16 22 ++V_LINESI[9:0]=x20D 525 [V_LINESI[9:8]=2; V_LINESI[7:0]=x0D] Number of input lines ++V_SCALE[13:0]=x1000 4096 [V_SCALE[13:8]=x10; V_SCALE[7:0]=0] Vert scale coefficient="none"? ++YATTENUATE[2:0]=0 no luma attenuation ++YCORING[2:0]=0 Luma-coring bypass ++YLPF[1:0]=0 Luma hoz low pass filter=bypass ++ ++***************** */ ++ ++static u16 registers_720_480[] = ++ { ++ 0x6e, 0x00, /* HSYNOFFSET[7:0]=0 */ ++ 0x70, 0x02, /* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */ ++ 0x72, 0x00, /* VSYNOFFSET[7:0]=0 */ ++ 0x74, 0x01, /* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */ ++ 0x76, 0xD0, /* H_CLKO[7:0]=xD0 */ ++ 0x78, 0xD0, /* H_ACTIVE[7:0]=xD0 */ ++ 0x7a, 0x80, /* HSYNC_WIDTH[7:0]=x80 */ ++ 0x7c, 0x92, /* HBURST_BEGIN[7:0]=x92 */ ++ 0x7e, 0x57, /* HBURST_END[7:0]=x57 */ ++ 0x80, 0x02, /* H_BLANKO[7:0]=x2 */ ++ 0x82, 0x16, /* V_BLANKO[7:0]=x16 */ ++ 0x84, 0xF0, /* V_ACTIVEO[7:0]=xF0 */ ++ 0x86, 0x26, /* V_ACTIVE0[8]=0; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */ ++ 0x88, 0x00, /* H_FRACT[7:0]=0 */ ++ 0x8a, 0xD0, /* H_CLKI[7:0]=xD0 */ ++ 0x8c, 0x80, /* H_BLANKI[7:0]=x80 */ ++ 0x8e, 0x03, /* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */ ++ 0x90, 0x0D, /* V_LINESI[7:0]=x0D */ ++ 0x92, 0x2A, /* V_BLANKI[7:0]=x2A */ ++ 0x94, 0xE0, /* V_ACTIVEI[7:0]=xE0 */ ++ 0x96, 0x06, /* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:8]=1; V_LINESI[9:8]=2 */ ++ 0x98, 0x00, /* V_SCALE[7:0]=0 */ ++ 0x9a, 0x50, /* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */ ++ 0x9c, 0x30, /* PLL_FRACT[7:0]=x30 */ ++ 0x9e, 0x0, /* PLL_FRACT[15:8]=0x0 */ ++ 0xa0, 0x8c, /* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */ ++ 0xa2, 0x0A, /* ECLIP=0; PAL_MD=0; DIS_SCRESET=0; VSYNC_DUR=1; 625LINE=0; SETUP=1; NI_OUT=0 */ ++ 0xa4, 0xE5, /* SYNC_AMP[7:0]=xE5 */ ++ 0xa6, 0x74, /* BST_AMP[7:0]=x74 */ ++ 0xa8, 0x77, /* MCR[7:0]=x77 */ ++ 0xaa, 0x43, /* MCB[7:0]=x43 */ ++ 0xac, 0x85, /* MY[7:0]=x85 */ ++ 0xae, 0xE5, /* MSC[7:0]=xE5 */ ++ 0xb0, 0x82, /* MSC[15:8]=x82 */ ++ 0xb2, 0x52, /* MSC[23:16]=x52 */ ++ 0xb4, 0x21, /* MSC[31:24]=x21 */ ++ 0xb6, 0x00, /* PHASE_OFF[7:0]=0 */ ++ //0xba, 0x21, /* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */ ++ 0xc4, 0x01, /* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */ ++ 0xc6, 0x00, /* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */ ++ 0xc8, 0x40, /* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */ ++ 0xca, 0xc0, /* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */ ++ 0xcc, 0xc0, /* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */ ++ //0xce, 0x24, /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ ++ //0xce, 0x04, /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ ++ 0xd6, 0x00, /* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */ ++ 0, 0 ++ }; ++ ++ ++int bt869_id = 0; ++ ++static int bt869_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, bt869_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int bt869_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, cur; ++ struct i2c_client *new_client; ++ struct bt869_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ ++ printk("bt869.o: probing address %d .\n", address); ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("bt869.o: bt869_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE | ++ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access bt869_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct bt869_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &bt869_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. It is lousy. */ ++ i2c_smbus_write_byte_data(new_client, 0xC4, 0); /* set status bank 0 */ ++ cur = i2c_smbus_read_byte(new_client); ++ printk("bt869.o: address 0x%X testing-->0x%X\n", address, cur); ++ if ((cur & 0xE0) != 0x20) ++ goto ERROR1; ++ ++ /* Determine the chip type */ ++ kind = ((cur & 0x20) >> 5); ++ ++ if (kind) { ++ type_name = "bt869"; ++ client_name = "bt869 chip"; ++ printk("bt869.o: BT869 detected\n"); ++ } else { ++ type_name = "bt868"; ++ client_name = "bt868 chip"; ++ printk("bt869.o: BT868 detected\n"); ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = bt869_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ bt869_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ bt869_init_client((struct i2c_client *) new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int bt869_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct bt869_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("bt869.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* All registers are byte-sized. ++ bt869 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int bt869_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte(client); ++} ++ ++/* All registers are byte-sized. ++ bt869 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++#ifdef DEBUG ++ printk("bt869.o: write_value(0x%X, 0x%X)\n", reg, value); ++#endif ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void bt869_write_values(struct i2c_client *client, u16 *values) ++{ ++ /* writes set of registers from array. 0,0 marks end of table */ ++ while (*values) { ++ bt869_write_value(client, values[0], values[1]); ++ values += 2; ++ } ++} ++ ++static void bt869_init_client(struct i2c_client *client) ++{ ++ struct bt869_data *data = client->data; ++ ++ /* Initialize the bt869 chip */ ++ bt869_write_value(client, 0x0ba, 0x80); ++ // bt869_write_value(client,0x0D6, 0x00); ++ /* Be a slave to the clock on the Voodoo3 */ ++ bt869_write_value(client, 0xa0, 0x80); ++ bt869_write_value(client, 0xba, 0x20); ++ /* depth =16bpp */ ++ bt869_write_value(client, 0x0C6, 0x001); ++ bt869_write_value(client, 0xC4, 1); ++ /* Flicker free enable and config */ ++ bt869_write_value(client, 0xC8, 0); ++ data->res[0] = 640; ++ data->res[1] = 480; ++ data->ntsc = 1; ++ data->half = 0; ++ data->colorbars = 0; ++ data->svideo = 0; ++ data->depth = 16; ++ ++} ++ ++static void bt869_update_client(struct i2c_client *client) ++{ ++ struct bt869_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++#ifdef DEBUG ++ printk("Starting bt869 update\n"); ++#endif ++ if ((data->res[0] == 800) && (data->res[1] == 600)) { ++ /* 800x600 built-in mode */ ++ bt869_write_value(client, 0xB8, ++ (2 + (!data->ntsc))); ++ bt869_write_value(client, 0xa0, 0x80 + 0x11); ++ printk("bt869.o: writing into config -->0x%X\n", ++ (2 + (!data->ntsc))); ++ } ++ else if ((data->res[0] == 720) && (data->res[1] == 576)) { ++ /* 720x576 no-overscan-compensation mode suitable for PAL DVD playback */ ++ data->ntsc = 0; /* This mode always PAL */ ++ bt869_write_values(client, registers_720_576); ++ } ++ else if ((data->res[0] == 720) && (data->res[1] == 480)) { ++ /* 720x480 no-overscan-compensation mode suitable for NTSC DVD playback */ ++ data->ntsc = 1; /* This mode always NTSC */ ++ bt869_write_values(client, registers_720_480); ++ } ++ else { ++ /* 640x480 built-in mode */ ++ bt869_write_value(client, 0xB8, (!data->ntsc)); ++ bt869_write_value(client, 0xa0, 0x80 + 0x0C); ++ printk("bt869.o: writing into config -->0x%X\n", ++ (0 + (!data->ntsc))); ++ if ((data->res[0] != 640) || (data->res[1] != 480)) { ++ printk ++ ("bt869.o: Warning: arbitrary resolutions not supported yet. Using 640x480.\n"); ++ data->res[0] = 640; ++ data->res[1] = 480; ++ } ++ } ++ /* Set colour depth */ ++ if ((data->depth != 24) && (data->depth != 16)) ++ data->depth = 16; ++ if (data->depth == 16) ++ bt869_write_value(client, 0x0C6, 0x001); ++ if (data->depth == 24) ++ bt869_write_value(client, 0x0C6, 0x000); ++ /* set "half" resolution mode */ ++ bt869_write_value(client, 0xd4, data->half << 6); ++ /* Set composite/svideo mode, also enable the right dacs */ ++ switch (data->svideo) { ++ case 2: /* RGB */ ++ /* requires hardware mod on Voodoo3 to get all outputs, ++ untested in practice... Feedback to steve@daviesfam.org please */ ++ bt869_write_value(client, 0xd6, 0x0c); ++ bt869_write_value(client, 0xce, 0x24); ++ bt869_write_value(client, 0xba, 0x20); ++ break; ++ case 1: /* Svideo*/ ++ bt869_write_value(client, 0xce, 0x24); ++ bt869_write_value(client, 0xba, 0x21); ++ break; ++ default: /* Composite */ ++ bt869_write_value(client, 0xce, 0x0); ++ bt869_write_value(client, 0xba, 0x21); ++ break; ++ } ++ /* Enable outputs */ ++ bt869_write_value(client, 0xC4, 1); ++ /* Issue timing reset */ ++ bt869_write_value(client, 0x6c, 0x80); ++ ++/* Read back status registers */ ++ bt869_write_value(client, 0xC4, ++ 1 | (data->colorbars << 2)); ++ data->status[0] = bt869_read_value(client, 1); ++ bt869_write_value(client, 0xC4, ++ 0x41 | (data->colorbars << 2)); ++ data->status[1] = bt869_read_value(client, 1); ++ bt869_write_value(client, 0xC4, ++ 0x81 | (data->colorbars << 2)); ++ data->status[2] = bt869_read_value(client, 1); ++ bt869_write_value(client, 0xC4, ++ 0x0C1 | (data->colorbars << 2)); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ up(&data->update_lock); ++} ++ ++ ++void bt869_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->status[0]; ++ results[1] = data->status[1]; ++ results[2] = data->status[2]; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk ++ ("bt869.o: Warning: write was requested on read-only proc file: status\n"); ++ } ++} ++ ++ ++void bt869_ntsc(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->ntsc; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->ntsc = (results[0] > 0); ++ } ++ bt869_update_client(client); ++ } ++} ++ ++ ++void bt869_svideo(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->svideo; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->svideo = results[0]; ++ } ++ bt869_update_client(client); ++ } ++} ++ ++ ++void bt869_res(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->res[0]; ++ results[1] = data->res[1]; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->res[0] = results[0]; ++ } ++ if (*nrels_mag >= 2) { ++ data->res[1] = results[1]; ++ } ++ bt869_update_client(client); ++ } ++} ++ ++ ++void bt869_half(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->half; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->half = (results[0] > 0); ++ bt869_update_client(client); ++ } ++ } ++} ++ ++void bt869_colorbars(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->colorbars; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->colorbars = (results[0] > 0); ++ bt869_update_client(client); ++ } ++ } ++} ++ ++void bt869_depth(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct bt869_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ bt869_update_client(client); ++ results[0] = data->depth; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->depth = results[0]; ++ bt869_update_client(client); ++ } ++ } ++} ++ ++static int __init sm_bt869_init(void) ++{ ++ printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&bt869_driver); ++} ++ ++static void __exit sm_bt869_exit(void) ++{ ++ i2c_del_driver(&bt869_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard , Philip Edelbrock , Stephen Davies "); ++MODULE_DESCRIPTION("bt869 driver"); ++ ++module_init(sm_bt869_init); ++module_exit(sm_bt869_exit); +--- linux-old/drivers/sensors/ddcmon.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/ddcmon.c Mon Dec 13 20:18:45 2004 +@@ -0,0 +1,591 @@ ++/* ++ ddcmon.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999, 2000 Frodo Looijaard , ++ Philip Edelbrock , ++ and Mark Studebaker ++ Copyright (c) 2003 Jean Delvare ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x50, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(ddcmon); ++ ++static int checksum = 0; ++MODULE_PARM(checksum, "i"); ++MODULE_PARM_DESC(checksum, "Only accept eeproms whose checksum is correct"); ++ ++/* Many constants specified below */ ++ ++/* DDCMON registers */ ++/* vendor section */ ++#define DDCMON_REG_MAN_ID 0x08 ++#define DDCMON_REG_PROD_ID 0x0A ++#define DDCMON_REG_SERIAL 0x0C ++#define DDCMON_REG_WEEK 0x10 ++#define DDCMON_REG_YEAR 0x11 ++/* EDID version */ ++#define DDCMON_REG_EDID_VER 0x12 ++#define DDCMON_REG_EDID_REV 0x13 ++/* display information */ ++#define DDCMON_REG_HORSIZE 0x15 ++#define DDCMON_REG_VERSIZE 0x16 ++#define DDCMON_REG_GAMMA 0x17 ++#define DDCMON_REG_DPMS_FLAGS 0x18 ++/* supported timings */ ++#define DDCMON_REG_ESTABLISHED_TIMINGS 0x23 ++#define DDCMON_REG_STANDARD_TIMINGS 0x26 ++#define DDCMON_REG_TIMBASE 0x36 ++#define DDCMON_REG_TIMINCR 18 ++#define DDCMON_REG_TIMNUM 4 ++ ++#define DDCMON_REG_CHECKSUM 0x7f ++ ++/* Size of DDCMON in bytes */ ++#define DDCMON_SIZE 128 ++ ++/* Each client has this additional data */ ++struct ddcmon_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 data[DDCMON_SIZE]; /* Register values */ ++}; ++ ++ ++static int ddcmon_attach_adapter(struct i2c_adapter *adapter); ++static int ddcmon_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int ddcmon_detach_client(struct i2c_client *client); ++ ++static void ddcmon_idcall(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_size(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_sync(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_maxclock(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_timings(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_serial(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_time(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_edid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_gamma(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_dpms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_standard_timing(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ddcmon_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver ddcmon_driver = { ++ .owner = THIS_MODULE, ++ .name = "DDCMON READER", ++ .id = I2C_DRIVERID_DDCMON, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = ddcmon_attach_adapter, ++ .detach_client = ddcmon_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define DDCMON_SYSCTL_ID 1010 ++#define DDCMON_SYSCTL_SIZE 1011 ++#define DDCMON_SYSCTL_SYNC 1012 ++#define DDCMON_SYSCTL_TIMINGS 1013 ++#define DDCMON_SYSCTL_SERIAL 1014 ++#define DDCMON_SYSCTL_TIME 1015 ++#define DDCMON_SYSCTL_EDID 1016 ++#define DDCMON_SYSCTL_GAMMA 1017 ++#define DDCMON_SYSCTL_DPMS 1018 ++#define DDCMON_SYSCTL_TIMING1 1021 ++#define DDCMON_SYSCTL_TIMING2 1022 ++#define DDCMON_SYSCTL_TIMING3 1023 ++#define DDCMON_SYSCTL_TIMING4 1024 ++#define DDCMON_SYSCTL_TIMING5 1025 ++#define DDCMON_SYSCTL_TIMING6 1026 ++#define DDCMON_SYSCTL_TIMING7 1027 ++#define DDCMON_SYSCTL_TIMING8 1028 ++#define DDCMON_SYSCTL_MAXCLOCK 1029 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected DDCMON. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table ddcmon_dir_table_template[] = { ++ {DDCMON_SYSCTL_ID, "id", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &ddcmon_idcall}, ++ {DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &ddcmon_size}, ++ {DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_sync}, ++ {DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_timings}, ++ {DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_serial}, ++ {DDCMON_SYSCTL_TIME, "time", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_time}, ++ {DDCMON_SYSCTL_EDID, "edid", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_edid}, ++ {DDCMON_SYSCTL_GAMMA, "gamma", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_gamma}, ++ {DDCMON_SYSCTL_DPMS, "dpms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_dpms}, ++ {DDCMON_SYSCTL_TIMING1, "timing1", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING2, "timing2", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING3, "timing3", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING4, "timing4", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING5, "timing5", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING6, "timing6", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING7, "timing7", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_TIMING8, "timing8", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_standard_timing}, ++ {DDCMON_SYSCTL_MAXCLOCK, "maxclock", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_maxclock}, ++ {0} ++}; ++ ++static int ddcmon_id = 0; ++ ++static int ddcmon_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, ddcmon_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int ddcmon_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, cs; ++ struct i2c_client *new_client; ++ struct ddcmon_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access ddcmon_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct ddcmon_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ memset(data->data, 0xff, DDCMON_SIZE); ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &ddcmon_driver; ++ new_client->flags = 0; ++ ++ /* prevent 24RF08 corruption (just in case) */ ++ i2c_smbus_write_quick(new_client, 0); ++ ++ /* Now, we do the remaining detection. */ ++ if (checksum) { ++ int cs = 0; ++ for (i = 0; i < 0x80; i++) ++ cs += i2c_smbus_read_byte_data(new_client, i); ++ if ((cs & 0xff) != 0) ++ goto ERROR1; ++ } ++ ++ /* Verify the first 8 locations 0x00FFFFFFFFFFFF00 */ ++ /* Allow force and force_ddcmon arguments */ ++ if(kind < 0) ++ { ++ for(i = 0; i < 8; i++) { ++ cs = i2c_smbus_read_byte_data(new_client, i); ++ if(i == 0 || i == 7) { ++ if(cs != 0) ++ goto ERROR1; ++ } else if(cs != 0xff) ++ goto ERROR1; ++ } ++ } ++ ++ type_name = "ddcmon"; ++ client_name = "DDC Monitor"; ++ ++ /* Fill in the remaining client fields and put it in the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = ddcmon_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ ddcmon_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int ddcmon_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct ddcmon_data *) (client->data))-> ++ sysctl_id); ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("ddcmon.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ kfree(client->data); ++ return 0; ++} ++ ++static void ddcmon_update_client(struct i2c_client *client) ++{ ++ struct ddcmon_data *data = client->data; ++ int i, j; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > 300 * HZ) || ++ (jiffies < data->last_updated) || !data->valid) { ++ if (i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) ++ { ++ for (i=0; idata + i) ++ != I2C_SMBUS_I2C_BLOCK_MAX) { ++ printk(KERN_WARNING "ddcmon.o: block read fail at 0x%.2x!\n", i); ++ goto DONE; ++ } ++ } else { ++ if (i2c_smbus_write_byte(client, 0)) { ++ printk(KERN_WARNING "ddcmon.o: read start fail at 0!\n"); ++ goto DONE; ++ } ++ for (i = 0; i < DDCMON_SIZE; i++) { ++ j = i2c_smbus_read_byte(client); ++ if (j < 0) { ++ printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i); ++ goto DONE; ++ } ++ data->data[i] = (u8) j; ++ } ++ } ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++DONE: ++ up(&data->update_lock); ++} ++ ++ ++void ddcmon_idcall(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_MAN_ID + 1] | ++ (data->data[DDCMON_REG_MAN_ID] << 8); ++ results[1] = data->data[DDCMON_REG_PROD_ID + 1] | ++ (data->data[DDCMON_REG_PROD_ID] << 8); ++ *nrels_mag = 2; ++ } ++} ++ ++void ddcmon_size(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_VERSIZE]; ++ results[1] = data->data[DDCMON_REG_HORSIZE]; ++ *nrels_mag = 2; ++ } ++} ++ ++void ddcmon_sync(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ int i, j; ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ *nrels_mag = 4; ++ /* look for monitor limits entry */ ++ for(i = DDCMON_REG_TIMBASE; ++ i < DDCMON_REG_TIMBASE + ++ (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR); ++ i += DDCMON_REG_TIMINCR) { ++ if (data->data[i] == 0x00 ++ && data->data[i + 1] == 0x00 ++ && data->data[i + 2] == 0x00 ++ && data->data[i + 3] == 0xfd) { ++ for(j = 0; j < 4; j++) ++ results[j] = data->data[i + j + 5]; ++ return; ++ } ++ } ++ for(j = 0; j < 4; j++) ++ results[j] = 0; ++ } ++} ++ ++void ddcmon_maxclock(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ int i; ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ *nrels_mag = 1; ++ /* look for monitor limits entry */ ++ for(i = DDCMON_REG_TIMBASE; ++ i < DDCMON_REG_TIMBASE + ++ (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR); ++ i += DDCMON_REG_TIMINCR) { ++ if (data->data[i] == 0x00 ++ && data->data[i + 1] == 0x00 ++ && data->data[i + 2] == 0x00 ++ && data->data[i + 3] == 0xfd) { ++ results[0] = (data->data[i + 9] == 0xff ? ++ 0 : data->data[i + 9] * 10); ++ return; ++ } ++ } ++ results[0] = 0; ++ } ++} ++ ++void ddcmon_timings(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_ESTABLISHED_TIMINGS] | ++ (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 1] << 8) | ++ (data->data[DDCMON_REG_ESTABLISHED_TIMINGS + 2] << 16); ++ *nrels_mag = 1; ++ } ++} ++ ++void ddcmon_serial(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_SERIAL] | ++ (data->data[DDCMON_REG_SERIAL + 1] << 8) | ++ (data->data[DDCMON_REG_SERIAL + 2] << 16) | ++ (data->data[DDCMON_REG_SERIAL + 3] << 24); ++ *nrels_mag = 1; ++ } ++} ++ ++void ddcmon_time(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_YEAR] + 1990; ++ results[1] = data->data[DDCMON_REG_WEEK]; ++ *nrels_mag = 2; ++ } ++} ++ ++void ddcmon_edid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_EDID_VER]; ++ results[1] = data->data[DDCMON_REG_EDID_REV]; ++ *nrels_mag = 2; ++ } ++} ++ ++void ddcmon_gamma(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = 100 + data->data[DDCMON_REG_GAMMA]; ++ *nrels_mag = 1; ++ } ++} ++ ++void ddcmon_dpms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ results[0] = data->data[DDCMON_REG_DPMS_FLAGS]; ++ *nrels_mag = 1; ++ } ++} ++ ++void ddcmon_standard_timing(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct ddcmon_data *data = client->data; ++ int nr = ctl_name - DDCMON_SYSCTL_TIMING1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ddcmon_update_client(client); ++ /* If both bytes of the timing are 0x00 or 0x01, then the timing ++ slot is unused. */ ++ if ((data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2] ++ | data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1]) & 0xfe) { ++ results[0] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2] + 31) * 8; ++ switch (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] >> 6) { ++ /* We don't care about rounding issues there, it really ++ should be OK without it. */ ++ case 0x00: ++ results[1] = results[0]; /* unconfirmed */ ++ break; ++ case 0x01: ++ results[1] = results[0] * 3 / 4; ++ break; ++ case 0x02: ++ results[1] = results[0] * 4 / 5; ++ break; ++ case 0x03: ++ results[1] = results[0] * 9 / 16; ++ break; ++ } ++ results[2] = (data->data[DDCMON_REG_STANDARD_TIMINGS + nr * 2 + 1] & 0x3f) + 60; ++ } else { ++ results[0] = 0; ++ results[1] = 0; ++ results[2] = 0; ++ } ++ *nrels_mag = 3; ++ } ++} ++ ++static int __init sm_ddcmon_init(void) ++{ ++ printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&ddcmon_driver); ++} ++ ++static void __exit sm_ddcmon_exit(void) ++{ ++ i2c_del_driver(&ddcmon_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "Mark Studebaker " ++ "and Jean Delvare "); ++MODULE_DESCRIPTION("DDCMON driver"); ++ ++module_init(sm_ddcmon_init); ++module_exit(sm_ddcmon_exit); +--- linux-old/drivers/sensors/ds1621.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/ds1621.c Mon Dec 13 20:18:45 2004 +@@ -0,0 +1,528 @@ ++/* ++ ds1621.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Christian W. Zuckschwerdt 2000-11-23 ++ based on lm75.c by Frodo Looijaard ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports DS1621. See doc/chips/ds1621 for details */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(ds1621); ++ ++/* Many DS1621 constants specified below */ ++ ++/* Config register used for detection */ ++/* 7 6 5 4 3 2 1 0 */ ++/* |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT| */ ++#define DS1621_REG_CONFIG_MASK 0x0C ++#define DS1621_REG_CONFIG_VAL 0x08 ++#define DS1621_REG_CONFIG_POLARITY 0x02 ++#define DS1621_REG_CONFIG_1SHOT 0x01 ++#define DS1621_REG_CONFIG_DONE 0x80 ++ ++/* Note: the done bit is always unset if continuous conversion is in progress. ++ We need to stop the continuous conversion or switch to single shot ++ before this bit becomes available! ++ */ ++ ++/* The DS1621 registers */ ++#define DS1621_REG_TEMP 0xAA /* word, RO */ ++#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */ ++#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */ ++#define DS1621_REG_CONF 0xAC /* byte, RW */ ++#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */ ++#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */ ++#define DS1621_COM_START 0xEE /* no data */ ++#define DS1621_COM_STOP 0x22 /* no data */ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | \ ++ ((val & 0x8000)?-256:0)) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0 ? (0x200+((val)/5))<<7 : \ ++ (((val) + 2) / 5) << 7),0,0xffff)) ++#define ALARMS_FROM_REG(val) ((val) & \ ++ (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) ++#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \ ++ ((val & 0x8000)?-256:0)) ++ ++/* Each client has this additional data */ ++struct ds1621_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u16 temp, temp_over, temp_hyst; /* Register values, word */ ++ u8 conf; /* Register encoding, combined */ ++ ++ char enable; /* !=0 if we're expected to restart the conversion */ ++ u8 temp_int, temp_counter, temp_slope; /* Register values, byte */ ++}; ++ ++static int ds1621_attach_adapter(struct i2c_adapter *adapter); ++static int ds1621_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void ds1621_init_client(struct i2c_client *client); ++static int ds1621_detach_client(struct i2c_client *client); ++ ++static int ds1621_read_value(struct i2c_client *client, u8 reg); ++static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value); ++static void ds1621_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ds1621_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ds1621_enable(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ds1621_continuous(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ds1621_polarity(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void ds1621_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver ds1621_driver = { ++ .owner = THIS_MODULE, ++ .name = "DS1621 sensor driver", ++ .id = I2C_DRIVERID_DS1621, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = ds1621_attach_adapter, ++ .detach_client = ds1621_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define DS1621_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ ++#define DS1621_SYSCTL_ALARMS 2001 /* bitvector */ ++#define DS1621_ALARM_TEMP_HIGH 0x40 ++#define DS1621_ALARM_TEMP_LOW 0x20 ++#define DS1621_SYSCTL_ENABLE 2002 ++#define DS1621_SYSCTL_CONTINUOUS 2003 ++#define DS1621_SYSCTL_POLARITY 2004 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected DS1621. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table ds1621_dir_table_template[] = { ++ {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp}, ++ {DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms}, ++ {DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable}, ++ {DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous}, ++ {DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity}, ++ {0} ++}; ++ ++static int ds1621_id = 0; ++ ++static int ds1621_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, ds1621_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int ds1621_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, conf; ++ struct i2c_client *new_client; ++ struct ds1621_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_WRITE_BYTE)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access ds1621_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &ds1621_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. It is lousy. */ ++ if (kind < 0) { ++ conf = i2c_smbus_read_byte_data(new_client, ++ DS1621_REG_CONF); ++ if ((conf & DS1621_REG_CONFIG_MASK) ++ != DS1621_REG_CONFIG_VAL) ++ goto ERROR1; ++ } ++ ++ /* Determine the chip type - only one kind supported! */ ++ if (kind <= 0) ++ kind = ds1621; ++ ++ if (kind == ds1621) { ++ type_name = "ds1621"; ++ client_name = "DS1621 chip"; ++ } else { ++#ifdef DEBUG ++ printk("ds1621.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = ds1621_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ ds1621_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ ds1621_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int ds1621_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct ds1621_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("ds1621.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* All registers are word-sized, except for the configuration register. ++ DS1621 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int ds1621_read_value(struct i2c_client *client, u8 reg) ++{ ++ if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) ++ || (reg == DS1621_REG_TEMP_SLOPE)) ++ return i2c_smbus_read_byte_data(client, reg); ++ else ++ return swab16(i2c_smbus_read_word_data(client, reg)); ++} ++ ++/* All registers are word-sized, except for the configuration register. ++ DS1621 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if ( (reg == DS1621_COM_START) || (reg == DS1621_COM_STOP) ) ++ return i2c_smbus_write_byte(client, reg); ++ else ++ if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) ++ || (reg == DS1621_REG_TEMP_SLOPE)) ++ return i2c_smbus_write_byte_data(client, reg, value); ++ else ++ return i2c_smbus_write_word_data(client, reg, swab16(value)); ++} ++ ++static void ds1621_init_client(struct i2c_client *client) ++{ ++ int reg; ++ ++ reg = ds1621_read_value(client, DS1621_REG_CONF); ++ /* start the continous conversion */ ++ if(reg & 0x01) ++ ds1621_write_value(client, DS1621_REG_CONF, reg & 0xfe); ++} ++ ++static void ds1621_update_client(struct i2c_client *client) ++{ ++ struct ds1621_data *data = client->data; ++ u8 new_conf; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting ds1621 update\n"); ++#endif ++ ++ data->conf = ds1621_read_value(client, DS1621_REG_CONF); ++ ++ data->temp = ds1621_read_value(client, ++ DS1621_REG_TEMP); ++ data->temp_over = ds1621_read_value(client, ++ DS1621_REG_TEMP_OVER); ++ data->temp_hyst = ds1621_read_value(client, ++ DS1621_REG_TEMP_HYST); ++ ++ /* wait for the DONE bit before reading extended values */ ++ ++ if (data->conf & DS1621_REG_CONFIG_DONE) { ++ data->temp_counter = ds1621_read_value(client, ++ DS1621_REG_TEMP_COUNTER); ++ data->temp_slope = ds1621_read_value(client, ++ DS1621_REG_TEMP_SLOPE); ++ data->temp_int = ITEMP_FROM_REG(data->temp); ++ /* restart the conversion */ ++ if (data->enable) ++ ds1621_write_value(client, DS1621_COM_START, 0); ++ } ++ ++ /* reset alarms if neccessary */ ++ new_conf = data->conf; ++ if (data->temp < data->temp_over) ++ new_conf &= ~DS1621_ALARM_TEMP_HIGH; ++ if (data->temp > data->temp_hyst) ++ new_conf &= ~DS1621_ALARM_TEMP_LOW; ++ if (data->conf != new_conf) ++ ds1621_write_value(client, DS1621_REG_CONF, ++ new_conf); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void ds1621_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct ds1621_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ if (!(data->conf & DS1621_REG_CONFIG_DONE) || ++ (data->temp_counter > data->temp_slope) || ++ (data->temp_slope == 0)) { ++ *nrels_mag = 1; ++ } else { ++ *nrels_mag = 2; ++ } ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ds1621_update_client(client); ++ /* decide wether to calculate more precise temp */ ++ if (!(data->conf & DS1621_REG_CONFIG_DONE) || ++ (data->temp_counter > data->temp_slope) || ++ (data->temp_slope == 0)) { ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ } else { ++ results[0] = TEMP_FROM_REG(data->temp_over)*10; ++ results[1] = TEMP_FROM_REG(data->temp_hyst)*10; ++ results[2] = data->temp_int * 100 - 25 + ++ ((data->temp_slope - data->temp_counter) * ++ 100 / data->temp_slope); ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ ds1621_write_value(client, DS1621_REG_TEMP_OVER, ++ data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ ds1621_write_value(client, DS1621_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct ds1621_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ds1621_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->conf); ++ *nrels_mag = 1; ++ } ++} ++ ++void ds1621_enable(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ /* If you really screw up your chip (like I did) this is */ ++ /* sometimes needed to (re)start the continous conversion */ ++ /* there is no data to read so this might hang your SMBus! */ ++ ++ struct ds1621_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ds1621_update_client(client); ++ results[0] = !(data->conf & DS1621_REG_CONFIG_DONE); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (results[0]) { ++ ds1621_write_value(client, DS1621_COM_START, 0); ++ data->enable=1; ++ } else { ++ ds1621_write_value(client, DS1621_COM_STOP, 0); ++ data->enable=0; ++ } ++ } else { ++ ds1621_write_value(client, DS1621_COM_START, 0); ++ data->enable=1; ++ } ++ } ++} ++ ++void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct ds1621_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ds1621_update_client(client); ++ results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ ds1621_update_client(client); ++ if (*nrels_mag >= 1) { ++ if (results[0]) { ++ ds1621_write_value(client, DS1621_REG_CONF, ++ data->conf & ~DS1621_REG_CONFIG_1SHOT); ++ } else { ++ ds1621_write_value(client, DS1621_REG_CONF, ++ data->conf | DS1621_REG_CONFIG_1SHOT); ++ } ++ } else { ++ ds1621_write_value(client, DS1621_REG_CONF, ++ data->conf & ~DS1621_REG_CONFIG_1SHOT); ++ } ++ } ++} ++ ++void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct ds1621_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ ds1621_update_client(client); ++ results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY)); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ ds1621_update_client(client); ++ if (*nrels_mag >= 1) { ++ if (results[0]) { ++ ds1621_write_value(client, DS1621_REG_CONF, ++ data->conf | DS1621_REG_CONFIG_POLARITY); ++ } else { ++ ds1621_write_value(client, DS1621_REG_CONF, ++ data->conf & ~DS1621_REG_CONFIG_POLARITY); ++ } ++ } ++ } ++} ++ ++static int __init sm_ds1621_init(void) ++{ ++ printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&ds1621_driver); ++} ++ ++static void __exit sm_ds1621_exit(void) ++{ ++ i2c_del_driver(&ds1621_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Christian W. Zuckschwerdt "); ++MODULE_DESCRIPTION("DS1621 driver"); ++ ++module_init(sm_ds1621_init); ++module_exit(sm_ds1621_exit); +--- linux-old/drivers/sensors/eeprom.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/eeprom.c Mon Dec 13 20:18:45 2004 +@@ -0,0 +1,418 @@ ++/* ++ eeprom.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard and ++ Philip Edelbrock ++ ++ 2003-08-18 Jean Delvare ++ Divide the eeprom in 2-row (arbitrary) slices. This significantly ++ speeds sensors up, as well as various scripts using the eeprom ++ module. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include /* for capable() */ ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x50, 0x57, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(eeprom); ++ ++static int checksum = 0; ++MODULE_PARM(checksum, "i"); ++MODULE_PARM_DESC(checksum, ++ "Only accept eeproms whose checksum is correct"); ++ ++ ++/* Many constants specified below */ ++ ++/* EEPROM registers */ ++#define EEPROM_REG_CHECKSUM 0x3f ++ ++/* possible natures */ ++#define NATURE_UNKNOWN 0 ++#define NATURE_VAIO 1 ++ ++/* Size of EEPROM in bytes */ ++#define EEPROM_SIZE 256 ++ ++/* Each client has this additional data */ ++struct eeprom_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ u8 valid; /* bitfield, bit!=0 if slice is valid */ ++ unsigned long last_updated[8]; /* In jiffies, 8 slices */ ++ ++ u8 data[EEPROM_SIZE]; /* Register values */ ++ u8 nature; ++}; ++ ++ ++static int eeprom_attach_adapter(struct i2c_adapter *adapter); ++static int eeprom_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int eeprom_detach_client(struct i2c_client *client); ++ ++#if 0 ++static int eeprom_write_value(struct i2c_client *client, u8 reg, ++ u8 value); ++#endif ++ ++static void eeprom_contents(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void eeprom_update_client(struct i2c_client *client, u8 slice); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver eeprom_driver = { ++ .owner = THIS_MODULE, ++ .name = "EEPROM READER", ++ .id = I2C_DRIVERID_EEPROM, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = eeprom_attach_adapter, ++ .detach_client = eeprom_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define EEPROM_SYSCTL1 1000 ++#define EEPROM_SYSCTL2 1001 ++#define EEPROM_SYSCTL3 1002 ++#define EEPROM_SYSCTL4 1003 ++#define EEPROM_SYSCTL5 1004 ++#define EEPROM_SYSCTL6 1005 ++#define EEPROM_SYSCTL7 1006 ++#define EEPROM_SYSCTL8 1007 ++#define EEPROM_SYSCTL9 1008 ++#define EEPROM_SYSCTL10 1009 ++#define EEPROM_SYSCTL11 1010 ++#define EEPROM_SYSCTL12 1011 ++#define EEPROM_SYSCTL13 1012 ++#define EEPROM_SYSCTL14 1013 ++#define EEPROM_SYSCTL15 1014 ++#define EEPROM_SYSCTL16 1015 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected EEPROM. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table eeprom_dir_table_template[] = { ++ {EEPROM_SYSCTL1, "00", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL2, "10", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL3, "20", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL4, "30", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL5, "40", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL6, "50", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL7, "60", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL8, "70", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL9, "80", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL10, "90", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL11, "a0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL12, "b0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL13, "c0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL14, "d0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL15, "e0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {EEPROM_SYSCTL16, "f0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &eeprom_contents}, ++ {0} ++}; ++ ++static int eeprom_id = 0; ++ ++static int eeprom_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, eeprom_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int eeprom_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct eeprom_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("eeprom.o: eeprom_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access eeprom_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ memset(data->data, 0xff, EEPROM_SIZE); ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &eeprom_driver; ++ new_client->flags = 0; ++ ++ /* prevent 24RF08 corruption */ ++ i2c_smbus_write_quick(new_client, 0); ++ ++ /* Now, we do the remaining detection. It is not there, unless you force ++ the checksum to work out. */ ++ if (checksum) { ++ int cs = 0; ++ for (i = 0; i <= 0x3e; i++) ++ cs += i2c_smbus_read_byte_data(new_client, i); ++ cs &= 0xff; ++ if (i2c_smbus_read_byte_data ++ (new_client, EEPROM_REG_CHECKSUM) != cs) ++ goto ERROR1; ++ } ++ ++ data->nature = NATURE_UNKNOWN; ++ /* Detect the Vaio nature of EEPROMs. ++ We use the "PCG-" prefix as the signature. */ ++ if (address == 0x57) ++ { ++ if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P' ++ && i2c_smbus_read_byte_data(new_client, 0x81) == 'C' ++ && i2c_smbus_read_byte_data(new_client, 0x82) == 'G' ++ && i2c_smbus_read_byte_data(new_client, 0x83) == '-') ++ data->nature = NATURE_VAIO; ++ } ++ ++ /* Determine the chip type - only one kind supported! */ ++ if (kind <= 0) ++ kind = eeprom; ++ ++ if (kind == eeprom) { ++ type_name = "eeprom"; ++ client_name = "EEPROM chip"; ++ } else { ++#ifdef DEBUG ++ printk("eeprom.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = eeprom_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ eeprom_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int eeprom_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct eeprom_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("eeprom.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++#if 0 ++/* No writes yet (PAE) */ ++static int eeprom_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++#endif ++ ++static void eeprom_update_client(struct i2c_client *client, u8 slice) ++{ ++ struct eeprom_data *data = client->data; ++ int i, j; ++ ++ down(&data->update_lock); ++ ++ if (!(data->valid & (1 << slice)) ++ || (jiffies - data->last_updated[slice] > 300 * HZ) ++ || (jiffies < data->last_updated[slice])) { ++ ++#ifdef DEBUG ++ printk("Starting eeprom update, slice %u\n", slice); ++#endif ++ ++ if (i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) ++ { ++ for (i = slice << 5; i < (slice + 1) << 5; ++ i += I2C_SMBUS_I2C_BLOCK_MAX) ++ if (i2c_smbus_read_i2c_block_data(client, ++ i, data->data + i) ++ != I2C_SMBUS_I2C_BLOCK_MAX) { ++ printk(KERN_WARNING "eeprom.o: block read fail at 0x%.2x!\n", i); ++ goto DONE; ++ } ++ } else { ++ if (i2c_smbus_write_byte(client, slice << 5)) { ++ printk(KERN_WARNING "eeprom.o: read start fail at 0x%.2x!\n", slice << 5); ++ goto DONE; ++ } ++ for (i = slice << 5; i < (slice + 1) << 5; i++) { ++ j = i2c_smbus_read_byte(client); ++ if (j < 0) { ++ printk(KERN_WARNING "eeprom.o: read fail at 0x%.2x!\n", i); ++ goto DONE; ++ } ++ data->data[i] = (u8) j; ++ } ++ } ++ data->last_updated[slice] = jiffies; ++ data->valid |= (1 << slice); ++ } ++DONE: ++ up(&data->update_lock); ++} ++ ++ ++void eeprom_contents(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ int i; ++ int nr = ctl_name - EEPROM_SYSCTL1; ++ struct eeprom_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ eeprom_update_client(client, nr >> 1); ++ /* Hide Vaio security settings to regular users */ ++ if (nr == 0 && data->nature == NATURE_VAIO ++ && !capable(CAP_SYS_ADMIN)) ++ for (i = 0; i < 16; i++) ++ results[i] = 0; ++ else ++ for (i = 0; i < 16; i++) ++ results[i] = data->data[i + nr * 16]; ++#ifdef DEBUG ++ printk("eeprom.o: 0x%X EEPROM contents (row %d):", ++ client->addr, nr + 1); ++ if (nr == 0 && data->nature == NATURE_VAIO) ++ printk(" \n"); ++ else { ++ for (i = 0; i < 16; i++) ++ printk(" 0x%02X", data->data[i + nr * 16]); ++ printk("\n"); ++ } ++#endif ++ *nrels_mag = 16; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ ++/* No writes to the EEPROM (yet, anyway) (PAE) */ ++ printk("eeprom.o: No writes to EEPROMs supported!\n"); ++ } ++} ++ ++static int __init sm_eeprom_init(void) ++{ ++ printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&eeprom_driver); ++} ++ ++static void __exit sm_eeprom_exit(void) ++{ ++ i2c_del_driver(&eeprom_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("EEPROM driver"); ++ ++module_init(sm_eeprom_init); ++module_exit(sm_eeprom_exit); +--- linux-old/drivers/sensors/fscpos.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/fscpos.c Mon Dec 13 20:18:46 2004 +@@ -0,0 +1,690 @@ ++/* ++ fscpos.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2001 Hermann Jung ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ fujitsu siemens poseidon chip, ++ module based on lm80.c ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ and Philip Edelbrock ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(fscpos); ++ ++/* The FSCPOS registers */ ++ ++/* chip identification */ ++#define FSCPOS_REG_IDENT_0 0x00 ++#define FSCPOS_REG_IDENT_1 0x01 ++#define FSCPOS_REG_IDENT_2 0x02 ++#define FSCPOS_REG_REVISION 0x03 ++ ++/* global control and status */ ++#define FSCPOS_REG_EVENT_STATE 0x04 ++#define FSCPOS_REG_CONTROL 0x05 ++ ++/* watchdog */ ++#define FSCPOS_REG_WDOG_PRESET 0x28 ++#define FSCPOS_REG_WDOG_STATE 0x23 ++#define FSCPOS_REG_WDOG_CONTROL 0x21 ++ ++/* fan 0 */ ++#define FSCPOS_REG_FAN0_MIN 0x55 ++#define FSCPOS_REG_FAN0_ACT 0x0e ++#define FSCPOS_REG_FAN0_STATE 0x0d ++#define FSCPOS_REG_FAN0_RIPPLE 0x0f ++ ++/* fan 1 */ ++#define FSCPOS_REG_FAN1_MIN 0x65 ++#define FSCPOS_REG_FAN1_ACT 0x6b ++#define FSCPOS_REG_FAN1_STATE 0x62 ++#define FSCPOS_REG_FAN1_RIPPLE 0x6f ++ ++/* fan 2 */ ++/* min speed fan2 not supported */ ++#define FSCPOS_REG_FAN2_ACT 0xab ++#define FSCPOS_REG_FAN2_STATE 0xa2 ++#define FSCPOS_REG_FAN2_RIPPLE 0x0af ++ ++/* voltage supervision */ ++#define FSCPOS_REG_VOLT_12 0x45 ++#define FSCPOS_REG_VOLT_5 0x42 ++#define FSCPOS_REG_VOLT_BATT 0x48 ++ ++/* temperatures */ ++/* sensor 0 */ ++#define FSCPOS_REG_TEMP0_ACT 0x64 ++#define FSCPOS_REG_TEMP0_STATE 0x71 ++ ++/* sensor 1 */ ++#define FSCPOS_REG_TEMP1_ACT 0x32 ++#define FSCPOS_REG_TEMP1_STATE 0x81 ++ ++/* sensor 2 */ ++#define FSCPOS_REG_TEMP2_ACT 0x35 ++#define FSCPOS_REG_TEMP2_STATE 0x91 ++ ++ ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) ++#define IN_FROM_REG(val,nr) (val) ++ ++/* Initial limits */ ++ ++/* For each registered FSCPOS, we need to keep some data in memory. That ++ data is pointed to by fscpos_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new fscpos client is ++ allocated. */ ++struct fscpos_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 revision; /* revision of chip */ ++ u8 global_event; /* global event status */ ++ u8 global_control; /* global control register */ ++ u8 watchdog[3]; /* watchdog */ ++ u8 volt[3]; /* 12, 5, battery current */ ++ u8 temp_act[3]; /* temperature */ ++ u8 temp_status[3]; /* status of sensor */ ++ u8 fan_act[3]; /* fans revolutions per second */ ++ u8 fan_status[3]; /* fan status */ ++ u8 fan_min[3]; /* fan min value for rps */ ++ u8 fan_ripple[3]; /* divider for rps */ ++}; ++ ++ ++static int fscpos_attach_adapter(struct i2c_adapter *adapter); ++static int fscpos_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int fscpos_detach_client(struct i2c_client *client); ++ ++static int fscpos_read_value(struct i2c_client *client, u8 register); ++static int fscpos_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void fscpos_update_client(struct i2c_client *client); ++static void fscpos_init_client(struct i2c_client *client); ++ ++ ++static void fscpos_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void fscpos_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscpos_fan_internal(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results, ++ int nr, int reg_state, int reg_min, int res_ripple); ++static void fscpos_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscpos_volt(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscpos_wdog(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int fscpos_id = 0; ++ ++static struct i2c_driver fscpos_driver = { ++ .owner = THIS_MODULE, ++ .name = "FSCPOS sensor driver", ++ .id = I2C_DRIVERID_FSCPOS, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = fscpos_attach_adapter, ++ .detach_client = fscpos_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define FSCPOS_SYSCTL_VOLT0 1000 /* 12 volt supply */ ++#define FSCPOS_SYSCTL_VOLT1 1001 /* 5 volt supply */ ++#define FSCPOS_SYSCTL_VOLT2 1002 /* batterie voltage*/ ++#define FSCPOS_SYSCTL_FAN0 1101 /* state, min, ripple, actual value fan 0 */ ++#define FSCPOS_SYSCTL_FAN1 1102 /* state, min, ripple, actual value fan 1 */ ++#define FSCPOS_SYSCTL_FAN2 1103 /* state, min, ripple, actual value fan 2 */ ++#define FSCPOS_SYSCTL_TEMP0 1201 /* state and value of sensor 0, cpu die */ ++#define FSCPOS_SYSCTL_TEMP1 1202 /* state and value of sensor 1, motherboard */ ++#define FSCPOS_SYSCTL_TEMP2 1203 /* state and value of sensor 2, chassis */ ++#define FSCPOS_SYSCTL_REV 2000 /* Revision */ ++#define FSCPOS_SYSCTL_EVENT 2001 /* global event status */ ++#define FSCPOS_SYSCTL_CONTROL 2002 /* global control byte */ ++#define FSCPOS_SYSCTL_WDOG 2003 /* state, min, ripple, actual value fan 2 */ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected FSCPOS. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table fscpos_dir_table_template[] = { ++ {FSCPOS_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_in}, ++ {FSCPOS_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_in}, ++ {FSCPOS_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_in}, ++ {FSCPOS_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_temp}, ++ {FSCPOS_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_temp}, ++ {FSCPOS_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_temp}, ++ {FSCPOS_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_volt}, ++ {FSCPOS_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_volt}, ++ {FSCPOS_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_volt}, ++ {FSCPOS_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_fan}, ++ {FSCPOS_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_fan}, ++ {FSCPOS_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_fan}, ++ {FSCPOS_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscpos_wdog}, ++ {0} ++}; ++ ++static int fscpos_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, fscpos_detect); ++} ++ ++int fscpos_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct fscpos_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("fscpos.o: fscpos_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access fscpos_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &fscpos_driver; ++ new_client->flags = 0; ++ ++ /* Do the remaining detection unless force or force_fscpos parameter */ ++ if (kind < 0) { ++ if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) != 0x50) ++ goto ERROR1; ++ if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) != 0x45) ++ goto ERROR1; ++ if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) != 0x47) ++ goto ERROR1; ++ } ++ ++ kind = fscpos; ++ ++ type_name = "fscpos"; ++ client_name = "fsc poseidon chip"; ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = fscpos_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ fscpos_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ fscpos_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int fscpos_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct fscpos_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("fscpos.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int fscpos_read_value(struct i2c_client *client, u8 reg) ++{ ++#ifdef DEBUG ++ printk("fscpos: read reg 0x%02x\n",reg); ++#endif ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++#ifdef DEBUG ++ printk("fscpos: write reg 0x%02x, val 0x%02x\n",reg, value); ++#endif ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new FSCPOS. It should set limits, etc. */ ++static void fscpos_init_client(struct i2c_client *client) ++{ ++ struct fscpos_data *data = client->data; ++ ++ /* read revision from chip */ ++ data->revision = fscpos_read_value(client,FSCPOS_REG_REVISION); ++ /* setup missing fan2_min value */ ++ data->fan_min[2] = 0xff; ++} ++ ++static void fscpos_update_client(struct i2c_client *client) ++{ ++ struct fscpos_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > 2 * HZ) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting fscpos update\n"); ++#endif ++ data->temp_act[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_ACT); ++ data->temp_act[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_ACT); ++ data->temp_act[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_ACT); ++ data->temp_status[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_STATE); ++ data->temp_status[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_STATE); ++ data->temp_status[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_STATE); ++ ++ data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); ++ data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); ++ data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); ++ ++ data->fan_act[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_ACT); ++ data->fan_act[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_ACT); ++ data->fan_act[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_ACT); ++ data->fan_status[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_STATE); ++ data->fan_status[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_STATE); ++ data->fan_status[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_STATE); ++ data->fan_min[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_MIN); ++ data->fan_min[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_MIN); ++ /* fan2_min is not supported */ ++ data->fan_ripple[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_RIPPLE); ++ data->fan_ripple[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_RIPPLE); ++ data->fan_ripple[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_RIPPLE); ++ ++ data->watchdog[0] = fscpos_read_value(client, FSCPOS_REG_WDOG_PRESET); ++ data->watchdog[1] = fscpos_read_value(client, FSCPOS_REG_WDOG_STATE); ++ data->watchdog[2] = fscpos_read_value(client, FSCPOS_REG_WDOG_CONTROL); ++ ++ data->global_event = fscpos_read_value(client, FSCPOS_REG_EVENT_STATE); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void fscpos_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscpos_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscpos_update_client(client); ++ switch(ctl_name) { ++ case FSCPOS_SYSCTL_REV: ++ results[0] = data->revision ; ++ break; ++ case FSCPOS_SYSCTL_EVENT: ++ results[0] = data->global_event & 0x1f; ++ break; ++ case FSCPOS_SYSCTL_CONTROL: ++ results[0] = data->global_control & 0x01; ++ break; ++ default: ++ printk("fscpos: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if((ctl_name == FSCPOS_SYSCTL_CONTROL) && (*nrels_mag >= 1)) { ++ data->global_control = (results[0] & 0x01); ++ printk("fscpos: writing 0x%02x to global_control\n", ++ data->global_control); ++ fscpos_write_value(client,FSCPOS_REG_CONTROL, ++ data->global_control); ++ } ++ else ++ printk("fscpos: writing to chip not supported\n"); ++ } ++} ++ ++#define TEMP_FROM_REG(val) (val-128) ++ ++ ++void fscpos_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscpos_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscpos_update_client(client); ++ switch(ctl_name) { ++ case FSCPOS_SYSCTL_TEMP0: ++ results[0] = data->temp_status[0] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[0]); ++ break; ++ case FSCPOS_SYSCTL_TEMP1: ++ results[0] = data->temp_status[1] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[1]); ++ break; ++ case FSCPOS_SYSCTL_TEMP2: ++ results[0] = data->temp_status[2] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[2]); ++ break; ++ default: ++ printk("fscpos: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(*nrels_mag >= 1) { ++ switch(ctl_name) { ++ case FSCPOS_SYSCTL_TEMP0: ++ data->temp_status[0] = ++ (data->temp_status[0] & ~0x02) ++ | (results[0] & 0x02); ++ printk("fscpos: writing value 0x%02x " ++ "to temp0_status\n", ++ data->temp_status[0]); ++ fscpos_write_value(client, ++ FSCPOS_REG_TEMP0_STATE, ++ data->temp_status[0] & 0x02); ++ break; ++ case FSCPOS_SYSCTL_TEMP1: ++ data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02); ++ printk("fscpos: writing value 0x%02x to temp1_status\n", data->temp_status[1]); ++ fscpos_write_value(client,FSCPOS_REG_TEMP1_STATE, ++ data->temp_status[1] & 0x02); ++ break; ++ case FSCPOS_SYSCTL_TEMP2: ++ data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02); ++ printk("fscpos: writing value 0x%02x to temp2_status\n", data->temp_status[2]); ++ fscpos_write_value(client,FSCPOS_REG_TEMP2_STATE, ++ data->temp_status[2] & 0x02); ++ break; ++ default: ++ printk("fscpos: ctl_name %d not supported\n",ctl_name); ++ } ++ } ++ else ++ printk("fscpos: writing to chip not supported\n"); ++ } ++} ++ ++#define VOLT_FROM_REG(val,mult) (val*mult/255) ++ ++void fscpos_volt(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscpos_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscpos_update_client(client); ++ switch(ctl_name) { ++ case FSCPOS_SYSCTL_VOLT0: ++ results[0] = VOLT_FROM_REG(data->volt[0],1420); ++ break; ++ case FSCPOS_SYSCTL_VOLT1: ++ results[0] = VOLT_FROM_REG(data->volt[1],660); ++ break; ++ case FSCPOS_SYSCTL_VOLT2: ++ results[0] = VOLT_FROM_REG(data->volt[2],330); ++ break; ++ default: ++ printk("fscpos: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk("fscpos: writing to chip not supported\n"); ++ } ++} ++ ++void fscpos_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ ++ switch(ctl_name) { ++ case FSCPOS_SYSCTL_FAN0: ++ fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 0,FSCPOS_REG_FAN0_STATE,FSCPOS_REG_FAN0_MIN, ++ FSCPOS_REG_FAN0_RIPPLE); ++ break; ++ case FSCPOS_SYSCTL_FAN1: ++ fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 1,FSCPOS_REG_FAN1_STATE,FSCPOS_REG_FAN1_MIN, ++ FSCPOS_REG_FAN1_RIPPLE); ++ break; ++ case FSCPOS_SYSCTL_FAN2: ++ fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 2,FSCPOS_REG_FAN2_STATE,0xff, ++ FSCPOS_REG_FAN2_RIPPLE); ++ break; ++ default: ++ printk("fscpos: illegal fan nr %d\n",ctl_name); ++ } ++} ++ ++#define RPM_FROM_REG(val) (val*60) ++ ++void fscpos_fan_internal(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results, int nr, ++ int reg_state, int reg_min, int reg_ripple ) ++{ ++ struct fscpos_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscpos_update_client(client); ++ results[0] = data->fan_status[nr] & 0x04; ++ results[1] = data->fan_min[nr]; ++ results[2] = data->fan_ripple[nr] & 0x03; ++ results[3] = RPM_FROM_REG(data->fan_act[nr]); ++ *nrels_mag = 4; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(*nrels_mag >= 1) { ++ data->fan_status[nr] = results[0] & 0x04; ++ printk("fscpos: writing value 0x%02x to fan%d_status\n", ++ data->fan_status[nr],nr); ++ fscpos_write_value(client,reg_state, ++ data->fan_status[nr]); ++ } ++ if((*nrels_mag >= 2) && (nr < 2)) { ++ /* minimal speed for fan2 not supported */ ++ data->fan_min[nr] = results[1]; ++ printk("fscpos: writing value 0x%02x to fan%d_min\n", ++ data->fan_min[nr],nr); ++ fscpos_write_value(client,reg_min, ++ data->fan_min[nr]); ++ } ++ if(*nrels_mag >= 3) { ++ if((results[2] & 0x03) == 0) { ++ printk("fscpos: fan%d ripple 0 not allowed\n",nr); ++ return; ++ } ++ data->fan_ripple[nr] = results[2] & 0x03; ++ printk("fscpos: writing value 0x%02x to fan%d_ripple\n", ++ data->fan_ripple[nr],nr); ++ fscpos_write_value(client,reg_ripple, ++ data->fan_ripple[nr]); ++ } ++ } ++} ++ ++void fscpos_wdog(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscpos_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscpos_update_client(client); ++ results[0] = data->watchdog[0] ; ++ results[1] = data->watchdog[1] & 0x02; ++ results[2] = data->watchdog[2] & 0xb0; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->watchdog[0] = results[0] & 0xff; ++ printk("fscpos: writing value 0x%02x to wdog_preset\n", ++ data->watchdog[0]); ++ fscpos_write_value(client,FSCPOS_REG_WDOG_PRESET, ++ data->watchdog[0]); ++ } ++ if (*nrels_mag >= 2) { ++ data->watchdog[1] = results[1] & 0x02; ++ printk("fscpos: writing value 0x%02x to wdog_state\n", ++ data->watchdog[1]); ++ fscpos_write_value(client,FSCPOS_REG_WDOG_STATE, ++ data->watchdog[1]); ++ } ++ if (*nrels_mag >= 3) { ++ data->watchdog[2] = results[2] & 0xb0; ++ printk("fscpos: writing value 0x%02x to wdog_control\n", ++ data->watchdog[2]); ++ fscpos_write_value(client,FSCPOS_REG_WDOG_CONTROL, ++ data->watchdog[2]); ++ } ++ } ++} ++ ++static int __init sm_fscpos_init(void) ++{ ++ printk("fscpos.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&fscpos_driver); ++} ++ ++static void __exit sm_fscpos_exit(void) ++{ ++ i2c_del_driver(&fscpos_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Hermann Jung based on work from Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_fscpos_init); ++module_exit(sm_fscpos_exit); +--- linux-old/drivers/sensors/fscscy.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/fscscy.c Mon Dec 13 20:18:46 2004 +@@ -0,0 +1,915 @@ ++/* ++ fscscy.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2001 Martin Knoblauch ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ fujitsu siemens scylla chip, ++ module based on lm80.c, fscpos.c ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ and Philip Edelbrock ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(fscscy); ++ ++/* The FSCSCY registers */ ++ ++/* chip identification */ ++#define FSCSCY_REG_IDENT_0 0x00 ++#define FSCSCY_REG_IDENT_1 0x01 ++#define FSCSCY_REG_IDENT_2 0x02 ++#define FSCSCY_REG_REVISION 0x03 ++ ++/* global control and status */ ++#define FSCSCY_REG_EVENT_STATE 0x04 ++#define FSCSCY_REG_CONTROL 0x05 ++ ++/* watchdog */ ++#define FSCSCY_REG_WDOG_PRESET 0x28 ++#define FSCSCY_REG_WDOG_STATE 0x23 ++#define FSCSCY_REG_WDOG_CONTROL 0x21 ++ ++/* ++** Fan definitions ++** ++** _RPMMIN: Minimum speed. Can be set via interface, but only for three of the fans ++** FAN1_RPMMIN is wired to Fan 0 (CPU Fans) ++** FAN4_RPMMIN is wired to Fan 2 (PS Fans ??) ++** FAN5_RPMMIN is wired to Fan 3 (AUX Fans ??) ++** _ACT: Actual Fan Speed ++** _STATE: Fan status register ++** _RIPPLE: Fan speed multiplier ++*/ ++ ++/* fan 0 */ ++#define FSCSCY_REG_FAN0_RPMMIN 0x65 ++#define FSCSCY_REG_FAN0_ACT 0x6b ++#define FSCSCY_REG_FAN0_STATE 0x62 ++#define FSCSCY_REG_FAN0_RIPPLE 0x6f ++ ++/* fan 1 */ ++#define FSCSCY_REG_FAN1_RPMMIN FSCSCY_REG_FAN0_RPMMIN ++#define FSCSCY_REG_FAN1_ACT 0x6c ++#define FSCSCY_REG_FAN1_STATE 0x61 ++#define FSCSCY_REG_FAN1_RIPPLE 0x6f ++ ++/* fan 2 */ ++#define FSCSCY_REG_FAN2_RPMMIN 0x55 ++#define FSCSCY_REG_FAN2_ACT 0x0e ++#define FSCSCY_REG_FAN2_STATE 0x0d ++#define FSCSCY_REG_FAN2_RIPPLE 0x0f ++ ++/* fan 3 */ ++#define FSCSCY_REG_FAN3_RPMMIN 0xa5 ++#define FSCSCY_REG_FAN3_ACT 0xab ++#define FSCSCY_REG_FAN3_STATE 0xa2 ++#define FSCSCY_REG_FAN3_RIPPLE 0xaf ++ ++/* fan 4 */ ++#define FSCSCY_REG_FAN4_RPMMIN FSCSCY_REG_FAN2_RPMMIN ++#define FSCSCY_REG_FAN4_ACT 0x5c ++#define FSCSCY_REG_FAN4_STATE 0x52 ++#define FSCSCY_REG_FAN4_RIPPLE 0x0f ++ ++/* fan 5 */ ++#define FSCSCY_REG_FAN5_RPMMIN FSCSCY_REG_FAN3_RPMMIN ++#define FSCSCY_REG_FAN5_ACT 0xbb ++#define FSCSCY_REG_FAN5_STATE 0xb2 ++#define FSCSCY_REG_FAN5_RIPPLE 0xbf ++ ++/* voltage supervision */ ++#define FSCSCY_REG_VOLT_12 0x45 ++#define FSCSCY_REG_VOLT_5 0x42 ++#define FSCSCY_REG_VOLT_BATT 0x48 ++ ++/* temperatures */ ++/* sensor 0 */ ++#define FSCSCY_REG_TEMP0_ACT 0x64 ++#define FSCSCY_REG_TEMP0_STATE 0x71 ++#define FSCSCY_REG_TEMP0_LIM 0x76 ++ ++/* sensor 1 */ ++#define FSCSCY_REG_TEMP1_ACT 0xD0 ++#define FSCSCY_REG_TEMP1_STATE 0xD1 ++#define FSCSCY_REG_TEMP1_LIM 0xD6 ++ ++/* sensor 2 */ ++#define FSCSCY_REG_TEMP2_ACT 0x32 ++#define FSCSCY_REG_TEMP2_STATE 0x81 ++#define FSCSCY_REG_TEMP2_LIM 0x86 ++ ++/* sensor3 */ ++#define FSCSCY_REG_TEMP3_ACT 0x35 ++#define FSCSCY_REG_TEMP3_STATE 0x91 ++#define FSCSCY_REG_TEMP3_LIM 0x96 ++ ++/* PCI Load */ ++#define FSCSCY_REG_PCILOAD 0x1a ++ ++/* Intrusion Sensor */ ++#define FSCSCY_REG_INTR_STATE 0x13 ++#define FSCSCY_REG_INTR_CTRL 0x12 ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) ++#define IN_FROM_REG(val,nr) (val) ++ ++/* Initial limits */ ++ ++/* For each registered FSCSCY, we need to keep some data in memory. That ++ data is pointed to by fscscy_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new fscscy client is ++ allocated. */ ++struct fscscy_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 revision; /* revision of chip */ ++ u8 global_event; /* global event status */ ++ u8 global_control; /* global control register */ ++ u8 watchdog[3]; /* watchdog */ ++ u8 volt[3]; /* 12, 5, battery current */ ++ u8 volt_min[3]; /* minimum voltages over module "lifetime" */ ++ u8 volt_max[3]; /* maximum voltages over module "lifetime" */ ++ u8 temp_act[4]; /* temperature */ ++ u8 temp_status[4]; /* status of temp. sensor */ ++ u8 temp_lim[4]; /* limit temperature of temp. sensor */ ++ u8 temp_min[4]; /* minimum of temp. sensor, this is just calculated by the module */ ++ u8 temp_max[4]; /* maximum of temp. sensor, this is just calculsted by the module */ ++ u8 fan_act[6]; /* fans revolutions per second */ ++ u8 fan_status[6]; /* fan status */ ++ u8 fan_rpmmin[6]; /* fan min value for rps */ ++ u8 fan_ripple[6]; /* divider for rps */ ++ u8 fan_min[6]; /* minimum RPM over module "lifetime" */ ++ u8 fan_max[6]; /* maximum RPM over module "lifetime" */ ++ u8 pciload; /* PCILoad value */ ++ u8 intr_status; /* Intrusion Status */ ++ u8 intr_control; /* Intrusion Control */ ++}; ++ ++ ++static int fscscy_attach_adapter(struct i2c_adapter *adapter); ++static int fscscy_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int fscscy_detach_client(struct i2c_client *client); ++ ++static int fscscy_read_value(struct i2c_client *client, u8 register); ++static int fscscy_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void fscscy_update_client(struct i2c_client *client); ++static void fscscy_init_client(struct i2c_client *client); ++ ++ ++static void fscscy_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void fscscy_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscscy_fan_internal(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results, ++ int nr, int reg_state, int reg_min, int res_ripple); ++static void fscscy_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscscy_volt(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscscy_wdog(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscscy_pciload(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void fscscy_intrusion(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int fscscy_id = 0; ++ ++static struct i2c_driver fscscy_driver = { ++ .owner = THIS_MODULE, ++ .name = "FSCSCY sensor driver", ++ .id = I2C_DRIVERID_FSCSCY, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = fscscy_attach_adapter, ++ .detach_client = fscscy_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define FSCSCY_SYSCTL_VOLT0 1000 /* 12 volt supply */ ++#define FSCSCY_SYSCTL_VOLT1 1001 /* 5 volt supply */ ++#define FSCSCY_SYSCTL_VOLT2 1002 /* batterie voltage*/ ++#define FSCSCY_SYSCTL_FAN0 1101 /* state, min, ripple, actual value fan 0 */ ++#define FSCSCY_SYSCTL_FAN1 1102 /* state, min, ripple, actual value fan 1 */ ++#define FSCSCY_SYSCTL_FAN2 1103 /* state, min, ripple, actual value fan 2 */ ++#define FSCSCY_SYSCTL_FAN3 1104 /* state, min, ripple, actual value fan 3 */ ++#define FSCSCY_SYSCTL_FAN4 1105 /* state, min, ripple, actual value fan 4 */ ++#define FSCSCY_SYSCTL_FAN5 1106 /* state, min, ripple, actual value fan 5 */ ++#define FSCSCY_SYSCTL_TEMP0 1201 /* state and value of sensor 0, cpu die */ ++#define FSCSCY_SYSCTL_TEMP1 1202 /* state and value of sensor 1, motherboard */ ++#define FSCSCY_SYSCTL_TEMP2 1203 /* state and value of sensor 2, chassis */ ++#define FSCSCY_SYSCTL_TEMP3 1204 /* state and value of sensor 3, chassis */ ++#define FSCSCY_SYSCTL_REV 2000 /* Revision */ ++#define FSCSCY_SYSCTL_EVENT 2001 /* global event status */ ++#define FSCSCY_SYSCTL_CONTROL 2002 /* global control byte */ ++#define FSCSCY_SYSCTL_WDOG 2003 /* state, min, ripple, actual value fan 2 */ ++#define FSCSCY_SYSCTL_PCILOAD 2004 /* PCILoad value */ ++#define FSCSCY_SYSCTL_INTRUSION 2005 /* state, control for intrusion sensor */ ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected FSCSCY. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table fscscy_dir_table_template[] = { ++ {FSCSCY_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_in}, ++ {FSCSCY_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_in}, ++ {FSCSCY_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_in}, ++ {FSCSCY_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_temp}, ++ {FSCSCY_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_temp}, ++ {FSCSCY_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_temp}, ++ {FSCSCY_SYSCTL_TEMP3, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_temp}, ++ {FSCSCY_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_volt}, ++ {FSCSCY_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_volt}, ++ {FSCSCY_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_volt}, ++ {FSCSCY_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_FAN3, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_FAN4, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_FAN5, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_fan}, ++ {FSCSCY_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_wdog}, ++ {FSCSCY_SYSCTL_PCILOAD, "pciload", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_pciload}, ++ {FSCSCY_SYSCTL_INTRUSION, "intrusion", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &fscscy_intrusion}, ++ {0} ++}; ++ ++static int fscscy_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, fscscy_detect); ++} ++ ++int fscscy_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct fscscy_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("fscscy.o: fscscy_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access fscscy_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct fscscy_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &fscscy_driver; ++ new_client->flags = 0; ++ ++ /* Do the remaining detection unless force or force_fscscy parameter */ ++ if (kind < 0) { ++ if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_0) != 0x53) ++ goto ERROR1; ++ if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_1) != 0x43) ++ goto ERROR1; ++ if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_2) != 0x59) ++ goto ERROR1; ++ } ++ ++ kind = fscscy; ++ ++ type_name = "fscscy"; ++ client_name = "fsc scylla chip"; ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = fscscy_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ fscscy_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ fscscy_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int fscscy_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct fscscy_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("fscscy.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int fscscy_read_value(struct i2c_client *client, u8 reg) ++{ ++#ifdef DEBUG ++ printk("fscscy: read reg 0x%02x\n",reg); ++#endif ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int fscscy_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++#ifdef DEBUG ++ printk("fscscy: write reg 0x%02x, val 0x%02x\n",reg, value); ++#endif ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new FSCSCY. It should set limits, etc. */ ++static void fscscy_init_client(struct i2c_client *client) ++{ ++ struct fscscy_data *data = client->data; ++ ++ /* read revision from chip */ ++ data->revision = fscscy_read_value(client,FSCSCY_REG_REVISION); ++ ++ /* Initialize min/max values from chip */ ++ data->fan_min[0] = data->fan_max[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT); ++ data->fan_min[1] = data->fan_max[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT); ++ data->fan_min[2] = data->fan_max[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT); ++ data->fan_min[3] = data->fan_max[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT); ++ data->fan_min[4] = data->fan_max[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT); ++ data->fan_min[4] = data->fan_max[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT); ++ data->temp_min[0] = data->temp_max[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT); ++ data->temp_min[1] = data->temp_max[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT); ++ data->temp_min[2] = data->temp_max[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT); ++ data->temp_min[3] = data->temp_max[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT); ++ data->volt_min[0] = data->volt_max[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12); ++ data->volt_min[1] = data->volt_max[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5); ++ data->volt_min[2] = data->volt_max[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT); ++} ++ ++static void fscscy_update_client(struct i2c_client *client) ++{ ++ struct fscscy_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > 2 * HZ) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting fscscy update\n"); ++#endif ++ data->temp_act[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT); ++ if (data->temp_min[0] > data->temp_act[0]) data->temp_min[0] = data->temp_act[0]; ++ if (data->temp_max[0] < data->temp_act[0]) data->temp_max[0] = data->temp_act[0]; ++ data->temp_act[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT); ++ if (data->temp_min[1] > data->temp_act[1]) data->temp_min[1] = data->temp_act[1]; ++ if (data->temp_max[1] < data->temp_act[1]) data->temp_max[1] = data->temp_act[1]; ++ data->temp_act[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT); ++ if (data->temp_min[2] > data->temp_act[2]) data->temp_min[2] = data->temp_act[2]; ++ if (data->temp_max[2] < data->temp_act[2]) data->temp_max[2] = data->temp_act[2]; ++ data->temp_act[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT); ++ if (data->temp_min[3] > data->temp_act[3]) data->temp_min[3] = data->temp_act[3]; ++ if (data->temp_max[3] < data->temp_act[3]) data->temp_max[3] = data->temp_act[3]; ++ data->temp_status[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_STATE); ++ data->temp_status[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_STATE); ++ data->temp_status[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_STATE); ++ data->temp_status[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_STATE); ++ data->temp_lim[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_LIM); ++ data->temp_lim[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_LIM); ++ data->temp_lim[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_LIM); ++ data->temp_lim[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_LIM); ++ ++ data->volt[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12); ++ if (data->volt_min[0] > data->volt[0]) data->volt_min[0] = data->volt[0]; ++ if (data->volt_max[0] < data->volt[0]) data->volt_max[0] = data->volt[0]; ++ data->volt[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5); ++ if (data->volt_min[1] > data->volt[1]) data->volt_min[1] = data->volt[1]; ++ if (data->volt_max[1] < data->volt[1]) data->volt_max[1] = data->volt[1]; ++ data->volt[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT); ++ if (data->volt_min[2] > data->volt[2]) data->volt_min[2] = data->volt[2]; ++ if (data->volt_max[2] < data->volt[2]) data->volt_max[2] = data->volt[2]; ++ ++ data->fan_act[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT); ++ if (data->fan_min[0] > data->fan_act[0]) data->fan_min[0] = data->fan_act[0]; ++ if (data->fan_max[0] < data->fan_act[0]) data->fan_max[0] = data->fan_act[0]; ++ data->fan_act[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT); ++ if (data->fan_min[1] > data->fan_act[1]) data->fan_min[1] = data->fan_act[1]; ++ if (data->fan_max[1] < data->fan_act[1]) data->fan_max[1] = data->fan_act[1]; ++ data->fan_act[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT); ++ if (data->fan_min[2] > data->fan_act[2]) data->fan_min[2] = data->fan_act[2]; ++ if (data->fan_max[2] < data->fan_act[2]) data->fan_max[2] = data->fan_act[2]; ++ data->fan_act[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT); ++ if (data->fan_min[3] > data->fan_act[3]) data->fan_min[3] = data->fan_act[3]; ++ if (data->fan_max[3] < data->fan_act[3]) data->fan_max[3] = data->fan_act[3]; ++ data->fan_act[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT); ++ if (data->fan_min[4] > data->fan_act[4]) data->fan_min[4] = data->fan_act[4]; ++ if (data->fan_max[4] < data->fan_act[4]) data->fan_max[4] = data->fan_act[4]; ++ data->fan_act[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT); ++ if (data->fan_min[5] > data->fan_act[5]) data->fan_min[5] = data->fan_act[5]; ++ if (data->fan_max[5] < data->fan_act[5]) data->fan_max[5] = data->fan_act[5]; ++ data->fan_status[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_STATE); ++ data->fan_status[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_STATE); ++ data->fan_status[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_STATE); ++ data->fan_status[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_STATE); ++ data->fan_status[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_STATE); ++ data->fan_status[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_STATE); ++ data->fan_rpmmin[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RPMMIN); ++ data->fan_rpmmin[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RPMMIN); ++ data->fan_rpmmin[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RPMMIN); ++ data->fan_rpmmin[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RPMMIN); ++ data->fan_rpmmin[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RPMMIN); ++ data->fan_rpmmin[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RPMMIN); ++ data->fan_ripple[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RIPPLE); ++ data->fan_ripple[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RIPPLE); ++ data->fan_ripple[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RIPPLE); ++ data->fan_ripple[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RIPPLE); ++ data->fan_ripple[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RIPPLE); ++ data->fan_ripple[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RIPPLE); ++ ++ data->watchdog[0] = fscscy_read_value(client, FSCSCY_REG_WDOG_PRESET); ++ data->watchdog[1] = fscscy_read_value(client, FSCSCY_REG_WDOG_STATE); ++ data->watchdog[2] = fscscy_read_value(client, FSCSCY_REG_WDOG_CONTROL); ++ ++ data->global_event = fscscy_read_value(client, FSCSCY_REG_EVENT_STATE); ++ data->global_control = fscscy_read_value(client, FSCSCY_REG_CONTROL); ++ data->pciload = fscscy_read_value(client, FSCSCY_REG_PCILOAD); ++ data->intr_status = fscscy_read_value(client, FSCSCY_REG_INTR_STATE); ++ data->intr_control = fscscy_read_value(client, FSCSCY_REG_INTR_CTRL); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void fscscy_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ switch(ctl_name) { ++ case FSCSCY_SYSCTL_REV: ++ results[0] = data->revision ; ++ break; ++ case FSCSCY_SYSCTL_EVENT: ++ results[0] = data->global_event & 0x9f; /* MKN */ ++ break; ++ case FSCSCY_SYSCTL_CONTROL: ++ results[0] = data->global_control & 0x19; /* MKN */ ++ break; ++ default: ++ printk("fscscy: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if((ctl_name == FSCSCY_SYSCTL_CONTROL) && (*nrels_mag >= 1)) { ++ data->global_control = (data->global_control & 0x18) | (results[0] & 0x01); /* MKN */ ++ printk("fscscy: writing 0x%02x to global_control\n", ++ data->global_control); ++ fscscy_write_value(client,FSCSCY_REG_CONTROL, ++ data->global_control); ++ } ++ else ++ printk("fscscy: writing to chip not supported\n"); ++ } ++} ++ ++#define TEMP_FROM_REG(val) (val-128) ++ ++ ++void fscscy_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ switch(ctl_name) { ++ case FSCSCY_SYSCTL_TEMP0: ++ results[0] = data->temp_status[0] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[0]); ++ results[2] = TEMP_FROM_REG(data->temp_lim[0]); ++ results[3] = TEMP_FROM_REG(data->temp_min[0]); ++ results[4] = TEMP_FROM_REG(data->temp_max[0]); ++ break; ++ case FSCSCY_SYSCTL_TEMP1: ++ results[0] = data->temp_status[1] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[1]); ++ results[2] = TEMP_FROM_REG(data->temp_lim[1]); ++ results[3] = TEMP_FROM_REG(data->temp_min[1]); ++ results[4] = TEMP_FROM_REG(data->temp_max[1]); ++ break; ++ case FSCSCY_SYSCTL_TEMP2: ++ results[0] = data->temp_status[2] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[2]); ++ results[2] = TEMP_FROM_REG(data->temp_lim[2]); ++ results[3] = TEMP_FROM_REG(data->temp_min[2]); ++ results[4] = TEMP_FROM_REG(data->temp_max[2]); ++ break; ++ case FSCSCY_SYSCTL_TEMP3: ++ results[0] = data->temp_status[3] & 0x03; ++ results[1] = TEMP_FROM_REG(data->temp_act[3]); ++ results[2] = TEMP_FROM_REG(data->temp_lim[3]); ++ results[3] = TEMP_FROM_REG(data->temp_min[3]); ++ results[4] = TEMP_FROM_REG(data->temp_max[3]); ++ break; ++ default: ++ printk("fscscy: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 5; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(*nrels_mag >= 1) { ++ switch(ctl_name) { ++ case FSCSCY_SYSCTL_TEMP0: ++ data->temp_status[0] = ++ (data->temp_status[0] & ~0x02) ++ | (results[0] & 0x02); ++ printk("fscscy: writing value 0x%02x " ++ "to temp0_status\n", ++ data->temp_status[0]); ++ fscscy_write_value(client, ++ FSCSCY_REG_TEMP0_STATE, ++ data->temp_status[0] & 0x02); ++ break; ++ case FSCSCY_SYSCTL_TEMP1: ++ data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02); ++ printk("fscscy: writing value 0x%02x to temp1_status\n", data->temp_status[1]); ++ fscscy_write_value(client,FSCSCY_REG_TEMP1_STATE, ++ data->temp_status[1] & 0x02); ++ break; ++ case FSCSCY_SYSCTL_TEMP2: ++ data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02); ++ printk("fscscy: writing value 0x%02x to temp2_status\n", data->temp_status[2]); ++ fscscy_write_value(client,FSCSCY_REG_TEMP2_STATE, ++ data->temp_status[2] & 0x02); ++ break; ++ case FSCSCY_SYSCTL_TEMP3: ++ data->temp_status[3] = (data->temp_status[3] & ~0x02) | (results[0] & 0x02); ++ printk("fscscy: writing value 0x%02x to temp3_status\n", data->temp_status[3]); ++ fscscy_write_value(client,FSCSCY_REG_TEMP3_STATE, ++ data->temp_status[3] & 0x02); ++ break; ++ default: ++ printk("fscscy: ctl_name %d not supported\n",ctl_name); ++ } ++ } ++ else ++ printk("fscscy: writing to chip not supported\n"); ++ } ++} ++ ++#define VOLT_FROM_REG(val,mult) (val*mult/255) ++ ++void fscscy_volt(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ switch(ctl_name) { ++ case FSCSCY_SYSCTL_VOLT0: ++ results[0] = VOLT_FROM_REG(data->volt[0],1420); ++ results[1] = VOLT_FROM_REG(data->volt_min[0],1420); ++ results[2] = VOLT_FROM_REG(data->volt_max[0],1420); ++ break; ++ case FSCSCY_SYSCTL_VOLT1: ++ results[0] = VOLT_FROM_REG(data->volt[1],660); ++ results[1] = VOLT_FROM_REG(data->volt_min[1],660); ++ results[2] = VOLT_FROM_REG(data->volt_max[1],660); ++ break; ++ case FSCSCY_SYSCTL_VOLT2: ++ results[0] = VOLT_FROM_REG(data->volt[2],330); ++ results[1] = VOLT_FROM_REG(data->volt_min[2],330); ++ results[2] = VOLT_FROM_REG(data->volt_max[2],330); ++ break; ++ default: ++ printk("fscscy: ctl_name %d not supported\n", ++ ctl_name); ++ *nrels_mag = 0; ++ return; ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk("fscscy: writing to chip not supported\n"); ++ } ++} ++ ++void fscscy_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ ++ switch(ctl_name) { ++ case FSCSCY_SYSCTL_FAN0: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 0,FSCSCY_REG_FAN0_STATE,FSCSCY_REG_FAN0_RPMMIN, ++ FSCSCY_REG_FAN0_RIPPLE); ++ break; ++ case FSCSCY_SYSCTL_FAN1: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 1,FSCSCY_REG_FAN1_STATE,FSCSCY_REG_FAN1_RPMMIN, ++ FSCSCY_REG_FAN1_RIPPLE); ++ break; ++ case FSCSCY_SYSCTL_FAN2: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 2,FSCSCY_REG_FAN2_STATE,FSCSCY_REG_FAN2_RPMMIN, ++ FSCSCY_REG_FAN2_RIPPLE); ++ break; ++ case FSCSCY_SYSCTL_FAN3: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 3,FSCSCY_REG_FAN3_STATE,FSCSCY_REG_FAN3_RPMMIN, ++ FSCSCY_REG_FAN3_RIPPLE); ++ break; ++ case FSCSCY_SYSCTL_FAN4: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 4,FSCSCY_REG_FAN4_STATE,FSCSCY_REG_FAN4_RPMMIN, ++ FSCSCY_REG_FAN4_RIPPLE); ++ break; ++ case FSCSCY_SYSCTL_FAN5: ++ fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, ++ 5,FSCSCY_REG_FAN5_STATE,FSCSCY_REG_FAN5_RPMMIN, ++ FSCSCY_REG_FAN5_RIPPLE); ++ break; ++ default: ++ printk("fscscy: illegal fan nr %d\n",ctl_name); ++ } ++} ++ ++#define RPM_FROM_REG(val) (val*60) ++ ++void fscscy_fan_internal(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results, int nr, ++ int reg_state, int reg_min, int reg_ripple ) ++{ ++ struct fscscy_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ results[0] = data->fan_status[nr] & 0x0f; /* MKN */ ++ results[1] = data->fan_rpmmin[nr]; ++ results[2] = data->fan_ripple[nr] & 0x03; ++ results[3] = RPM_FROM_REG(data->fan_act[nr]); ++ results[4] = RPM_FROM_REG(data->fan_min[nr]); ++ results[5] = RPM_FROM_REG(data->fan_max[nr]); ++ *nrels_mag = 6; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(*nrels_mag >= 1) { ++ data->fan_status[nr] = (data->fan_status[nr] & 0x0b) | (results[0] & 0x04); /* MKN */ ++ printk("fscscy: writing value 0x%02x to fan%d_status\n", ++ data->fan_status[nr],nr); ++ fscscy_write_value(client,reg_state, ++ data->fan_status[nr]); ++ } ++ if(*nrels_mag >= 2) { ++ if((results[1] & 0xff) == 0) { ++ printk("fscscy: fan%d rpmmin 0 not allowed for safety reasons\n",nr); ++ return; ++ } ++ data->fan_rpmmin[nr] = results[1]; ++ printk("fscscy: writing value 0x%02x to fan%d_min\n", ++ data->fan_rpmmin[nr],nr); ++ fscscy_write_value(client,reg_min, ++ data->fan_rpmmin[nr]); ++ } ++ if(*nrels_mag >= 3) { ++ if((results[2] & 0x03) == 0) { ++ printk("fscscy: fan%d ripple 0 is nonsense/not allowed\n",nr); ++ return; ++ } ++ data->fan_ripple[nr] = results[2] & 0x03; ++ printk("fscscy: writing value 0x%02x to fan%d_ripple\n", ++ data->fan_ripple[nr],nr); ++ fscscy_write_value(client,reg_ripple, ++ data->fan_ripple[nr]); ++ } ++ } ++} ++ ++void fscscy_wdog(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ results[0] = data->watchdog[0] ; ++ results[1] = data->watchdog[1] & 0x02; ++ results[2] = data->watchdog[2] & 0xb0; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->watchdog[0] = results[0] & 0xff; ++ printk("fscscy: writing value 0x%02x to wdog_preset\n", ++ data->watchdog[0]); ++ fscscy_write_value(client,FSCSCY_REG_WDOG_PRESET, ++ data->watchdog[0]); ++ } ++ if (*nrels_mag >= 2) { ++ data->watchdog[1] = results[1] & 0x02; ++ printk("fscscy: writing value 0x%02x to wdog_state\n", ++ data->watchdog[1]); ++ fscscy_write_value(client,FSCSCY_REG_WDOG_STATE, ++ data->watchdog[1]); ++ } ++ if (*nrels_mag >= 3) { ++ data->watchdog[2] = results[2] & 0xb0; ++ printk("fscscy: writing value 0x%02x to wdog_control\n", ++ data->watchdog[2]); ++ fscscy_write_value(client,FSCSCY_REG_WDOG_CONTROL, ++ data->watchdog[2]); ++ } ++ } ++} ++ ++void fscscy_pciload(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ results[0] = data->pciload; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk("fscscy: writing PCILOAD to chip not supported\n"); ++ } ++} ++ ++void fscscy_intrusion(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct fscscy_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ fscscy_update_client(client); ++ results[0] = data->intr_control & 0x80; ++ results[1] = data->intr_status & 0xc0; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->intr_control = results[0] & 0x80; ++ printk("fscscy: writing value 0x%02x to intr_control\n", ++ data->intr_control); ++ fscscy_write_value(client,FSCSCY_REG_INTR_CTRL, ++ data->intr_control); ++ } ++ } ++} ++ ++static int __init sm_fscscy_init(void) ++{ ++ printk("fscscy.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&fscscy_driver); ++} ++ ++static void __exit sm_fscscy_exit(void) ++{ ++ i2c_del_driver(&fscscy_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Martin Knoblauch based on work (fscpos) from Hermann Jung "); ++MODULE_DESCRIPTION("fujitsu siemens scylla chip driver"); ++ ++module_init(sm_fscscy_init); ++module_exit(sm_fscscy_exit); +--- linux-old/drivers/sensors/gl518sm.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/gl518sm.c Mon Dec 13 20:18:46 2004 +@@ -0,0 +1,989 @@ ++/* ++ gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard , ++ Kyösti Mälkki ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#ifdef __SMP__ ++#include ++#endif ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); ++ ++/* Defining this will enable debug messages for the voltage iteration ++ code used with rev 0 ICs */ ++#undef DEBUG_VIN ++ ++/* Many GL518 constants specified below */ ++ ++/* The GL518 registers */ ++#define GL518_REG_CHIP_ID 0x00 ++#define GL518_REG_REVISION 0x01 ++#define GL518_REG_VENDOR_ID 0x02 ++#define GL518_REG_CONF 0x03 ++#define GL518_REG_TEMP 0x04 ++#define GL518_REG_TEMP_OVER 0x05 ++#define GL518_REG_TEMP_HYST 0x06 ++#define GL518_REG_FAN_COUNT 0x07 ++#define GL518_REG_FAN_LIMIT 0x08 ++#define GL518_REG_VIN1_LIMIT 0x09 ++#define GL518_REG_VIN2_LIMIT 0x0a ++#define GL518_REG_VIN3_LIMIT 0x0b ++#define GL518_REG_VDD_LIMIT 0x0c ++#define GL518_REG_VIN3 0x0d ++#define GL518_REG_MISC 0x0f ++#define GL518_REG_ALARM 0x10 ++#define GL518_REG_MASK 0x11 ++#define GL518_REG_INT 0x12 ++#define GL518_REG_VIN2 0x13 ++#define GL518_REG_VIN1 0x14 ++#define GL518_REG_VDD 0x15 ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+119),\ ++ 0,255)) ++#define TEMP_FROM_REG(val) (((val) - 119) * 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) \ ++ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) ++ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) ++#define IN_FROM_REG(val) (((val)*19)/10) ++ ++#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) ++#define VDD_FROM_REG(val) (((val)*23)/10) ++ ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++#define ALARMS_FROM_REG(val) val ++ ++#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) ++#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) ++ ++#define BEEPS_TO_REG(val) ((val) & 0x7f) ++#define BEEPS_FROM_REG(val) ((val) & 0x7f) ++ ++/* Each client has this additional data */ ++struct gl518_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ ++ int iterate_lock; ++ int quit_thread; ++ struct task_struct *thread; ++ wait_queue_head_t wq; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ unsigned long last_updated_v00; ++ /* In jiffies (used only by rev00 chips) */ ++ ++ u8 voltage[4]; /* Register values; [0] = VDD */ ++ u8 voltage_min[4]; /* Register values; [0] = VDD */ ++ u8 voltage_max[4]; /* Register values; [0] = VDD */ ++ u8 iter_voltage[4]; /* Register values; [0] = VDD */ ++ u8 fan[2]; ++ u8 fan_min[2]; ++ u8 temp; /* Register values */ ++ u8 temp_over; /* Register values */ ++ u8 temp_hyst; /* Register values */ ++ u8 alarms, beeps; /* Register value */ ++ u8 alarm_mask; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u8 beep_enable; /* Boolean */ ++ u8 iterate; /* Voltage iteration mode */ ++}; ++ ++static int gl518_attach_adapter(struct i2c_adapter *adapter); ++static int gl518_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void gl518_init_client(struct i2c_client *client); ++static int gl518_detach_client(struct i2c_client *client); ++ ++static int gl518_read_value(struct i2c_client *client, u8 reg); ++static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value); ++static void gl518_update_client(struct i2c_client *client); ++ ++static void gl518_update_client_rev00(struct i2c_client *client); ++static void gl518_update_iterate(struct i2c_client *client); ++ ++static void gl518_vin(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_beep(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_fan1off(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl518_iterate(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver gl518_driver = { ++ .owner = THIS_MODULE, ++ .name = "GL518SM sensor chip driver", ++ .id = I2C_DRIVERID_GL518, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = gl518_attach_adapter, ++ .detach_client = gl518_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define GL518_SYSCTL_VDD 1000 /* Volts * 100 */ ++#define GL518_SYSCTL_VIN1 1001 ++#define GL518_SYSCTL_VIN2 1002 ++#define GL518_SYSCTL_VIN3 1003 ++#define GL518_SYSCTL_FAN1 1101 /* RPM */ ++#define GL518_SYSCTL_FAN2 1102 ++#define GL518_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ ++#define GL518_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define GL518_SYSCTL_ALARMS 2001 /* bitvector */ ++#define GL518_SYSCTL_BEEP 2002 /* bitvector */ ++#define GL518_SYSCTL_FAN1OFF 2003 ++#define GL518_SYSCTL_ITERATE 2004 ++ ++#define GL518_ALARM_VDD 0x01 ++#define GL518_ALARM_VIN1 0x02 ++#define GL518_ALARM_VIN2 0x04 ++#define GL518_ALARM_VIN3 0x08 ++#define GL518_ALARM_TEMP 0x10 ++#define GL518_ALARM_FAN1 0x20 ++#define GL518_ALARM_FAN2 0x40 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected GL518. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table gl518_dir_table_template[] = { ++ {GL518_SYSCTL_VIN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_vin}, ++ {GL518_SYSCTL_VIN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_vin}, ++ {GL518_SYSCTL_VIN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_vin}, ++ {GL518_SYSCTL_VDD, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_vin}, ++ {GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_fan}, ++ {GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_fan}, ++ {GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_temp}, ++ {GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_fan_div}, ++ {GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_alarms}, ++ {GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_beep}, ++ {GL518_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_fan1off}, ++ {GL518_SYSCTL_ITERATE, "iterate", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl518_iterate}, ++ {0} ++}; ++ ++/* I choose here for semi-static GL518SM allocation. Complete dynamic ++ allocation could also be used; the code needed for this would probably ++ take more memory than the datastructure takes now. */ ++#define MAX_GL518_NR 4 ++static struct i2c_client *gl518_list[MAX_GL518_NR]; ++ ++static int gl518_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, gl518_detect); ++} ++ ++static int gl518_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct gl518_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("gl518sm.o: gl518_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access gl518_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &gl518_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ( ++ (gl518_read_value(new_client, GL518_REG_CHIP_ID) != ++ 0x80) ++ || (gl518_read_value(new_client, GL518_REG_CONF) & ++ 0x80)) goto ERROR1; ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ i = gl518_read_value(new_client, GL518_REG_REVISION); ++ if (i == 0x00) ++ kind = gl518sm_r00; ++ else if (i == 0x80) ++ kind = gl518sm_r80; ++ else { ++ if (kind == 0) ++ printk ++ ("gl518sm.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ goto ERROR1; ++ } ++ } ++ ++ type_name = "gl518sm"; ++ if (kind == gl518sm_r00) { ++ client_name = "GL518SM Revision 0x00 chip"; ++ } else if (kind == gl518sm_r80) { ++ client_name = "GL518SM Revision 0x80 chip"; ++ } else { ++#ifdef DEBUG ++ printk("gl518sm.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ for (i = 0; i < MAX_GL518_NR; i++) ++ if (!gl518_list[i]) ++ break; ++ if (i == MAX_GL518_NR) { ++ printk ++ ("gl518sm.o: No empty slots left, recompile and heighten " ++ "MAX_GL518_NR!\n"); ++ err = -ENOMEM; ++ goto ERROR2; ++ } ++ gl518_list[i] = new_client; ++ new_client->id = i; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ gl518_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the GL518SM chip */ ++ if (kind == gl518sm_r00) ++ data->iterate = 0; ++ else ++ data->iterate = 3; ++ data->iterate_lock = 0; ++ data->quit_thread = 0; ++ data->thread = NULL; ++ data->alarm_mask = 0xff; ++ data->voltage[0]=data->voltage[1]=data->voltage[2]=0; ++ gl518_init_client((struct i2c_client *) new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ for (i = 0; i < MAX_GL518_NR; i++) ++ if (new_client == gl518_list[i]) ++ gl518_list[i] = NULL; ++ ERROR2: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++ ++/* Called when we have found a new GL518SM. It should set limits, etc. */ ++static void gl518_init_client(struct i2c_client *client) ++{ ++ /* Power-on defaults (bit 7=1) */ ++ gl518_write_value(client, GL518_REG_CONF, 0x80); ++ ++ /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0), ++ standby mode (bit6=0) */ ++ gl518_write_value(client, GL518_REG_CONF, 0x04); ++ ++ /* Never interrupts */ ++ gl518_write_value(client, GL518_REG_MASK, 0x00); ++ ++ /* Clear status register (bit 5=1), start (bit6=1) */ ++ gl518_write_value(client, GL518_REG_CONF, 0x24); ++ gl518_write_value(client, GL518_REG_CONF, 0x44); ++} ++ ++static int gl518_detach_client(struct i2c_client *client) ++{ ++ int err, i; ++ struct gl518_data *data = client->data; ++ ++ i2c_deregister_entry(((struct gl518_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("gl518sm.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ for (i = 0; i < MAX_GL518_NR; i++) ++ if (client == gl518_list[i]) ++ break; ++ if ((i == MAX_GL518_NR)) { ++ printk("gl518sm.o: Client to detach not found.\n"); ++ return -ENOENT; ++ } ++ gl518_list[i] = NULL; ++ ++ if (data->thread) { ++ data->quit_thread = 1; ++ wake_up_interruptible(&data->wq); ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* Registers 0x07 to 0x0c are word-sized, others are byte-sized ++ GL518 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int gl518_read_value(struct i2c_client *client, u8 reg) ++{ ++ if ((reg >= 0x07) && (reg <= 0x0c)) ++ return swab16(i2c_smbus_read_word_data(client, reg)); ++ else ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* Registers 0x07 to 0x0c are word-sized, others are byte-sized ++ GL518 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if ((reg >= 0x07) && (reg <= 0x0c)) ++ return i2c_smbus_write_word_data(client, reg, swab16(value)); ++ else ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void gl518_update_client(struct i2c_client *client) ++{ ++ struct gl518_data *data = client->data; ++ int val; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting gl518 update\n"); ++#endif ++ ++ data->alarms = gl518_read_value(client, GL518_REG_INT); ++ data->beeps = gl518_read_value(client, GL518_REG_ALARM); ++ ++ val = gl518_read_value(client, GL518_REG_VDD_LIMIT); ++ data->voltage_min[0] = val & 0xff; ++ data->voltage_max[0] = (val >> 8) & 0xff; ++ val = gl518_read_value(client, GL518_REG_VIN1_LIMIT); ++ data->voltage_min[1] = val & 0xff; ++ data->voltage_max[1] = (val >> 8) & 0xff; ++ val = gl518_read_value(client, GL518_REG_VIN2_LIMIT); ++ data->voltage_min[2] = val & 0xff; ++ data->voltage_max[2] = (val >> 8) & 0xff; ++ val = gl518_read_value(client, GL518_REG_VIN3_LIMIT); ++ data->voltage_min[3] = val & 0xff; ++ data->voltage_max[3] = (val >> 8) & 0xff; ++ ++ val = gl518_read_value(client, GL518_REG_FAN_COUNT); ++ data->fan[0] = (val >> 8) & 0xff; ++ data->fan[1] = val & 0xff; ++ ++ val = gl518_read_value(client, GL518_REG_FAN_LIMIT); ++ data->fan_min[0] = (val >> 8) & 0xff; ++ data->fan_min[1] = val & 0xff; ++ ++ data->temp = gl518_read_value(client, GL518_REG_TEMP); ++ data->temp_over = ++ gl518_read_value(client, GL518_REG_TEMP_OVER); ++ data->temp_hyst = ++ gl518_read_value(client, GL518_REG_TEMP_HYST); ++ ++ val = gl518_read_value(client, GL518_REG_MISC); ++ data->fan_div[0] = (val >> 6) & 0x03; ++ data->fan_div[1] = (val >> 4) & 0x03; ++ ++ data->alarms &= data->alarm_mask; ++ ++ val = gl518_read_value(client, GL518_REG_CONF); ++ data->beep_enable = (val >> 2) & 1; ++ ++#ifndef DEBUG_VIN ++ if (data->type != gl518sm_r00) { ++ data->voltage[0] = ++ gl518_read_value(client, GL518_REG_VDD); ++ data->voltage[1] = ++ gl518_read_value(client, GL518_REG_VIN1); ++ data->voltage[2] = ++ gl518_read_value(client, GL518_REG_VIN2); ++ data->voltage[3] = ++ gl518_read_value(client, GL518_REG_VIN3); ++ } else ++ gl518_update_client_rev00(client); ++#else ++ gl518_update_client_rev00(client); ++#endif ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++/* Here we decide how to run the iteration code. ++ When called, we trigger the iteration and report the last ++ measured voltage. No delay for user apps */ ++static void gl518_update_client_rev00(struct i2c_client *client) ++{ ++ struct gl518_data *data = client->data; ++ int i; ++ ++ if (data->iterate == 1) { /* 10 sec delay */ ++ /* as that update is slow, we consider the data valid for 30 seconds */ ++ if ( ++ ((jiffies - data->last_updated_v00 > 30 * HZ) ++ || (data->alarms & 7) ++ || (!data->valid)) && (!data->iterate_lock)) { ++ data->iterate_lock = 1; ++ gl518_update_iterate(client); ++ data->iterate_lock = 0; ++ } ++ for (i = 0; i < 4; i++) ++ data->voltage[i] = data->iter_voltage[i]; ++ } else if (data->iterate == 2) { /* show results of last iteration */ ++ for (i = 0; i < 4; i++) ++ data->voltage[i] = data->iter_voltage[i]; ++ wake_up_interruptible(&data->wq); ++ } else { /* no iteration */ ++ data->voltage[3] = ++ gl518_read_value(client, GL518_REG_VIN3); ++ } ++} ++ ++static int gl518_update_thread(void *c) ++{ ++ struct i2c_client *client = c; ++ struct gl518_data *data = client->data; ++ ++#ifdef __SMP__ ++ lock_kernel(); ++#endif ++ exit_mm(current); ++ current->session = 1; ++ current->pgrp = 1; ++ sigfillset(¤t->blocked); ++ current->fs->umask = 0; ++ strcpy(current->comm, "gl518sm"); ++ ++ init_waitqueue_head(&(data->wq)); ++ data->thread = current; ++ ++#ifdef __SMP__ ++ unlock_kernel(); ++#endif ++ ++ for (;;) { ++ if (!data->iterate_lock) { ++ data->iterate_lock = 1; ++ gl518_update_iterate(client); ++ data->iterate_lock = 0; ++ } ++ ++ if ((data->quit_thread) || signal_pending(current)) ++ break; ++ interruptible_sleep_on(&data->wq); ++ } ++ ++ data->thread = NULL; ++ data->quit_thread = 0; ++ return 0; ++} ++ ++/* This updates vdd, vin1, vin2 values by doing slow and multiple ++ comparisons for the GL518SM rev 00 that lacks support for direct ++ reading of these values. Values are kept in iter_voltage */ ++ ++static void gl518_update_iterate(struct i2c_client *client) ++{ ++ struct gl518_data *data = client->data; ++ int i, j, loop_more = 1, min[3], max[3], delta[3]; ++ int alarm, beeps, irqs; ++ ++#define VIN_REG(c) c==0?GL518_REG_VDD_LIMIT:\ ++ c==1?GL518_REG_VIN1_LIMIT:\ ++ GL518_REG_VIN2_LIMIT ++ ++ /* disable beeps & irqs for vin0-2 */ ++ beeps = gl518_read_value(client, GL518_REG_ALARM); ++ irqs = gl518_read_value(client, GL518_REG_MASK); ++ gl518_write_value(client, GL518_REG_ALARM, beeps & ~0x7); ++ gl518_write_value(client, GL518_REG_MASK, irqs & ~0x7); ++ ++ alarm = data->alarms; ++ ++ for (i = 0; i < 3; i++) { ++ if (alarm & (1 << i)) { ++ min[i] = 0; ++ max[i] = 127; ++ } else { ++ min[i] = data->voltage_min[i]; ++ max[i] = ++ (data->voltage_max[i] + ++ data->voltage_min[i]) / 2; ++ } ++ delta[i] = (max[i] - min[i]) / 2; ++ } ++ ++ for (j = 0; (j < 10 && loop_more); j++) { ++ ++ for (i = 0; i < 3; i++) ++ gl518_write_value(client, VIN_REG(i), ++ max[i] << 8 | min[i]); ++ ++ if ((data->thread) && ++ ((data->quit_thread) || signal_pending(current))) ++ goto finish; ++ ++ /* we wait now 1.5 seconds before comparing */ ++ current->state = TASK_INTERRUPTIBLE; ++ schedule_timeout(HZ + HZ / 2); ++ ++ alarm = gl518_read_value(client, GL518_REG_INT); ++ ++#ifdef DEBUG_VIN ++ printk("gl518sm: iteration %2d: %4d%c %4d%c %4d%c\n", j, ++ max[0], (alarm & 1) ? '!' : ' ', ++ max[1], (alarm & 2) ? '!' : ' ', ++ max[2], (alarm & 4) ? '!' : ' '); ++#endif ++ ++ for (loop_more = 0, i = 0; i < 3; i++) { ++ if (alarm & (1 << i)) ++ max[i] += delta[i]; ++ else ++ max[i] -= delta[i]; ++ ++ if (delta[i]) ++ loop_more++; ++ delta[i] >>= 1; ++ } ++ ++ } ++ ++ for (i = 0; i < 3; i++) ++ if (alarm & (1 << i)) ++ max[i]++; ++ ++#ifdef DEBUG_VIN ++ printk("gl518sm: final :%5d %5d %5d\n", max[0], max[1], ++ max[2]); ++ printk("gl518sm: meter :%5d %5d %5d\n", data->voltage[0], ++ data->voltage[1], data->voltage[2]); ++#endif ++ ++ /* update values, including vin3 */ ++ for (i = 0; i < 3; i++) { ++ data->iter_voltage[i] = max[i]; ++ } ++ data->iter_voltage[3] = gl518_read_value(client, GL518_REG_VIN3); ++ data->last_updated_v00 = jiffies; ++ ++ finish: ++ ++ /* reset values */ ++ for (i = 0; i < 3; i++) { ++ gl518_write_value(client, VIN_REG(i), ++ data->voltage_max[i] << 8 | data-> ++ voltage_min[i]); ++ } ++ ++ gl518_write_value(client, GL518_REG_ALARM, beeps); ++ gl518_write_value(client, GL518_REG_MASK, irqs); ++ ++#undef VIN_REG ++} ++ ++void gl518_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ gl518_write_value(client, GL518_REG_TEMP_OVER, ++ data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ gl518_write_value(client, GL518_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void gl518_vin(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ int nr = ctl_name - GL518_SYSCTL_VDD; ++ int regnr, old = 0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : ++ VDD_FROM_REG(data->voltage_min[nr]); ++ results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : ++ VDD_FROM_REG(data->voltage_max[nr]); ++ results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : ++ VDD_FROM_REG(data->voltage[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ regnr = ++ nr == 0 ? GL518_REG_VDD_LIMIT : nr == ++ 1 ? GL518_REG_VIN1_LIMIT : nr == ++ 2 ? GL518_REG_VIN2_LIMIT : GL518_REG_VIN3_LIMIT; ++ if (*nrels_mag == 1) ++ old = gl518_read_value(client, regnr) & 0xff00; ++ if (*nrels_mag >= 2) { ++ data->voltage_max[nr] = ++ nr ? IN_TO_REG(results[1]) : ++ VDD_TO_REG(results[1]); ++ old = data->voltage_max[nr] << 8; ++ } ++ if (*nrels_mag >= 1) { ++ data->voltage_min[nr] = ++ nr ? IN_TO_REG(results[0]) : ++ VDD_TO_REG(results[0]); ++ old |= data->voltage_min[nr]; ++ gl518_write_value(client, regnr, old); ++ } ++ } ++} ++ ++ ++void gl518_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ int nr = ctl_name - GL518_SYSCTL_FAN1; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr] = FAN_TO_REG(results[0], ++ DIV_FROM_REG(data-> ++ fan_div ++ [nr])); ++ old = ++ gl518_read_value(client, GL518_REG_FAN_LIMIT); ++ ++ if (nr == 0) { ++ old = ++ (old & 0x00ff) | (data-> ++ fan_min[0] << 8); ++ if (results[0] == 0) ++ data->alarm_mask &= ~0x20; ++ else ++ data->alarm_mask |= 0x20; ++ } else { ++ old = (old & 0xff00) | data->fan_min[1]; ++ if (results[0] == 0) ++ data->alarm_mask &= ~0x40; ++ else ++ data->alarm_mask |= 0x40; ++ } ++ gl518_write_value(client, GL518_REG_FAN_LIMIT, ++ old); ++ } ++ } ++} ++ ++ ++void gl518_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void gl518_beep(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); ++ results[1] = BEEPS_FROM_REG(data->beeps); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); ++ gl518_write_value(client, GL518_REG_CONF, ++ (gl518_read_value(client, ++ GL518_REG_CONF) ++ & 0xfb) | (data-> ++ beep_enable << 2)); ++ } ++ if (*nrels_mag >= 2) { ++ data->beeps = ++ BEEPS_TO_REG(results[1]) & data->alarm_mask; ++ gl518_write_value(client, GL518_REG_ALARM, ++ data->beeps); ++ } ++ } ++} ++ ++ ++void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ int old; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl518_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = gl518_read_value(client, GL518_REG_MISC); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0xcf) | (data->fan_div[1] << 4); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0x3f) | (data->fan_div[0] << 6); ++ } ++ gl518_write_value(client, GL518_REG_MISC, old); ++ } ++} ++ ++void gl518_fan1off(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int old; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = ++ ((gl518_read_value(client, GL518_REG_MISC) & 0x08) != ++ 0); ++ results[1] = ++ ((gl518_read_value(client, GL518_REG_CONF) & 0x10) != ++ 0); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ old = ++ gl518_read_value(client, ++ GL518_REG_MISC) & 0xf7; ++ if (results[0]) ++ old |= 0x08; ++ gl518_write_value(client, GL518_REG_MISC, old); ++ } ++ if (*nrels_mag >= 2) { ++ old = ++ gl518_read_value(client, ++ GL518_REG_CONF) & 0xef; ++ if (results[1]) ++ old |= 0x10; ++ gl518_write_value(client, GL518_REG_CONF, old); ++ } ++ } ++} ++ ++void gl518_iterate(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl518_data *data = client->data; ++ int i; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->iterate; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE && ++ data->type == gl518sm_r00 ) { ++ if ((*nrels_mag >= 1) && (data->iterate != results[0])) { ++ data->iterate = results[0]; ++ for (i = 0; i < 4; i++) { ++ data->voltage[i] = 0; ++ data->iter_voltage[i] = 0; ++ } ++ data->valid = 0; ++ ++ if ((data->iterate != 2) && (data->thread)) { ++ data->quit_thread = 1; ++ wake_up_interruptible(&data->wq); ++ } else if ((data->iterate == 2) && (!data->thread)) { ++ init_waitqueue_head(&(data->wq)); ++ kernel_thread(gl518_update_thread, ++ (void *) client, 0); ++ } ++ } ++ } ++} ++ ++static int __init sm_gl518sm_init(void) ++{ ++ printk("gl518sm.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&gl518_driver); ++} ++ ++static void __exit sm_gl518sm_exit(void) ++{ ++ i2c_del_driver(&gl518_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Kyösti Mälkki "); ++MODULE_DESCRIPTION("GL518SM driver"); ++ ++module_init(sm_gl518sm_init); ++module_exit(sm_gl518sm_exit); +--- linux-old/drivers/sensors/gl520sm.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/gl520sm.c Mon Dec 13 20:18:47 2004 +@@ -0,0 +1,809 @@ ++/* ++ gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard , ++ Kyösti Mälkki ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(gl520sm); ++ ++/* Many GL520 constants specified below ++One of the inputs can be configured as either temp or voltage. ++That's why _TEMP2 and _VIN4 access the same register ++*/ ++ ++/* The GL520 registers */ ++#define GL520_REG_CHIP_ID 0x00 ++#define GL520_REG_REVISION 0x01 ++#define GL520_REG_VID 0x02 ++#define GL520_REG_CONF 0x03 ++#define GL520_REG_TEMP1 0x04 ++#define GL520_REG_TEMP1_OVER 0x05 ++#define GL520_REG_TEMP1_HYST 0x06 ++#define GL520_REG_FAN_COUNT 0x07 ++#define GL520_REG_FAN_LIMIT 0x08 ++#define GL520_REG_VIN1_LIMIT 0x09 ++#define GL520_REG_VIN2_LIMIT 0x0a ++#define GL520_REG_VIN3_LIMIT 0x0b ++#define GL520_REG_VDD_LIMIT 0x0c ++#define GL520_REG_VIN3 0x0d ++#define GL520_REG_VIN4 0x0e ++#define GL520_REG_TEMP2 0x0e ++#define GL520_REG_MISC 0x0f ++#define GL520_REG_ALARM 0x10 ++#define GL520_REG_MASK 0x11 ++#define GL520_REG_INT 0x12 ++#define GL520_REG_VIN2 0x13 ++#define GL520_REG_VIN1 0x14 ++#define GL520_REG_VDD 0x15 ++#define GL520_REG_TEMP2_OVER 0x17 ++#define GL520_REG_VIN4_MAX 0x17 ++#define GL520_REG_TEMP2_HYST 0x18 ++#define GL520_REG_VIN4_MIN 0x18 ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+130),\ ++ 0,255)) ++#define TEMP_FROM_REG(val) (((val) - 130) * 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) \ ++ ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) ++ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) ++#define IN_FROM_REG(val) (((val)*19)/10) ++ ++#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) ++#define VDD_FROM_REG(val) (((val)*23)/10) ++ ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++#define ALARMS_FROM_REG(val) val ++ ++#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) ++#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) ++ ++#define BEEPS_TO_REG(val) (val) ++#define BEEPS_FROM_REG(val) (val) ++ ++#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ ++ 205-(val)*5) ++ ++/* Each client has this additional data */ ++struct gl520_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 voltage[5]; /* Register values; [0] = VDD */ ++ u8 voltage_min[5]; /* Register values; [0] = VDD */ ++ u8 voltage_max[5]; /* Register values; [0] = VDD */ ++ u8 fan[2]; ++ u8 fan_min[2]; ++ u8 temp[2]; /* Register values */ ++ u8 temp_over[2]; /* Register values */ ++ u8 temp_hyst[2]; /* Register values */ ++ u8 alarms, beeps, vid; /* Register value */ ++ u8 alarm_mask; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u8 beep_enable; /* Boolean */ ++ u8 two_temps; /* Boolean */ ++}; ++ ++static int gl520_attach_adapter(struct i2c_adapter *adapter); ++static int gl520_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void gl520_init_client(struct i2c_client *client); ++static int gl520_detach_client(struct i2c_client *client); ++ ++static int gl520_read_value(struct i2c_client *client, u8 reg); ++static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value); ++static void gl520_update_client(struct i2c_client *client); ++ ++static void gl520_vin(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_beep(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_fan1off(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void gl520_config(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver gl520_driver = { ++ .owner = THIS_MODULE, ++ .name = "GL520SM sensor chip driver", ++ .id = I2C_DRIVERID_GL520, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = gl520_attach_adapter, ++ .detach_client = gl520_detach_client, ++}; ++/* -- SENSORS SYSCTL START -- */ ++ ++#define GL520_SYSCTL_VDD 1000 /* Volts * 100 */ ++#define GL520_SYSCTL_VIN1 1001 ++#define GL520_SYSCTL_VIN2 1002 ++#define GL520_SYSCTL_VIN3 1003 ++#define GL520_SYSCTL_VIN4 1004 ++#define GL520_SYSCTL_FAN1 1101 /* RPM */ ++#define GL520_SYSCTL_FAN2 1102 ++#define GL520_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define GL520_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ ++#define GL520_SYSCTL_VID 1300 ++#define GL520_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define GL520_SYSCTL_ALARMS 2001 /* bitvector */ ++#define GL520_SYSCTL_BEEP 2002 /* bitvector */ ++#define GL520_SYSCTL_FAN1OFF 2003 ++#define GL520_SYSCTL_CONFIG 2004 ++ ++#define GL520_ALARM_VDD 0x01 ++#define GL520_ALARM_VIN1 0x02 ++#define GL520_ALARM_VIN2 0x04 ++#define GL520_ALARM_VIN3 0x08 ++#define GL520_ALARM_TEMP1 0x10 ++#define GL520_ALARM_FAN1 0x20 ++#define GL520_ALARM_FAN2 0x40 ++#define GL520_ALARM_TEMP2 0x80 ++#define GL520_ALARM_VIN4 0x80 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected GL520. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table gl520_dir_table_template[] = { ++ {GL520_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vin}, ++ {GL520_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vin}, ++ {GL520_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vin}, ++ {GL520_SYSCTL_VIN4, "vin4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vin}, ++ {GL520_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vin}, ++ {GL520_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_vid}, ++ {GL520_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_fan}, ++ {GL520_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_fan}, ++ {GL520_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_temp}, ++ {GL520_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_temp}, ++ {GL520_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_fan_div}, ++ {GL520_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_alarms}, ++ {GL520_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_beep}, ++ {GL520_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_fan1off}, ++ {GL520_SYSCTL_CONFIG, "config", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &gl520_config}, ++ {0} ++}; ++ ++static int gl520_id = 0; ++ ++static int gl520_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, gl520_detect); ++} ++ ++static int gl520_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct gl520_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ char client_name[32]; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("gl520sm.o: gl520_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access gl520_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &gl520_driver; ++ new_client->flags = 0; ++ ++ /* Determine the chip type. */ ++ ++ if (gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) { ++ printk ++ ("gl520sm.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ goto ERROR1; ++ } else { ++ kind = gl520sm; ++ } ++ ++ i = gl520_read_value(new_client, GL520_REG_REVISION); ++ if (kind == gl520sm) { ++ type_name = "gl520sm"; ++ sprintf(client_name, "GL520SM Revision %02x chip", i); ++ } else { ++#ifdef DEBUG ++ printk("gl520sm.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = gl520_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ gl520_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the GL520SM chip */ ++ data->alarm_mask = 0xff; ++ gl520_init_client(new_client); ++ if (data->two_temps) ++ data->voltage_max[4] = data->voltage_min[4] = ++ data->voltage[4] = 0; ++ else ++ data->temp_hyst[1] = data->temp_over[1] = ++ data->temp[1] = 0; ++ ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++ ++/* Called when we have found a new GL520SM. */ ++static void gl520_init_client(struct i2c_client *client) ++{ ++ struct gl520_data *data = (struct gl520_data *)(client->data); ++ u8 oldconf, conf; ++ ++ conf = oldconf = gl520_read_value(client, GL520_REG_CONF); ++ data->two_temps = !(conf & 0x10); ++ ++ /* If IRQ# is disabled, we can safely force comparator mode */ ++ if (!(conf & 0x20)) ++ conf &= 0xf7; ++ ++ /* Enable monitoring if needed */ ++ conf |= 0x40; ++ ++ if (conf != oldconf) ++ gl520_write_value(client, GL520_REG_CONF, conf); ++} ++ ++static int gl520_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct gl520_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("gl520sm.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* Registers 0x07 to 0x0c are word-sized, others are byte-sized ++ GL520 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int gl520_read_value(struct i2c_client *client, u8 reg) ++{ ++ if ((reg >= 0x07) && (reg <= 0x0c)) ++ return swab16(i2c_smbus_read_word_data(client, reg)); ++ else ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* Registers 0x07 to 0x0c are word-sized, others are byte-sized ++ GL520 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if ((reg >= 0x07) && (reg <= 0x0c)) ++ return i2c_smbus_write_word_data(client, reg, swab16(value)); ++ else ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void gl520_update_client(struct i2c_client *client) ++{ ++ struct gl520_data *data = client->data; ++ int val; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting gl520 update\n"); ++#endif ++ ++ data->alarms = gl520_read_value(client, GL520_REG_INT); ++ data->beeps = gl520_read_value(client, GL520_REG_ALARM); ++ data->vid = gl520_read_value(client, GL520_REG_VID) & 0x1f; ++ ++ val = gl520_read_value(client, GL520_REG_VDD_LIMIT); ++ data->voltage_min[0] = val & 0xff; ++ data->voltage_max[0] = (val >> 8) & 0xff; ++ val = gl520_read_value(client, GL520_REG_VIN1_LIMIT); ++ data->voltage_min[1] = val & 0xff; ++ data->voltage_max[1] = (val >> 8) & 0xff; ++ val = gl520_read_value(client, GL520_REG_VIN2_LIMIT); ++ data->voltage_min[2] = val & 0xff; ++ data->voltage_max[2] = (val >> 8) & 0xff; ++ val = gl520_read_value(client, GL520_REG_VIN3_LIMIT); ++ data->voltage_min[3] = val & 0xff; ++ data->voltage_max[3] = (val >> 8) & 0xff; ++ ++ val = gl520_read_value(client, GL520_REG_FAN_COUNT); ++ data->fan[0] = (val >> 8) & 0xff; ++ data->fan[1] = val & 0xff; ++ ++ val = gl520_read_value(client, GL520_REG_FAN_LIMIT); ++ data->fan_min[0] = (val >> 8) & 0xff; ++ data->fan_min[1] = val & 0xff; ++ ++ data->temp[0] = gl520_read_value(client, GL520_REG_TEMP1); ++ data->temp_over[0] = ++ gl520_read_value(client, GL520_REG_TEMP1_OVER); ++ data->temp_hyst[0] = ++ gl520_read_value(client, GL520_REG_TEMP1_HYST); ++ ++ val = gl520_read_value(client, GL520_REG_MISC); ++ data->fan_div[0] = (val >> 6) & 0x03; ++ data->fan_div[1] = (val >> 4) & 0x03; ++ ++ data->alarms &= data->alarm_mask; ++ ++ val = gl520_read_value(client, GL520_REG_CONF); ++ data->beep_enable = (val >> 2) & 1; ++ ++ data->voltage[0] = gl520_read_value(client, GL520_REG_VDD); ++ data->voltage[1] = ++ gl520_read_value(client, GL520_REG_VIN1); ++ data->voltage[2] = ++ gl520_read_value(client, GL520_REG_VIN2); ++ data->voltage[3] = ++ gl520_read_value(client, GL520_REG_VIN3); ++ ++ /* Temp1 and Vin4 are the same input */ ++ if (data->two_temps) { ++ data->temp[1] = ++ gl520_read_value(client, GL520_REG_TEMP2); ++ data->temp_over[1] = ++ gl520_read_value(client, GL520_REG_TEMP2_OVER); ++ data->temp_hyst[1] = ++ gl520_read_value(client, GL520_REG_TEMP2_HYST); ++ } else { ++ data->voltage[4] = ++ gl520_read_value(client, GL520_REG_VIN4); ++ data->voltage_min[4] = ++ gl520_read_value(client, GL520_REG_VIN4_MIN); ++ data->voltage_max[4] = ++ gl520_read_value(client, GL520_REG_VIN4_MAX); ++ } ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++void gl520_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ int nr = ctl_name - GL520_SYSCTL_TEMP1; ++ int regnr; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if ((nr == 1) && (!data->two_temps)) ++ return; ++ regnr = ++ nr == 0 ? GL520_REG_TEMP1_OVER : GL520_REG_TEMP2_OVER; ++ if (*nrels_mag >= 1) { ++ data->temp_over[nr] = TEMP_TO_REG(results[0]); ++ gl520_write_value(client, regnr, ++ data->temp_over[nr]); ++ } ++ regnr = ++ nr == 0 ? GL520_REG_TEMP1_HYST : GL520_REG_TEMP2_HYST; ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = TEMP_TO_REG(results[1]); ++ gl520_write_value(client, regnr, ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void gl520_vin(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ int nr = ctl_name - GL520_SYSCTL_VDD; ++ int regnr, old = 0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : ++ VDD_FROM_REG(data->voltage_min[nr]); ++ results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : ++ VDD_FROM_REG(data->voltage_max[nr]); ++ results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : ++ VDD_FROM_REG(data->voltage[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (nr != 4) { ++ regnr = ++ nr == 0 ? GL520_REG_VDD_LIMIT : nr == ++ 1 ? GL520_REG_VIN1_LIMIT : nr == ++ 2 ? GL520_REG_VIN2_LIMIT : ++ GL520_REG_VIN3_LIMIT; ++ if (*nrels_mag == 1) ++ old = ++ gl520_read_value(client, ++ regnr) & 0xff00; ++ if (*nrels_mag >= 2) { ++ data->voltage_max[nr] = ++ nr ? IN_TO_REG(results[1]) : ++ VDD_TO_REG(results[1]); ++ old = data->voltage_max[nr] << 8; ++ } ++ if (*nrels_mag >= 1) { ++ data->voltage_min[nr] = ++ nr ? IN_TO_REG(results[0]) : ++ VDD_TO_REG(results[0]); ++ old |= data->voltage_min[nr]; ++ gl520_write_value(client, regnr, old); ++ } ++ } else if (!data->two_temps) { ++ if (*nrels_mag == 1) ++ gl520_write_value(client, ++ GL520_REG_VIN4_MIN, ++ IN_TO_REG(results[0])); ++ if (*nrels_mag >= 2) ++ gl520_write_value(client, ++ GL520_REG_VIN4_MAX, ++ IN_TO_REG(results[1])); ++ } ++ } ++} ++ ++ ++void gl520_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ int nr = ctl_name - GL520_SYSCTL_FAN1; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr] = FAN_TO_REG(results[0], ++ DIV_FROM_REG(data-> ++ fan_div ++ [nr])); ++ old = ++ gl520_read_value(client, GL520_REG_FAN_LIMIT); ++ ++ if (nr == 0) { ++ old = ++ (old & 0x00ff) | (data-> ++ fan_min[nr] << 8); ++ if (results[0] == 0) ++ data->alarm_mask &= ~0x20; ++ else ++ data->alarm_mask |= 0x20; ++ } else { ++ old = (old & 0xff00) | data->fan_min[nr]; ++ if (results[0] == 0) ++ data->alarm_mask &= ~0x40; ++ else ++ data->alarm_mask |= 0x40; ++ } ++ gl520_write_value(client, GL520_REG_FAN_LIMIT, ++ old); ++ } ++ } ++} ++ ++ ++void gl520_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void gl520_beep(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); ++ results[1] = BEEPS_FROM_REG(data->beeps); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); ++ gl520_write_value(client, GL520_REG_CONF, ++ (gl520_read_value(client, ++ GL520_REG_CONF) ++ & 0xfb) | (data-> ++ beep_enable << 2)); ++ } ++ if (*nrels_mag >= 2) { ++ data->beeps = ++ BEEPS_TO_REG(results[1]) & data->alarm_mask; ++ gl520_write_value(client, GL520_REG_ALARM, ++ data->beeps); ++ } ++ } ++} ++ ++ ++void gl520_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ int old; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = gl520_read_value(client, GL520_REG_MISC); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0xcf) | (data->fan_div[1] << 4); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0x3f) | (data->fan_div[0] << 6); ++ } ++ gl520_write_value(client, GL520_REG_MISC, old); ++ } ++} ++ ++void gl520_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ gl520_update_client(client); ++ results[0] = VID_FROM_REG(data->vid); ++ *nrels_mag = 1; ++ } ++} ++ ++void gl520_fan1off(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int old; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = ++ ((gl520_read_value(client, GL520_REG_MISC) & 0x04) != ++ 0); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ old = ++ gl520_read_value(client, ++ GL520_REG_MISC) & 0xfb; ++ if (results[0]) ++ old |= 0x04; ++ gl520_write_value(client, GL520_REG_MISC, old); ++ } ++ } ++} ++ ++void gl520_config(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct gl520_data *data = client->data; ++ int old; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = ++ ((gl520_read_value(client, GL520_REG_CONF) & 0x10) == ++ 0); ++ data->two_temps = results[0]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ old = ++ gl520_read_value(client, ++ GL520_REG_CONF) & 0xef; ++ if (!results[1]) { ++ old |= 0x10; ++ data->two_temps = 0; ++ data->temp_hyst[1] = data->temp_over[1] = ++ data->temp[1] = 0; ++ } else { ++ data->two_temps = 1; ++ data->voltage_max[4] = data->voltage_min[4] = ++ data->voltage[4] = 0; ++ } ++ gl520_write_value(client, GL520_REG_CONF, old); ++ } ++ } ++} ++ ++static int __init sm_gl520sm_init(void) ++{ ++ printk("gl520sm.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&gl520_driver); ++} ++ ++static void __exit sm_gl520sm_exit(void) ++{ ++ i2c_del_driver(&gl520_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Kyösti Mälkki "); ++MODULE_DESCRIPTION("GL520SM driver"); ++ ++module_init(sm_gl520sm_init); ++module_exit(sm_gl520sm_exit); +--- linux-old/drivers/sensors/it87.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/it87.c Mon Dec 13 20:18:47 2004 +@@ -0,0 +1,1128 @@ ++/* ++ it87.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring. ++ ++ Supports: IT8705F Super I/O chip w/LPC interface ++ IT8712F Super I/O chup w/LPC interface & SMbus ++ Sis950 A clone of the IT8705F ++ ++ Copyright (c) 2001 Chris Gauthron ++ Largely inspired by lm78.c of the same package ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ djg@pdp8.net David Gesswein 7/18/01 ++ Modified to fix bug with not all alarms enabled. ++ Added ability to read battery voltage and select temperature sensor ++ type at module load time. ++*/ ++ ++/* ++ michael.hufer@gmx.de Michael Hufer 09/07/03 ++ Modified configure (enable/disable) chip reset at module load time. ++ Added ability to read and set fan pwm registers and the smart ++ guardian (sg) features of the chip. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_4(it87, it8705, it8712, sis950); ++ ++ ++#define REG 0x2e /* The register to read/write */ ++#define DEV 0x07 /* Register: Logical device select */ ++#define VAL 0x2f /* The value to read/write */ ++#define PME 0x04 /* The device with the fan registers in it */ ++#define DEVID 0x20 /* Register: Device ID */ ++ ++static inline void ++superio_outb(int reg, int val) ++{ ++ outb(reg, REG); ++ outb(val, VAL); ++} ++ ++static inline int ++superio_inb(int reg) ++{ ++ outb(reg, REG); ++ return inb(VAL); ++} ++ ++static inline void ++superio_select(void) ++{ ++ outb(DEV, REG); ++ outb(PME, VAL); ++} ++ ++static inline void ++superio_enter(void) ++{ ++ outb(0x87, REG); ++ outb(0x01, REG); ++ outb(0x55, REG); ++ outb(0x55, REG); ++} ++ ++static inline void ++superio_exit(void) ++{ ++ outb(0x02, REG); ++ outb(0x02, VAL); ++} ++ ++/* just IT8712F for now - this should be extended to support the other ++ chips as well */ ++#define IT87_DEVID_MATCH(id) ((id) == 0x8712) ++ ++#define IT87_ACT_REG 0x30 ++#define IT87_BASE_REG 0x60 ++ ++/* Update battery voltage after every reading if true */ ++static int update_vbat = 0; ++ ++/* Reset the registers on init */ ++static int reset = 0; ++ ++/* Many IT87 constants specified below */ ++ ++/* Length of ISA address segment */ ++#define IT87_EXTENT 8 ++ ++/* Where are the ISA address/data registers relative to the base address */ ++#define IT87_ADDR_REG_OFFSET 5 ++#define IT87_DATA_REG_OFFSET 6 ++ ++/*----- The IT87 registers -----*/ ++ ++#define IT87_REG_CONFIG 0x00 ++ ++#define IT87_REG_ALARM1 0x01 ++#define IT87_REG_ALARM2 0x02 ++#define IT87_REG_ALARM3 0x03 ++ ++#define IT87_REG_VID 0x0a ++#define IT87_REG_FAN_DIV 0x0b ++ ++#define IT87_REG_FAN(nr) (0x0c + (nr)) ++#define IT87_REG_FAN_MIN(nr) (0x0f + (nr)) ++#define IT87_REG_FAN_CTRL 0x13 ++ ++/* pwm and smart guardian registers */ ++ ++#define IT87_REG_FAN_ONOFF 0x14 ++#define IT87_REG_PWM(nr) (0x14 + (nr)) ++#define IT87_REG_SG_TL_OFF(nr) (0x58 + (nr)*8) ++#define IT87_REG_SG_TL_LOW(nr) (0x59 + (nr)*8) ++#define IT87_REG_SG_TL_MED(nr) (0x5a + (nr)*8) ++#define IT87_REG_SG_TL_HI(nr) (0x5b + (nr)*8) ++#define IT87_REG_SG_TL_OVR(nr) (0x5c + (nr)*8) ++#define IT87_REG_SG_PWM_LOW(nr) (0x5d + (nr)*8) ++#define IT87_REG_SG_PWM_MED(nr) (0x5e + (nr)*8) ++#define IT87_REG_SG_PWM_HI(nr) (0x5f + (nr)*8) ++ ++/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ ++ ++#define IT87_REG_VIN(nr) (0x20 + (nr)) ++#define IT87_REG_TEMP(nr) (0x28 + (nr)) ++ ++#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) ++#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) ++#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2) ++#define IT87_REG_TEMP_LOW(nr) (0x3f + (nr) * 2) ++ ++#define IT87_REG_I2C_ADDR 0x48 ++ ++#define IT87_REG_VIN_ENABLE 0x50 ++#define IT87_REG_TEMP_ENABLE 0x51 ++ ++#define IT87_REG_CHIPID 0x58 ++ ++/* sensor pin types */ ++#define UNUSED 0 ++#define THERMISTOR 2 ++#define PIIDIODE 3 ++ ++/* Conversions. Limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) ++ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),-127,127)) ++#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) ++ ++#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ ++ 205-(val)*5) ++#define ALARMS_FROM_REG(val) (val) ++ ++extern inline u8 DIV_TO_REG(long val) ++{ ++ u8 i; ++ for( i = 0; i <= 7; i++ ) ++ { ++ if( val>>i == 1 ) ++ return i; ++ } ++ return 1; ++} ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++/* For each registered IT87, we need to keep some data in memory. That ++ data is pointed to by it87_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new it87 client is ++ allocated. */ ++struct it87_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[9]; /* Register value */ ++ u8 in_max[9]; /* Register value */ ++ u8 in_min[9]; /* Register value */ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u8 temp[3]; /* Register value */ ++ u8 temp_high[3]; /* Register value */ ++ u8 temp_low[3]; /* Register value */ ++ u8 fan_div[3]; /* Register encoding, shifted right */ ++ u8 vid; /* Register encoding, combined */ ++ u32 alarms; /* Register encoding, combined */ ++ u8 pwm[3]; /* Register value */ ++ u8 fan_ctl[2]; /* Register encoding */ ++ u8 sg_tl[3][5]; /* Register value */ ++ u8 sg_pwm[3][3]; /* Register value */ ++ u8 sens[3]; /* 2 = Thermistor, ++ 3 = PII/Celeron diode */ ++}; ++ ++ ++static int it87_attach_adapter(struct i2c_adapter *adapter); ++static int it87_find(int *address); ++static int it87_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int it87_detach_client(struct i2c_client *client); ++ ++static int it87_read_value(struct i2c_client *client, u8 register); ++static int it87_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void it87_update_client(struct i2c_client *client); ++static void it87_init_client(struct i2c_client *client); ++ ++ ++static void it87_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void it87_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_fan_ctl(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_sgpwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_sgtl(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void it87_sens(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static struct i2c_driver it87_driver = { ++ .owner = THIS_MODULE, ++ .name = "IT87xx sensor driver", ++ .id = I2C_DRIVERID_IT87, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = it87_attach_adapter, ++ .detach_client = it87_detach_client, ++}; ++ ++static int it87_id = 0; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define IT87_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define IT87_SYSCTL_IN1 1001 ++#define IT87_SYSCTL_IN2 1002 ++#define IT87_SYSCTL_IN3 1003 ++#define IT87_SYSCTL_IN4 1004 ++#define IT87_SYSCTL_IN5 1005 ++#define IT87_SYSCTL_IN6 1006 ++#define IT87_SYSCTL_IN7 1007 ++#define IT87_SYSCTL_IN8 1008 ++#define IT87_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define IT87_SYSCTL_FAN2 1102 ++#define IT87_SYSCTL_FAN3 1103 ++#define IT87_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define IT87_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ ++#define IT87_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ ++#define IT87_SYSCTL_VID 1300 /* Volts * 100 */ ++#define IT87_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define IT87_SYSCTL_ALARMS 2004 /* bitvector */ ++ ++#define IT87_SYSCTL_PWM1 1401 ++#define IT87_SYSCTL_PWM2 1402 ++#define IT87_SYSCTL_PWM3 1403 ++#define IT87_SYSCTL_FAN_CTL 1501 ++#define IT87_SYSCTL_FAN_ON_OFF 1502 ++#define IT87_SYSCTL_SENS1 1601 /* 1, 2, or Beta (3000-5000) */ ++#define IT87_SYSCTL_SENS2 1602 ++#define IT87_SYSCTL_SENS3 1603 ++ ++#define IT87_ALARM_IN0 0x000100 ++#define IT87_ALARM_IN1 0x000200 ++#define IT87_ALARM_IN2 0x000400 ++#define IT87_ALARM_IN3 0x000800 ++#define IT87_ALARM_IN4 0x001000 ++#define IT87_ALARM_IN5 0x002000 ++#define IT87_ALARM_IN6 0x004000 ++#define IT87_ALARM_IN7 0x008000 ++#define IT87_ALARM_FAN1 0x0001 ++#define IT87_ALARM_FAN2 0x0002 ++#define IT87_ALARM_FAN3 0x0004 ++#define IT87_ALARM_TEMP1 0x00010000 ++#define IT87_ALARM_TEMP2 0x00020000 ++#define IT87_ALARM_TEMP3 0x00040000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected IT87. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table it87_dir_table_template[] = { ++ {IT87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_in}, ++ {IT87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan}, ++ {IT87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan}, ++ {IT87_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan}, ++ {IT87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_temp}, ++ {IT87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_temp}, ++ {IT87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_temp}, ++ {IT87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_vid}, ++ {IT87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan_div}, ++ {IT87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_alarms}, ++ {IT87_SYSCTL_FAN_CTL, "fan_ctl", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan_ctl}, ++ {IT87_SYSCTL_FAN_ON_OFF, "fan_on_off", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_fan_ctl}, ++ {IT87_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_pwm}, ++ {IT87_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_pwm}, ++ {IT87_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_pwm}, ++ {IT87_SYSCTL_PWM1, "sg_pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgpwm}, ++ {IT87_SYSCTL_PWM2, "sg_pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgpwm}, ++ {IT87_SYSCTL_PWM3, "sg_pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgpwm}, ++ {IT87_SYSCTL_PWM1, "sg_tl1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgtl}, ++ {IT87_SYSCTL_PWM2, "sg_tl2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgtl}, ++ {IT87_SYSCTL_PWM3, "sg_tl3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sgtl}, ++ {IT87_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sens}, ++ {IT87_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sens}, ++ {IT87_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &it87_sens}, ++ {0} ++}; ++ ++ ++/* This function is called when: ++ * it87_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and it87_driver is still present) */ ++static int it87_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, it87_detect); ++} ++ ++static int it87_find(int *address) ++{ ++ u16 val; ++ ++ superio_enter(); ++ val = (superio_inb(DEVID) << 8) | ++ superio_inb(DEVID + 1); ++ if (!IT87_DEVID_MATCH(val)) { ++ superio_exit(); ++ return -ENODEV; ++ } ++ ++ superio_select(); ++ val = (superio_inb(IT87_BASE_REG) << 8) | ++ superio_inb(IT87_BASE_REG + 1); ++ superio_exit(); ++ *address = val & ~(IT87_EXTENT - 1); ++ if (*address == 0) { ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++/* This function is called by i2c_detect */ ++int it87_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct it87_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ int is_isa = i2c_is_isa_adapter(adapter); ++ ++ if (!is_isa ++ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return 0; ++ ++ if (is_isa ++ && check_region(address, IT87_EXTENT)) ++ return 0; ++ ++ /* Probe whether there is anything available on this address. Already ++ done for SMBus clients */ ++ if (is_isa && kind < 0) { ++#define REALLY_SLOW_IO ++ /* We need the timeouts for at least some IT87-like chips. ++ But only if we read 'undefined' registers. */ ++ i = inb_p(address + 1); ++ if (inb_p(address + 2) != i ++ || inb_p(address + 3) != i ++ || inb_p(address + 7) != i) ++ return -ENODEV; ++#undef REALLY_SLOW_IO ++ ++ /* Let's just hope nothing breaks here */ ++ i = inb_p(address + 5) & 0x7f; ++ outb_p(~i & 0x7f, address + 5); ++ if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { ++ outb_p(i, address + 5); ++ return -ENODEV; ++ } ++ } ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access it87_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct it87_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ if (is_isa) ++ init_MUTEX(&data->lock); ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &it87_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) ++ || (!is_isa ++ && it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) { ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ i = it87_read_value(new_client, IT87_REG_CHIPID); ++ if (i == 0x90) { ++ kind = it87; ++ } ++ else { ++ if (kind == 0) ++ printk ++ ("it87.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ } ++ ++ if (kind == it87) { ++ type_name = "it87"; ++ client_name = "IT87 chip"; ++ } /* else if (kind == it8712) { ++ type_name = "it8712"; ++ client_name = "IT87-J chip"; ++ } */ else { ++#ifdef DEBUG ++ printk("it87.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Reserve the ISA region */ ++ if (is_isa) ++ request_region(address, IT87_EXTENT, type_name); ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = it87_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ it87_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the IT87 chip */ ++ it87_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ if (is_isa) ++ release_region(address, IT87_EXTENT); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int it87_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct it87_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("it87.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ if(i2c_is_isa_client(client)) ++ release_region(client->addr, IT87_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* The SMBus locks itself, but ISA access must be locked explicitely! ++ We don't want to lock the whole ISA bus, so we lock each client ++ separately. ++ We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the IT87 access and should not be necessary. ++ There are some ugly typecasts here, but the good new is - they should ++ nowhere else be necessary! */ ++static int it87_read_value(struct i2c_client *client, u8 reg) ++{ ++ int res; ++ if (i2c_is_isa_client(client)) { ++ down(&(((struct it87_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); ++ res = inb_p(client->addr + IT87_DATA_REG_OFFSET); ++ up(&(((struct it87_data *) (client->data))->lock)); ++ return res; ++ } else ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* The SMBus locks itself, but ISA access muse be locked explicitely! ++ We don't want to lock the whole ISA bus, so we lock each client ++ separately. ++ We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the IT87 access and should not be necessary. ++ There are some ugly typecasts here, but the good new is - they should ++ nowhere else be necessary! */ ++static int it87_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ if (i2c_is_isa_client(client)) { ++ down(&(((struct it87_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); ++ outb_p(value, client->addr + IT87_DATA_REG_OFFSET); ++ up(&(((struct it87_data *) (client->data))->lock)); ++ return 0; ++ } else ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new IT87. */ ++static void it87_init_client(struct i2c_client *client) ++{ ++ int tmp; ++ ++ if (reset) { ++ /* Reset all except Watchdog values and last conversion values ++ This sets fan-divs to 2, among others */ ++ it87_write_value(client, IT87_REG_CONFIG, 0x80); ++ } ++ ++ /* Check if temperature channnels are reset manually or by some reason */ ++ tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); ++ if ((tmp & 0x3f) == 0) { ++ /* Temp1,Temp3=thermistor; Temp2=thermal diode */ ++ tmp = (tmp & 0xc0) | 0x2a; ++ it87_write_value(client, IT87_REG_TEMP_ENABLE, tmp); ++ } ++ ++ /* Check if voltage monitors are reset manually or by some reason */ ++ tmp = it87_read_value(client, IT87_REG_VIN_ENABLE); ++ if ((tmp & 0xff) == 0) { ++ /* Enable all voltage monitors */ ++ it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff); ++ } ++ ++ /* Check if tachometers are reset manually or by some reason */ ++ tmp = it87_read_value(client, IT87_REG_FAN_CTRL); ++ if ((tmp & 0x70) == 0) { ++ /* Enable all fan tachometers */ ++ tmp = (tmp & 0x8f) | 0x70; ++ it87_write_value(client, IT87_REG_FAN_CTRL, tmp); ++ } ++ ++ /* Start monitoring */ ++ it87_write_value(client, IT87_REG_CONFIG, ++ (it87_read_value(client, IT87_REG_CONFIG) & 0x36) ++ | (update_vbat ? 0x41 : 0x01)); ++} ++ ++static void it87_update_client(struct i2c_client *client) ++{ ++ struct it87_data *data = client->data; ++ int i, tmp, tmp2; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++ if (update_vbat) { ++ /* Cleared after each update, so reenable. Value ++ returned by this read will be previous value */ ++ it87_write_value(client, IT87_REG_CONFIG, ++ it87_read_value(client, IT87_REG_CONFIG) | 0x40); ++ } ++ for (i = 0; i <= 7; i++) { ++ data->in[i] = ++ it87_read_value(client, IT87_REG_VIN(i)); ++ data->in_min[i] = ++ it87_read_value(client, IT87_REG_VIN_MIN(i)); ++ data->in_max[i] = ++ it87_read_value(client, IT87_REG_VIN_MAX(i)); ++ } ++ data->in[8] = ++ it87_read_value(client, IT87_REG_VIN(8)); ++ /* VBAT sensor doesn't have limit registers, set ++ to min and max value */ ++ data->in_min[8] = 0; ++ data->in_max[8] = 255; ++ ++ for (i = 1; i <= 3; i++) { ++ data->fan[i - 1] = ++ it87_read_value(client, IT87_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ it87_read_value(client, IT87_REG_FAN_MIN(i)); ++ } ++ for (i = 1; i <= 3; i++) { ++ data->temp[i - 1] = ++ it87_read_value(client, IT87_REG_TEMP(i)); ++ data->temp_high[i - 1] = ++ it87_read_value(client, IT87_REG_TEMP_HIGH(i)); ++ data->temp_low[i - 1] = ++ it87_read_value(client, IT87_REG_TEMP_LOW(i)); ++ } ++ ++ /* The 8705 does not have VID capability */ ++ /*if (data->type == it8712) { ++ data->vid = it87_read_value(client, IT87_REG_VID); ++ data->vid &= 0x1f; ++ } ++ else */ { ++ data->vid = 0x1f; ++ } ++ ++ i = it87_read_value(client, IT87_REG_FAN_DIV); ++ data->fan_div[0] = i & 0x07; ++ data->fan_div[1] = (i >> 3) & 0x07; ++ data->fan_div[2] = ( (i&0x40)==0x40 ? 3 : 1 ); ++ ++ for( i = 1; i <= 3; i++ ) { ++ data->pwm[i-1] = it87_read_value(client, IT87_REG_PWM(i)); ++ data->sg_tl[i-1][0] = it87_read_value(client, IT87_REG_SG_TL_OFF(i)); ++ data->sg_tl[i-1][1] = it87_read_value(client, IT87_REG_SG_TL_LOW(i)); ++ data->sg_tl[i-1][2] = it87_read_value(client, IT87_REG_SG_TL_MED(i)); ++ data->sg_tl[i-1][3] = it87_read_value(client, IT87_REG_SG_TL_HI(i)); ++ data->sg_tl[i-1][4] = it87_read_value(client, IT87_REG_SG_TL_OVR(i)); ++ data->sg_pwm[i-1][0] = it87_read_value(client, IT87_REG_SG_PWM_LOW(i)); ++ data->sg_pwm[i-1][1] = it87_read_value(client, IT87_REG_SG_PWM_MED(i)); ++ data->sg_pwm[i-1][2] = it87_read_value(client, IT87_REG_SG_PWM_HI(i)); ++ } ++ data->alarms = ++ it87_read_value(client, IT87_REG_ALARM1) | ++ (it87_read_value(client, IT87_REG_ALARM2) << 8) | ++ (it87_read_value(client, IT87_REG_ALARM3) << 16); ++ data->fan_ctl[0] = it87_read_value(client, IT87_REG_FAN_CTRL); ++ data->fan_ctl[1] = it87_read_value(client, IT87_REG_FAN_ONOFF); ++ ++ tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); ++ for(i = 0; i < 3; i++) { ++ tmp2 = (tmp >> i) & 0x09; ++ if(tmp2 == 0x01) ++ data->sens[i] = PIIDIODE; ++ else if(tmp2 == 0x08) ++ data->sens[i] = THERMISTOR; ++ else ++ data->sens[i] = UNUSED; ++ } ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ - Each function must return the magnitude (power of 10 to divide the ++ data with) if it is called with operation==SENSORS_PROC_REAL_INFO. ++ - It must put a maximum of *nrels elements in results reflecting the ++ data of this file, and set *nrels to the number it actually put ++ in it, if operation==SENSORS_PROC_REAL_READ. ++ - Finally, it must get upto *nrels elements from results and write them ++ to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void it87_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ it87_write_value(client, IT87_REG_VIN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ it87_write_value(client, IT87_REG_VIN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void it87_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ it87_write_value(client, IT87_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void it87_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_TEMP1 + 1; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_high[nr - 1]); ++ results[1] = TEMP_FROM_REG(data->temp_low[nr - 1]); ++ results[2] = TEMP_FROM_REG(data->temp[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_high[nr - 1] = TEMP_TO_REG(results[0]); ++ it87_write_value(client, IT87_REG_TEMP_HIGH(nr), ++ data->temp_high[nr - 1]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_low[nr - 1] = TEMP_TO_REG(results[1]); ++ it87_write_value(client, IT87_REG_TEMP_LOW(nr), ++ data->temp_low[nr - 1]); ++ } ++ } ++} ++ ++void it87_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_PWM1 + 1; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = data->pwm[nr - 1]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->pwm[nr - 1] = results[0]; ++ it87_write_value(client, IT87_REG_PWM(nr), data->pwm[nr - 1]); ++ } ++ } ++} ++ ++void it87_sgpwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_PWM1 + 1; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = data->sg_pwm[nr - 1][0]; ++ results[1] = data->sg_pwm[nr - 1][1]; ++ results[2] = data->sg_pwm[nr - 1][2]; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->sg_pwm[nr - 1][0] = results[0]; ++ it87_write_value(client, IT87_REG_SG_PWM_LOW(nr), data->sg_pwm[nr - 1][0]); ++ } ++ if (*nrels_mag >= 2) { ++ data->sg_pwm[nr - 1][1] = results[1]; ++ it87_write_value(client, IT87_REG_SG_PWM_MED(nr), data->sg_pwm[nr - 1][1]); ++ } ++ if (*nrels_mag >= 3) { ++ data->sg_pwm[nr - 1][2] = results[2]; ++ it87_write_value(client, IT87_REG_SG_PWM_HI(nr), data->sg_pwm[nr - 1][2]); ++ } ++ } ++} ++ ++void it87_sgtl(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = ctl_name - IT87_SYSCTL_PWM1 + 1; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = TEMP_FROM_REG(data->sg_tl[nr - 1][0]); ++ results[1] = TEMP_FROM_REG(data->sg_tl[nr - 1][1]); ++ results[2] = TEMP_FROM_REG(data->sg_tl[nr - 1][2]); ++ results[3] = TEMP_FROM_REG(data->sg_tl[nr - 1][3]); ++ results[4] = TEMP_FROM_REG(data->sg_tl[nr - 1][4]); ++ *nrels_mag = 5; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->sg_tl[nr - 1][0] = TEMP_TO_REG(results[0]); ++ it87_write_value(client, IT87_REG_SG_TL_OFF(nr), data->sg_tl[nr - 1][0]); ++ } ++ if (*nrels_mag >= 2) { ++ data->sg_tl[nr - 1][1] = TEMP_TO_REG(results[1]); ++ it87_write_value(client, IT87_REG_SG_TL_LOW(nr), data->sg_tl[nr - 1][1]); ++ } ++ if (*nrels_mag >= 3) { ++ data->sg_tl[nr - 1][2] = TEMP_TO_REG(results[2]); ++ it87_write_value(client, IT87_REG_SG_TL_MED(nr), data->sg_tl[nr - 1][2]); ++ } ++ if (*nrels_mag >= 4) { ++ data->sg_tl[nr - 1][3] = TEMP_TO_REG(results[3]); ++ it87_write_value(client, IT87_REG_SG_TL_HI(nr), data->sg_tl[nr - 1][3]); ++ } ++ if (*nrels_mag >= 5) { ++ data->sg_tl[nr - 1][4] = TEMP_TO_REG(results[4]); ++ it87_write_value(client, IT87_REG_SG_TL_OVR(nr), data->sg_tl[nr - 1][4]); ++ } ++ } ++} ++ ++void it87_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = VID_FROM_REG(data->vid); ++ *nrels_mag = 1; ++ } ++} ++ ++void it87_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void it87_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ results[2] = DIV_FROM_REG(data->fan_div[2]);; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = it87_read_value(client, IT87_REG_FAN_DIV); ++ if (*nrels_mag >= 3) { ++ data->fan_div[2] = DIV_TO_REG(results[2]); ++ if( data->fan[2]!=3 ) { ++ data->fan_div[2] = 1; ++ old = (old & 0xbf); ++ } else { ++ old = (old | 0x40); ++ } ++ } ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0xc3) | (data->fan_div[1] << 3); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xf8) | data->fan_div[0]; ++ it87_write_value(client, IT87_REG_FAN_DIV, old); ++ } ++ } ++} ++ ++void it87_fan_ctl(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int index = ctl_name - IT87_SYSCTL_FAN_CTL; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ it87_update_client(client); ++ results[0] = data->fan_ctl[index]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_ctl[index] = results[0]; ++ if( index == 0 ) ++ it87_write_value(client, IT87_REG_FAN_CTRL, data->fan_ctl[index] ); ++ else ++ it87_write_value(client, IT87_REG_FAN_ONOFF, data->fan_ctl[index] ); ++ } ++ } ++} ++ ++void it87_sens(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct it87_data *data = client->data; ++ int nr = 1 + ctl_name - IT87_SYSCTL_SENS1; ++ u8 tmp, val1, val2; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->sens[nr - 1]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ val1 = 0x01 << (nr - 1); ++ val2 = 0x08 << (nr - 1); ++ tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); ++ switch (results[0]) { ++ case PIIDIODE: ++ tmp &= ~ val2; ++ tmp |= val1; ++ break; ++ case THERMISTOR: ++ tmp &= ~ val1; ++ tmp |= val2; ++ break; ++ case UNUSED: ++ tmp &= ~ val1; ++ tmp &= ~ val2; ++ break; ++ default: ++ printk(KERN_ERR "it87.o: Invalid sensor type %ld; " ++ "must be 0 (unused), 2 (thermistor) " ++ "or 3 (diode)\n", results[0]); ++ return; ++ } ++ it87_write_value(client, ++ IT87_REG_TEMP_ENABLE, tmp); ++ data->sens[nr - 1] = results[0]; ++ } ++ } ++} ++ ++static int __init sm_it87_init(void) ++{ ++ int addr; ++ ++ printk("it87.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ if (!it87_find(&addr)) { ++ normal_isa[0] = addr; ++ } ++ return i2c_add_driver(&it87_driver); ++} ++ ++static void __exit sm_it87_exit(void) ++{ ++ i2c_del_driver(&it87_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Chris Gauthron "); ++MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); ++MODULE_PARM(update_vbat, "i"); ++MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); ++MODULE_PARM(reset, "i"); ++MODULE_PARM_DESC(reset, "Reset the chip's registers, default no"); ++ ++module_init(sm_it87_init); ++module_exit(sm_it87_exit); +--- linux-old/drivers/sensors/lm75.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm75.c Mon Dec 13 20:18:47 2004 +@@ -0,0 +1,331 @@ ++/* ++ lm75.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include "lm75.h" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(lm75); ++ ++/* Many LM75 constants specified below */ ++ ++/* The LM75 registers */ ++#define LM75_REG_TEMP 0x00 ++#define LM75_REG_CONF 0x01 ++#define LM75_REG_TEMP_HYST 0x02 ++#define LM75_REG_TEMP_OS 0x03 ++ ++/* Each client has this additional data */ ++struct lm75_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u16 temp, temp_os, temp_hyst; /* Register values */ ++}; ++ ++static int lm75_attach_adapter(struct i2c_adapter *adapter); ++static int lm75_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void lm75_init_client(struct i2c_client *client); ++static int lm75_detach_client(struct i2c_client *client); ++ ++static int lm75_read_value(struct i2c_client *client, u8 reg); ++static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); ++static void lm75_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm75_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver lm75_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM75 sensor chip driver", ++ .id = I2C_DRIVERID_LM75, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm75_attach_adapter, ++ .detach_client = lm75_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define LM75_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected LM75. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table lm75_dir_table_template[] = { ++ {LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm75_temp}, ++ {0} ++}; ++ ++static int lm75_id = 0; ++ ++static int lm75_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm75_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int lm75_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct lm75_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA)) ++ goto error0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access lm75_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto error0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm75_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. There is no identification- ++ dedicated register so we have to rely on several tricks: ++ unused bits, registers cycling over 8-address boundaries, ++ addresses 0x04-0x07 returning the last read value. ++ The cycling+unused addresses combination is not tested, ++ since it would significantly slow the detection down and would ++ hardly add any value. */ ++ if (kind < 0) { ++ int cur, conf, hyst, os; ++ ++ /* Unused addresses */ ++ cur = i2c_smbus_read_word_data(new_client, 0); ++ conf = i2c_smbus_read_byte_data(new_client, 1); ++ hyst = i2c_smbus_read_word_data(new_client, 2); ++ if (i2c_smbus_read_word_data(new_client, 4) != hyst ++ || i2c_smbus_read_word_data(new_client, 5) != hyst ++ || i2c_smbus_read_word_data(new_client, 6) != hyst ++ || i2c_smbus_read_word_data(new_client, 7) != hyst) ++ goto error1; ++ os = i2c_smbus_read_word_data(new_client, 3); ++ if (i2c_smbus_read_word_data(new_client, 4) != os ++ || i2c_smbus_read_word_data(new_client, 5) != os ++ || i2c_smbus_read_word_data(new_client, 6) != os ++ || i2c_smbus_read_word_data(new_client, 7) != os) ++ goto error1; ++ ++ /* Unused bits */ ++ if (conf & 0xe0) ++ goto error1; ++ ++ /* Addresses cycling */ ++ for (i = 8; i < 0xff; i += 8) ++ if (i2c_smbus_read_byte_data(new_client, i + 1) != conf ++ || i2c_smbus_read_word_data(new_client, i + 2) != hyst ++ || i2c_smbus_read_word_data(new_client, i + 3) != os) ++ goto error1; ++ } ++ ++ /* Determine the chip type - only one kind supported! */ ++ if (kind <= 0) ++ kind = lm75; ++ ++ if (kind == lm75) { ++ type_name = "lm75"; ++ client_name = "LM75 chip"; ++ } else { ++ pr_debug("lm75.o: Internal error: unknown kind (%d)?!?", kind); ++ goto error1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = lm75_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto error3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ lm75_dir_table_template)) < 0) { ++ err = i; ++ goto error4; ++ } ++ data->sysctl_id = i; ++ ++ lm75_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ error4: ++ i2c_detach_client(new_client); ++ error3: ++ error1: ++ kfree(data); ++ error0: ++ return err; ++} ++ ++static int lm75_detach_client(struct i2c_client *client) ++{ ++ struct lm75_data *data = client->data; ++ ++ i2c_deregister_entry(data->sysctl_id); ++ i2c_detach_client(client); ++ kfree(client->data); ++ return 0; ++} ++ ++/* All registers are word-sized, except for the configuration register. ++ LM75 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int lm75_read_value(struct i2c_client *client, u8 reg) ++{ ++ if (reg == LM75_REG_CONF) ++ return i2c_smbus_read_byte_data(client, reg); ++ else ++ return swab16(i2c_smbus_read_word_data(client, reg)); ++} ++ ++/* All registers are word-sized, except for the configuration register. ++ LM75 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if (reg == LM75_REG_CONF) ++ return i2c_smbus_write_byte_data(client, reg, value); ++ else ++ return i2c_smbus_write_word_data(client, reg, swab16(value)); ++} ++ ++static void lm75_init_client(struct i2c_client *client) ++{ ++ /* Initialize the LM75 chip */ ++ lm75_write_value(client, LM75_REG_CONF, 0); ++} ++ ++static void lm75_update_client(struct i2c_client *client) ++{ ++ struct lm75_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ pr_debug("Starting lm75 update\n"); ++ ++ data->temp = lm75_read_value(client, LM75_REG_TEMP); ++ data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS); ++ data->temp_hyst = ++ lm75_read_value(client, LM75_REG_TEMP_HYST); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void lm75_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm75_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm75_update_client(client); ++ results[0] = LM75_TEMP_FROM_REG(data->temp_os); ++ results[1] = LM75_TEMP_FROM_REG(data->temp_hyst); ++ results[2] = LM75_TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_os = LM75_TEMP_TO_REG(results[0]); ++ lm75_write_value(client, LM75_REG_TEMP_OS, ++ data->temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = LM75_TEMP_TO_REG(results[1]); ++ lm75_write_value(client, LM75_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++static int __init sm_lm75_init(void) ++{ ++ printk(KERN_INFO "lm75.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&lm75_driver); ++} ++ ++static void __exit sm_lm75_exit(void) ++{ ++ i2c_del_driver(&lm75_driver); ++} ++ ++MODULE_AUTHOR("Frodo Looijaard "); ++MODULE_DESCRIPTION("LM75 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_lm75_init); ++module_exit(sm_lm75_exit); +--- linux-old/drivers/sensors/lm75.h Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm75.h Mon Dec 13 20:18:47 2004 +@@ -0,0 +1,49 @@ ++/* ++ lm75.h - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2003 Mark M. Hoffman ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This file contains common code for encoding/decoding LM75 type ++ temperature readings, which are emulated by many of the chips ++ we support. As the user is unlikely to load more than one driver ++ which contains this code, we don't worry about the wasted space. ++*/ ++ ++#include ++ ++/* straight from the datasheet */ ++#define LM75_TEMP_MIN (-550) ++#define LM75_TEMP_MAX 1250 ++ ++/* TEMP: 0.1C/bit (-55C to +125C) ++ REG: (0.5C/bit, two's complement) << 7 */ ++static inline u16 LM75_TEMP_TO_REG(int temp) ++{ ++ int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); ++ ntemp += (ntemp<0 ? -2 : 2); ++ return (u16)((ntemp / 5) << 7); ++} ++ ++static inline int LM75_TEMP_FROM_REG(u16 reg) ++{ ++ /* use integer division instead of equivalent right shift to ++ guarantee arithmetic shift and preserve the sign */ ++ return ((s16)reg / 128) * 5; ++} ++ +--- linux-old/drivers/sensors/lm78.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm78.c Mon Dec 13 20:18:47 2004 +@@ -0,0 +1,729 @@ ++/* ++ lm78.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_3(lm78, lm78j, lm79); ++ ++/* Many LM78 constants specified below */ ++ ++/* Length of ISA address segment */ ++#define LM78_EXTENT 8 ++ ++/* Where are the ISA address/data registers relative to the base address */ ++#define LM78_ADDR_REG_OFFSET 5 ++#define LM78_DATA_REG_OFFSET 6 ++ ++/* The LM78 registers */ ++#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) ++#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) ++#define LM78_REG_IN(nr) (0x20 + (nr)) ++ ++#define LM78_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define LM78_REG_FAN(nr) (0x27 + (nr)) ++ ++#define LM78_REG_TEMP 0x27 ++#define LM78_REG_TEMP_OVER 0x39 ++#define LM78_REG_TEMP_HYST 0x3a ++ ++#define LM78_REG_ALARM1 0x41 ++#define LM78_REG_ALARM2 0x42 ++ ++#define LM78_REG_VID_FANDIV 0x47 ++ ++#define LM78_REG_CONFIG 0x40 ++#define LM78_REG_CHIPID 0x49 ++#define LM78_REG_I2C_ADDR 0x48 ++ ++ ++/* Conversions. Limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) ++ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),0,255)) ++#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) ++ ++#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ ++ 205-(val)*5) ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++/* There are some complications in a module like this. First off, LM78 chips ++ may be both present on the SMBus and the ISA bus, and we have to handle ++ those cases separately at some places. Second, there might be several ++ LM78 chips available (well, actually, that is probably never done; but ++ it is a clean illustration of how to handle a case like that). Finally, ++ a specific chip may be attached to *both* ISA and SMBus, and we would ++ not like to detect it double. Fortunately, in the case of the LM78 at ++ least, a register tells us what SMBus address we are on, so that helps ++ a bit - except if there could be more than one SMBus. Groan. No solution ++ for this yet. */ ++ ++/* This module may seem overly long and complicated. In fact, it is not so ++ bad. Quite a lot of bookkeeping is done. A real driver can often cut ++ some corners. */ ++ ++/* For each registered LM78, we need to keep some data in memory. That ++ data is pointed to by lm78_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new lm78 client is ++ allocated. */ ++struct lm78_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u8 temp; /* Register value */ ++ u8 temp_over; /* Register value */ ++ u8 temp_hyst; /* Register value */ ++ u8 fan_div[3]; /* Register encoding, shifted right */ ++ u8 vid; /* Register encoding, combined */ ++ u16 alarms; /* Register encoding, combined */ ++}; ++ ++ ++static int lm78_attach_adapter(struct i2c_adapter *adapter); ++static int lm78_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int lm78_detach_client(struct i2c_client *client); ++ ++static int lm78_read_value(struct i2c_client *client, u8 register); ++static int lm78_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void lm78_update_client(struct i2c_client *client); ++static void lm78_init_client(struct i2c_client *client); ++ ++ ++static void lm78_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void lm78_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm78_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm78_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm78_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm78_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static struct i2c_driver lm78_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM78(-J) and LM79 sensor driver", ++ .id = I2C_DRIVERID_LM78, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm78_attach_adapter, ++ .detach_client = lm78_detach_client, ++}; ++ ++static int lm78_id = 0; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define LM78_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define LM78_SYSCTL_IN1 1001 ++#define LM78_SYSCTL_IN2 1002 ++#define LM78_SYSCTL_IN3 1003 ++#define LM78_SYSCTL_IN4 1004 ++#define LM78_SYSCTL_IN5 1005 ++#define LM78_SYSCTL_IN6 1006 ++#define LM78_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define LM78_SYSCTL_FAN2 1102 ++#define LM78_SYSCTL_FAN3 1103 ++#define LM78_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ ++#define LM78_SYSCTL_VID 1300 /* Volts * 100 */ ++#define LM78_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define LM78_SYSCTL_ALARMS 2001 /* bitvector */ ++ ++#define LM78_ALARM_IN0 0x0001 ++#define LM78_ALARM_IN1 0x0002 ++#define LM78_ALARM_IN2 0x0004 ++#define LM78_ALARM_IN3 0x0008 ++#define LM78_ALARM_IN4 0x0100 ++#define LM78_ALARM_IN5 0x0200 ++#define LM78_ALARM_IN6 0x0400 ++#define LM78_ALARM_FAN1 0x0040 ++#define LM78_ALARM_FAN2 0x0080 ++#define LM78_ALARM_FAN3 0x0800 ++#define LM78_ALARM_TEMP 0x0010 ++#define LM78_ALARM_BTI 0x0020 ++#define LM78_ALARM_CHAS 0x1000 ++#define LM78_ALARM_FIFO 0x2000 ++#define LM78_ALARM_SMI_IN 0x4000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected LM78. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table lm78_dir_table_template[] = { ++ {LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_in}, ++ {LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_fan}, ++ {LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_fan}, ++ {LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_fan}, ++ {LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_temp}, ++ {LM78_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_vid}, ++ {LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_fan_div}, ++ {LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm78_alarms}, ++ {0} ++}; ++ ++ ++/* This function is called when: ++ * lm78_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and lm78_driver is still present) */ ++static int lm78_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm78_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int lm78_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct lm78_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ int is_isa = i2c_is_isa_adapter(adapter); ++ ++ if (!is_isa ++ && !i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) goto ++ ERROR0; ++ ++ if (is_isa) { ++ if (check_region(address, LM78_EXTENT)) ++ goto ERROR0; ++ } ++ ++ /* Probe whether there is anything available on this address. Already ++ done for SMBus clients */ ++ if (kind < 0) { ++ if (is_isa) { ++ ++#define REALLY_SLOW_IO ++ /* We need the timeouts for at least some LM78-like chips. But only ++ if we read 'undefined' registers. */ ++ i = inb_p(address + 1); ++ if (inb_p(address + 2) != i) ++ goto ERROR0; ++ if (inb_p(address + 3) != i) ++ goto ERROR0; ++ if (inb_p(address + 7) != i) ++ goto ERROR0; ++#undef REALLY_SLOW_IO ++ ++ /* Let's just hope nothing breaks here */ ++ i = inb_p(address + 5) & 0x7f; ++ outb_p(~i & 0x7f, address + 5); ++ if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { ++ outb_p(i, address + 5); ++ return 0; ++ } ++ } ++ } ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access lm78_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ if (is_isa) ++ init_MUTEX(&data->lock); ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm78_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) ++ goto ERROR1; ++ if (!is_isa ++ && (lm78_read_value(new_client, LM78_REG_I2C_ADDR) != ++ address)) goto ERROR1; ++ } ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ i = lm78_read_value(new_client, LM78_REG_CHIPID); ++ if (i == 0x00 || i == 0x20) ++ kind = lm78; ++ else if (i == 0x40) ++ kind = lm78j; ++ else if ((i & 0xfe) == 0xc0) ++ kind = lm79; ++ else { ++ if (kind == 0) ++ printk ++ ("lm78.o: Ignoring 'force' parameter for unknown chip at " ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ goto ERROR1; ++ } ++ } ++ ++ if (kind == lm78) { ++ type_name = "lm78"; ++ client_name = "LM78 chip"; ++ } else if (kind == lm78j) { ++ type_name = "lm78-j"; ++ client_name = "LM78-J chip"; ++ } else if (kind == lm79) { ++ type_name = "lm79"; ++ client_name = "LM79 chip"; ++ } else { ++#ifdef DEBUG ++ printk("lm78.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Reserve the ISA region */ ++ if (is_isa) ++ request_region(address, LM78_EXTENT, type_name); ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = lm78_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ lm78_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the LM78 chip */ ++ lm78_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ if (is_isa) ++ release_region(address, LM78_EXTENT); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int lm78_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct lm78_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("lm78.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ if(i2c_is_isa_client(client)) ++ release_region(client->addr, LM78_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* The SMBus locks itself, but ISA access must be locked explicitely! ++ We don't want to lock the whole ISA bus, so we lock each client ++ separately. ++ We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the LM78 access and should not be necessary. ++ There are some ugly typecasts here, but the good new is - they should ++ nowhere else be necessary! */ ++static int lm78_read_value(struct i2c_client *client, u8 reg) ++{ ++ int res; ++ if (i2c_is_isa_client(client)) { ++ down(&(((struct lm78_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); ++ res = inb_p(client->addr + LM78_DATA_REG_OFFSET); ++ up(&(((struct lm78_data *) (client->data))->lock)); ++ return res; ++ } else ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* The SMBus locks itself, but ISA access muse be locked explicitely! ++ We don't want to lock the whole ISA bus, so we lock each client ++ separately. ++ We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the LM78 access and should not be necessary. ++ There are some ugly typecasts here, but the good new is - they should ++ nowhere else be necessary! */ ++static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ if (i2c_is_isa_client(client)) { ++ down(&(((struct lm78_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); ++ outb_p(value, client->addr + LM78_DATA_REG_OFFSET); ++ up(&(((struct lm78_data *) (client->data))->lock)); ++ return 0; ++ } else ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new LM78. */ ++static void lm78_init_client(struct i2c_client *client) ++{ ++ u8 config = lm78_read_value(client, LM78_REG_CONFIG); ++ ++ /* Start monitoring */ ++ if (!(config & 0x01)) ++ lm78_write_value(client, LM78_REG_CONFIG, ++ (config & 0xf7) | 0x01); ++} ++ ++static void lm78_update_client(struct i2c_client *client) ++{ ++ struct lm78_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting lm78 update\n"); ++#endif ++ for (i = 0; i <= 6; i++) { ++ data->in[i] = ++ lm78_read_value(client, LM78_REG_IN(i)); ++ data->in_min[i] = ++ lm78_read_value(client, LM78_REG_IN_MIN(i)); ++ data->in_max[i] = ++ lm78_read_value(client, LM78_REG_IN_MAX(i)); ++ } ++ for (i = 1; i <= 3; i++) { ++ data->fan[i - 1] = ++ lm78_read_value(client, LM78_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ lm78_read_value(client, LM78_REG_FAN_MIN(i)); ++ } ++ data->temp = lm78_read_value(client, LM78_REG_TEMP); ++ data->temp_over = ++ lm78_read_value(client, LM78_REG_TEMP_OVER); ++ data->temp_hyst = ++ lm78_read_value(client, LM78_REG_TEMP_HYST); ++ i = lm78_read_value(client, LM78_REG_VID_FANDIV); ++ data->vid = i & 0x0f; ++ if (data->type == lm79) ++ data->vid |= ++ (lm78_read_value(client, LM78_REG_CHIPID) & ++ 0x01) << 4; ++ else ++ data->vid |= 0x10; ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + ++ (lm78_read_value(client, LM78_REG_ALARM2) << 8); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ ++ data->fan_div[2] = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void lm78_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ int nr = ctl_name - LM78_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ lm78_write_value(client, LM78_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ lm78_write_value(client, LM78_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void lm78_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ int nr = ctl_name - LM78_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data-> ++ fan_div[nr - 1])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr - ++ 1])); ++ lm78_write_value(client, LM78_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void lm78_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ lm78_write_value(client, LM78_REG_TEMP_OVER, ++ data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ lm78_write_value(client, LM78_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void lm78_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = VID_FROM_REG(data->vid); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++/* Note: we save and restore the fan minimum here, because its value is ++ determined in part by the fan divisor. This follows the principle of ++ least surprise: the user doesn't expect the fan minimum to change just ++ because the divisor changed. */ ++void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm78_data *data = client->data; ++ int old, min; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm78_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ results[2] = 2; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = lm78_read_value(client, LM78_REG_VID_FANDIV); ++ if (*nrels_mag >= 2) { ++ min = FAN_FROM_REG(data->fan_min[1], ++ DIV_FROM_REG(data->fan_div[1])); ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ data->fan_min[1] = FAN_TO_REG(min, ++ DIV_FROM_REG(data->fan_div[1])); ++ lm78_write_value(client, LM78_REG_FAN_MIN(2), ++ data->fan_min[1]); ++ } ++ if (*nrels_mag >= 1) { ++ min = FAN_FROM_REG(data->fan_min[0], ++ DIV_FROM_REG(data->fan_div[0])); ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ data->fan_min[0] = FAN_TO_REG(min, ++ DIV_FROM_REG(data->fan_div[0])); ++ lm78_write_value(client, LM78_REG_FAN_MIN(1), ++ data->fan_min[0]); ++ lm78_write_value(client, LM78_REG_VID_FANDIV, old); ++ } ++ } ++} ++ ++static int __init sm_lm78_init(void) ++{ ++ printk("lm78.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&lm78_driver); ++} ++ ++static void __exit sm_lm78_exit(void) ++{ ++ i2c_del_driver(&lm78_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard "); ++MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); ++ ++module_init(sm_lm78_init); ++module_exit(sm_lm78_exit); +--- linux-old/drivers/sensors/lm80.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm80.c Mon Dec 13 20:18:48 2004 +@@ -0,0 +1,606 @@ ++/* ++ lm80.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ and Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(lm80); ++ ++/* Many LM80 constants specified below */ ++ ++/* The LM80 registers */ ++#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) ++#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) ++#define LM80_REG_IN(nr) (0x20 + (nr)) ++ ++#define LM80_REG_FAN1_MIN 0x3c ++#define LM80_REG_FAN2_MIN 0x3d ++#define LM80_REG_FAN1 0x28 ++#define LM80_REG_FAN2 0x29 ++ ++#define LM80_REG_TEMP 0x27 ++#define LM80_REG_TEMP_HOT_MAX 0x38 ++#define LM80_REG_TEMP_HOT_HYST 0x39 ++#define LM80_REG_TEMP_OS_MAX 0x3a ++#define LM80_REG_TEMP_OS_HYST 0x3b ++ ++#define LM80_REG_CONFIG 0x00 ++#define LM80_REG_ALARM1 0x01 ++#define LM80_REG_ALARM2 0x02 ++#define LM80_REG_MASK1 0x03 ++#define LM80_REG_MASK2 0x04 ++#define LM80_REG_FANDIV 0x05 ++#define LM80_REG_RES 0x06 ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define IN_TO_REG(val) (SENSORS_LIMIT((val),0,255)) ++#define IN_FROM_REG(val) (val) ++ ++static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:\ ++ (val)==255?0:1350000/((div)*(val))) ++ ++static inline long TEMP_FROM_REG(u16 temp) ++{ ++ long res; ++ ++ temp >>= 4; ++ if (temp < 0x0800) ++ res = 625 * (long) temp; ++ else ++ res = ((long) temp - 0x01000) * 625; ++ ++ return res / 100; ++} ++ ++#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100) ++ ++#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-50)/100):\ ++ ((val)+50)/100), \ ++ 0,255) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++ ++struct lm80_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u16 temp; /* Register values, shifted right */ ++ u8 temp_hot_max; /* Register value */ ++ u8 temp_hot_hyst; /* Register value */ ++ u8 temp_os_max; /* Register value */ ++ u8 temp_os_hyst; /* Register value */ ++ u16 alarms; /* Register encoding, combined */ ++}; ++ ++ ++ ++static int lm80_attach_adapter(struct i2c_adapter *adapter); ++static int lm80_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int lm80_detach_client(struct i2c_client *client); ++ ++static int lm80_read_value(struct i2c_client *client, u8 reg); ++static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value); ++static void lm80_update_client(struct i2c_client *client); ++static void lm80_init_client(struct i2c_client *client); ++ ++ ++static void lm80_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void lm80_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm80_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm80_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm80_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int lm80_id = 0; ++ ++static struct i2c_driver lm80_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM80 sensor driver", ++ .id = I2C_DRIVERID_LM80, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm80_attach_adapter, ++ .detach_client = lm80_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define LM80_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define LM80_SYSCTL_IN1 1001 ++#define LM80_SYSCTL_IN2 1002 ++#define LM80_SYSCTL_IN3 1003 ++#define LM80_SYSCTL_IN4 1004 ++#define LM80_SYSCTL_IN5 1005 ++#define LM80_SYSCTL_IN6 1006 ++#define LM80_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define LM80_SYSCTL_FAN2 1102 ++#define LM80_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ ++#define LM80_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define LM80_SYSCTL_ALARMS 2001 /* bitvector */ ++ ++#define LM80_ALARM_IN0 0x0001 ++#define LM80_ALARM_IN1 0x0002 ++#define LM80_ALARM_IN2 0x0004 ++#define LM80_ALARM_IN3 0x0008 ++#define LM80_ALARM_IN4 0x0010 ++#define LM80_ALARM_IN5 0x0020 ++#define LM80_ALARM_IN6 0x0040 ++#define LM80_ALARM_FAN1 0x0400 ++#define LM80_ALARM_FAN2 0x0800 ++#define LM80_ALARM_TEMP_HOT 0x0100 ++#define LM80_ALARM_TEMP_OS 0x2000 ++#define LM80_ALARM_CHAS 0x1000 ++#define LM80_ALARM_BTI 0x0200 ++#define LM80_ALARM_INT_IN 0x0080 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected LM80. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table lm80_dir_table_template[] = { ++ {LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_in}, ++ {LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_fan}, ++ {LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_fan}, ++ {LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_temp}, ++ {LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_fan_div}, ++ {LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm80_alarms}, ++ {0} ++}; ++ ++static int lm80_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm80_detect); ++} ++ ++int lm80_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, cur; ++ struct i2c_client *new_client; ++ struct lm80_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("lm80.o: lm80_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access lm80_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm80_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. It is lousy. */ ++ if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0) ++ goto ERROR1; ++ for (i = 0x2a; i <= 0x3d; i++) { ++ cur = i2c_smbus_read_byte_data(new_client, i); ++ if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur) ++ || (i2c_smbus_read_byte_data(new_client, i + 0x80) != ++ cur) ++ || (i2c_smbus_read_byte_data(new_client, i + 0xc0) != ++ cur)) goto ERROR1; ++ } ++ ++ /* Determine the chip type - only one kind supported! */ ++ if (kind <= 0) ++ kind = lm80; ++ ++ if (kind == lm80) { ++ type_name = "lm80"; ++ client_name = "LM80 chip"; ++ } else { ++#ifdef DEBUG ++ printk("lm80.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = lm80_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ lm80_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ lm80_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int lm80_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct lm80_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("lm80.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int lm80_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new LM80. */ ++static void lm80_init_client(struct i2c_client *client) ++{ ++ /* Reset all except Watchdog values and last conversion values ++ This sets fan-divs to 2, among others. This makes most other ++ initializations unnecessary */ ++ lm80_write_value(client, LM80_REG_CONFIG, 0x80); ++ /* Set 11-bit temperature resolution */ ++ lm80_write_value(client, LM80_REG_RES, 0x08); ++ ++ /* Start monitoring */ ++ lm80_write_value(client, LM80_REG_CONFIG, 0x01); ++} ++ ++static void lm80_update_client(struct i2c_client *client) ++{ ++ struct lm80_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > 2 * HZ) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting lm80 update\n"); ++#endif ++ for (i = 0; i <= 6; i++) { ++ data->in[i] = ++ lm80_read_value(client, LM80_REG_IN(i)); ++ data->in_min[i] = ++ lm80_read_value(client, LM80_REG_IN_MIN(i)); ++ data->in_max[i] = ++ lm80_read_value(client, LM80_REG_IN_MAX(i)); ++ } ++ data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); ++ data->fan_min[0] = ++ lm80_read_value(client, LM80_REG_FAN1_MIN); ++ data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); ++ data->fan_min[1] = ++ lm80_read_value(client, LM80_REG_FAN2_MIN); ++ ++ data->temp = ++ (lm80_read_value(client, LM80_REG_TEMP) << 8) | ++ (lm80_read_value(client, LM80_REG_RES) & 0xf0); ++ data->temp_os_max = ++ lm80_read_value(client, LM80_REG_TEMP_OS_MAX); ++ data->temp_os_hyst = ++ lm80_read_value(client, LM80_REG_TEMP_OS_HYST); ++ data->temp_hot_max = ++ lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); ++ data->temp_hot_hyst = ++ lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); ++ ++ i = lm80_read_value(client, LM80_REG_FANDIV); ++ data->fan_div[0] = (i >> 2) & 0x03; ++ data->fan_div[1] = (i >> 4) & 0x03; ++ data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + ++ (lm80_read_value(client, LM80_REG_ALARM2) << 8); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void lm80_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm80_data *data = client->data; ++ int nr = ctl_name - LM80_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm80_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ lm80_write_value(client, LM80_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ lm80_write_value(client, LM80_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void lm80_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm80_data *data = client->data; ++ int nr = ctl_name - LM80_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm80_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data-> ++ fan_div[nr - 1])); ++ results[1] = ++ FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr - ++ 1])); ++ lm80_write_value(client, ++ nr == ++ 1 ? LM80_REG_FAN1_MIN : ++ LM80_REG_FAN2_MIN, ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void lm80_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm80_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm80_update_client(client); ++ results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst); ++ results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max); ++ results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); ++ results[4] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 5; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]); ++ lm80_write_value(client, LM80_REG_TEMP_HOT_MAX, ++ data->temp_hot_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hot_hyst = ++ TEMP_LIMIT_TO_REG(results[1]); ++ lm80_write_value(client, LM80_REG_TEMP_HOT_HYST, ++ data->temp_hot_hyst); ++ } ++ if (*nrels_mag >= 3) { ++ data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]); ++ lm80_write_value(client, LM80_REG_TEMP_OS_MAX, ++ data->temp_os_max); ++ } ++ if (*nrels_mag >= 4) { ++ data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]); ++ lm80_write_value(client, LM80_REG_TEMP_OS_HYST, ++ data->temp_os_hyst); ++ } ++ } ++} ++ ++void lm80_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm80_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm80_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm80_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm80_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ results[2] = 2; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = lm80_read_value(client, LM80_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0xcf) | (data->fan_div[1] << 4); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xf3) | (data->fan_div[0] << 2); ++ lm80_write_value(client, LM80_REG_FANDIV, old); ++ } ++ } ++} ++ ++static int __init sm_lm80_init(void) ++{ ++ printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&lm80_driver); ++} ++ ++static void __exit sm_lm80_exit(void) ++{ ++ i2c_del_driver(&lm80_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("LM80 driver"); ++ ++module_init(sm_lm80_init); ++module_exit(sm_lm80_exit); +--- linux-old/drivers/sensors/lm83.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm83.c Mon Dec 13 20:18:48 2004 +@@ -0,0 +1,513 @@ ++/* ++ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring ++ * Copyright (C) 2003 Jean Delvare ++ * ++ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is ++ * a sensor chip made by National Semiconductor. It reports up to four ++ * temperatures (its own plus up to three external ones) with a 1 deg ++ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained ++ * from National's website at: ++ * http://www.national.com/pf/LM/LM83.html ++ * Since the datasheet omits to give the chip stepping code, I give it ++ * here: 0x03 (at register 0xff). ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* ++ * Addresses to scan ++ * Address is selected using 2 three-level pins, resulting in 9 possible ++ * addresses. ++ */ ++ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, ++ 0x4c, 0x4e, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* ++ * Insmod parameters ++ */ ++ ++SENSORS_INSMOD_1(lm83); ++ ++/* ++ * The LM83 registers ++ * Manufacturer ID is 0x01 for National Semiconductor. ++ */ ++ ++#define LM83_REG_R_MAN_ID 0xFE ++#define LM83_REG_R_CHIP_ID 0xFF ++#define LM83_REG_R_CONFIG 0x03 ++#define LM83_REG_W_CONFIG 0x09 ++#define LM83_REG_R_STATUS1 0x02 ++#define LM83_REG_R_STATUS2 0x35 ++#define LM83_REG_R_LOCAL_TEMP 0x00 ++#define LM83_REG_R_LOCAL_HIGH 0x05 ++#define LM83_REG_W_LOCAL_HIGH 0x0B ++#define LM83_REG_R_REMOTE1_TEMP 0x30 ++#define LM83_REG_R_REMOTE1_HIGH 0x38 ++#define LM83_REG_W_REMOTE1_HIGH 0x50 ++#define LM83_REG_R_REMOTE2_TEMP 0x01 ++#define LM83_REG_R_REMOTE2_HIGH 0x07 ++#define LM83_REG_W_REMOTE2_HIGH 0x0D ++#define LM83_REG_R_REMOTE3_TEMP 0x31 ++#define LM83_REG_R_REMOTE3_HIGH 0x3A ++#define LM83_REG_W_REMOTE3_HIGH 0x52 ++#define LM83_REG_R_TCRIT 0x42 ++#define LM83_REG_W_TCRIT 0x5A ++ ++/* ++ * Conversions and various macros ++ * The LM83 uses signed 8-bit values. ++ */ ++ ++#define TEMP_FROM_REG(val) ((val) > 127 ? (val) - 0x100 : (val)) ++#define TEMP_TO_REG(val) ((val) <= -50 ? -50 + 0x100 : \ ++ (val) >= 127 ? 127 : \ ++ (val) >= 0 ? (val) : \ ++ (val) + 0x100) ++ ++static const u8 LM83_REG_R_TEMP[] = { ++ LM83_REG_R_LOCAL_TEMP, ++ LM83_REG_R_REMOTE1_TEMP, ++ LM83_REG_R_REMOTE2_TEMP, ++ LM83_REG_R_REMOTE3_TEMP ++}; ++ ++static const u8 LM83_REG_R_HIGH[] = { ++ LM83_REG_R_LOCAL_HIGH, ++ LM83_REG_R_REMOTE1_HIGH, ++ LM83_REG_R_REMOTE2_HIGH, ++ LM83_REG_R_REMOTE3_HIGH ++}; ++ ++static const u8 LM83_REG_W_HIGH[] = { ++ LM83_REG_W_LOCAL_HIGH, ++ LM83_REG_W_REMOTE1_HIGH, ++ LM83_REG_W_REMOTE2_HIGH, ++ LM83_REG_W_REMOTE3_HIGH ++}; ++ ++/* ++ * Functions declaration ++ */ ++ ++static int lm83_attach_adapter(struct i2c_adapter *adapter); ++static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned ++ short flags, int kind); ++static int lm83_detach_client(struct i2c_client *client); ++static void lm83_update_client(struct i2c_client *client); ++static void lm83_temp(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++static void lm83_tcrit(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++static void lm83_alarms(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++ ++/* ++ * Driver data (common to all clients) ++ */ ++ ++static struct i2c_driver lm83_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM83 sensor driver", ++ .id = I2C_DRIVERID_LM83, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm83_attach_adapter, ++ .detach_client = lm83_detach_client, ++}; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++ ++struct lm83_data ++{ ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* registers values */ ++ u8 temp[4], temp_high[4], tcrit; ++ u16 alarms; /* bitvector, combined */ ++}; ++ ++/* ++ * Proc entries ++ * These files are created for each detected LM83. ++ */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define LM83_SYSCTL_LOCAL_TEMP 1200 ++#define LM83_SYSCTL_REMOTE1_TEMP 1201 ++#define LM83_SYSCTL_REMOTE2_TEMP 1202 ++#define LM83_SYSCTL_REMOTE3_TEMP 1203 ++#define LM83_SYSCTL_TCRIT 1208 ++#define LM83_SYSCTL_ALARMS 1210 ++ ++#define LM83_ALARM_LOCAL_HIGH 0x0040 ++#define LM83_ALARM_LOCAL_CRIT 0x0001 ++#define LM83_ALARM_REMOTE1_HIGH 0x8000 ++#define LM83_ALARM_REMOTE1_CRIT 0x0100 ++#define LM83_ALARM_REMOTE1_OPEN 0x2000 ++#define LM83_ALARM_REMOTE2_HIGH 0x0010 ++#define LM83_ALARM_REMOTE2_CRIT 0x0002 ++#define LM83_ALARM_REMOTE2_OPEN 0x0004 ++#define LM83_ALARM_REMOTE3_HIGH 0x1000 ++#define LM83_ALARM_REMOTE3_CRIT 0x0200 ++#define LM83_ALARM_REMOTE3_OPEN 0x0400 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++ ++static ctl_table lm83_dir_table_template[] = ++{ ++ {LM83_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp}, ++ {LM83_SYSCTL_REMOTE1_TEMP, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp}, ++ {LM83_SYSCTL_REMOTE2_TEMP, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp}, ++ {LM83_SYSCTL_REMOTE3_TEMP, "temp4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_temp}, ++ {LM83_SYSCTL_TCRIT, "tcrit", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_tcrit}, ++ {LM83_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm83_alarms}, ++ {0} ++}; ++ ++/* ++ * Internal variables ++ */ ++ ++static int lm83_id = 0; ++ ++/* ++ * Real code ++ */ ++ ++static int lm83_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm83_detect); ++} ++ ++/* ++ * The following function does more than just detection. If detection ++ * succeeds, it also registers the new chip. ++ */ ++static int lm83_detect(struct i2c_adapter *adapter, int address, unsigned ++ short flags, int kind) ++{ ++ struct i2c_client *new_client; ++ struct lm83_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) ++ { ++ printk("lm83.o: Called for an ISA bus adapter, aborting.\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ { ++#ifdef DEBUG ++ printk("lm83.o: I2C bus doesn't support byte read mode, " ++ "skipping.\n"); ++#endif ++ return 0; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL))) ++ { ++ printk("lm83.o: Out of memory in lm83_detect (new_client).\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * The common I2C client data is placed right before the ++ * LM83-specific data. The LM83-specific data is pointed to by the ++ * data field from the I2C client data. ++ */ ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm83_driver; ++ new_client->flags = 0; ++ ++ /* ++ * Now we do the remaining detection. A negative kind means that ++ * the driver was loaded with no force parameter (default), so we ++ * must both detect and identify the chip (actually there is only ++ * one possible kind of chip for now, LM83). A zero kind means that ++ * the driver was loaded with the force parameter, the detection ++ * step shall be skipped. A positive kind means that the driver ++ * was loaded with the force parameter and a given kind of chip is ++ * requested, so both the detection and the identification steps ++ * are skipped. ++ */ ++ ++ /* Default to an LM83 if forced */ ++ if (kind == 0) ++ kind = lm83; ++ ++ if (kind < 0) /* detection */ ++ { ++ if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) ++ & 0xA8) != 0x00) ++ || ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) ++ & 0x48) != 0x00) ++ || ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) ++ & 0x41) != 0x00)) ++ { ++#ifdef DEBUG ++ printk(KERN_DEBUG "lm83.o: Detection failed at 0x%02x.\n", ++ address); ++#endif ++ goto ERROR1; ++ } ++ } ++ ++ if (kind <= 0) /* identification */ ++ { ++ u8 man_id, chip_id; ++ ++ man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID); ++ chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID); ++ if (man_id == 0x01) /* National Semiconductor */ ++ { ++ if (chip_id == 0x03) ++ kind = lm83; ++ } ++ } ++ ++ if (kind <= 0) /* identification failed */ ++ { ++ printk("lm83.o: Unsupported chip.\n"); ++ goto ERROR1; ++ } ++ ++ if (kind == lm83) ++ { ++ type_name = "lm83"; ++ client_name = "LM83 chip"; ++ } ++ else ++ { ++ printk("lm83.o: Unknown kind %d.\n", kind); ++ goto ERROR1; ++ } ++ ++ /* ++ * OK, we got a valid chip so we can fill in the remaining client ++ * fields. ++ */ ++ ++ strcpy(new_client->name, client_name); ++ new_client->id = lm83_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* ++ * Tell the I2C layer a new client has arrived. ++ */ ++ ++ if ((err = i2c_attach_client(new_client))) ++ { ++#ifdef DEBUG ++ printk("lm83.o: Failed attaching client.\n"); ++#endif ++ goto ERROR1; ++ } ++ ++ /* ++ * Register a new directory entry. ++ */ ++ ++ if ((err = i2c_register_entry(new_client, type_name, ++ lm83_dir_table_template)) < 0) ++ { ++#ifdef DEBUG ++ printk("lm83.o: Failed registering directory entry.\n"); ++#endif ++ goto ERROR2; ++ } ++ data->sysctl_id = err; ++ ++ /* ++ * Initialize the LM83 chip ++ * (Nothing to do for this one.) ++ */ ++ ++ return 0; ++ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ return err; ++} ++ ++static int lm83_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct lm83_data *) (client->data))->sysctl_id); ++ if ((err = i2c_detach_client(client))) ++ { ++ printk("lm83.o: Client deregistration failed, client not " ++ "detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ return 0; ++} ++ ++static void lm83_update_client(struct i2c_client *client) ++{ ++ struct lm83_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ * 2) || ++ (jiffies < data->last_updated) || !data->valid) ++ { ++ int nr; ++#ifdef DEBUG ++ printk("lm83.o: Updating LM83 data.\n"); ++#endif ++ for (nr = 0; nr < 4 ; nr++) ++ { ++ data->temp[nr] = ++ i2c_smbus_read_byte_data(client, LM83_REG_R_TEMP[nr]); ++ data->temp_high[nr] = ++ i2c_smbus_read_byte_data(client, LM83_REG_R_HIGH[nr]); ++ } ++ data->tcrit = i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT); ++ data->alarms = ++ i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) + ++ (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) << 8); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++static void lm83_temp(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm83_data *data = client->data; ++ int nr = ctl_name - LM83_SYSCTL_LOCAL_TEMP; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm83_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_high[nr]); ++ results[1] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 2; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->temp_high[nr] = TEMP_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr], ++ data->temp_high[nr]); ++ } ++ } ++} ++ ++static void lm83_tcrit(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm83_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm83_update_client(client); ++ results[0] = TEMP_FROM_REG(data->tcrit); ++ *nrels_mag = 1; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->tcrit = TEMP_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT, ++ data->tcrit); ++ } ++ } ++} ++ ++static void lm83_alarms(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm83_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm83_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++static int __init sm_lm83_init(void) ++{ ++ printk(KERN_INFO "lm83.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&lm83_driver); ++} ++ ++static void __exit sm_lm83_exit(void) ++{ ++ i2c_del_driver(&lm83_driver); ++} ++ ++MODULE_AUTHOR("Jean Delvare "); ++MODULE_DESCRIPTION("LM83 sensor driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_lm83_init); ++module_exit(sm_lm83_exit); +--- linux-old/drivers/sensors/lm85.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm85.c Mon Dec 13 20:18:49 2004 +@@ -0,0 +1,2043 @@ ++/* ++ lm85.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ Copyright (c) 2002, 2003 Philip Pokorny ++ Copyright (c) 2003 Margit Schubert-While ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ CHANGELOG ++ ++ 2002-11-13 First patch for LM85 functionality ++ 2002-11-18 LM85 functionality mostly done ++ 2002-12-02 Adding ADM1027 functionality ++ 2002-12-06 Adding ADT7463 functionality ++ 2003-01-09 Code cleanup. ++ Save reserved bits in case they are implemented ++ in a future chip. (Solve problem with lockups ++ on ADM1027 due to chip initialization) ++ Added chip initialization bypass option ++ 2003-02-12 Add THERM asserted counts for ADT7463 ++ Added #ifdef so we can compile against 2.6.5 ++ without updating i2c-ids.h ++ 2003-02-17 Prepare for switch to 2.7.0 development ++ Implement tmin_control for ADT7463 ++ Expose THERM asserted counts to /proc ++ Code cleanup ++ 2003-02-19 Working with Margit and LM_SENSORS developers ++ 2003-02-23 Removed chip initialization entirely ++ Scale voltages in driver at Margit's request ++ Change PWM from 0-100% to 0-255 per LM sensors standard ++ 2003-02-27 Documentation and code cleanups ++ Added this CHANGELOG ++ Print additional precision for temperatures and voltages ++ Many thanks to Margit Schubert-While and Brandt xxxxxx ++ for help testing this version ++ 2003-02-28 More diagnostic messages regarding BIOS setup ++ 2003-03-01 Added Interrupt mask register support. ++ 2003-03-08 Fixed problem with pseudo 16-bit registers ++ Cleaned up some compiler warnings. ++ Fixed problem with Operating Point and THERM counting ++ 2003-03-21 Initial support for EMC6D100 and EMC6D101 chips ++ 2003-06-30 Add support for EMC6D100 extra voltage inputs. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++#ifndef I2C_DRIVERID_LM85 ++#define I2C_DRIVERID_LM85 1039 ++#endif ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_5(lm85b, lm85c, adm1027, adt7463, emc6d100); ++ ++/* Many LM85 constants specified below */ ++ ++/* The LM85 registers */ ++#define LM85_REG_IN(nr) (0x20 + (nr)) ++#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2) ++#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2) ++ ++#define LM85_REG_TEMP(nr) (0x25 + (nr)) ++#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2) ++#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2) ++ ++/* Fan speeds are LSB, MSB (2 bytes) */ ++#define LM85_REG_FAN(nr) (0x28 + (nr) *2) ++#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2) ++ ++#define LM85_REG_PWM(nr) (0x30 + (nr)) ++ ++#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr)) ++ ++#define ADT7463_REG_TMIN_CTL1 0x36 ++#define ADT7463_REG_TMIN_CTL2 0x37 ++#define ADT7463_REG_TMIN_CTL 0x0136 ++ ++#define LM85_REG_DEVICE 0x3d ++#define LM85_REG_COMPANY 0x3e ++#define LM85_REG_VERSTEP 0x3f ++/* These are the recognized values for the above regs */ ++#define LM85_DEVICE_ADX 0x27 ++#define LM85_COMPANY_NATIONAL 0x01 ++#define LM85_COMPANY_ANALOG_DEV 0x41 ++#define LM85_COMPANY_SMSC 0x5c ++#define LM85_VERSTEP_VMASK 0xf0 ++#define LM85_VERSTEP_SMASK 0x0f ++#define LM85_VERSTEP_GENERIC 0x60 ++#define LM85_VERSTEP_LM85C 0x60 ++#define LM85_VERSTEP_LM85B 0x62 ++#define LM85_VERSTEP_ADM1027 0x60 ++#define LM85_VERSTEP_ADT7463 0x62 ++#define LM85_VERSTEP_EMC6D100_A0 0x60 ++#define LM85_VERSTEP_EMC6D100_A1 0x61 ++ ++#define LM85_REG_CONFIG 0x40 ++ ++#define LM85_REG_ALARM1 0x41 ++#define LM85_REG_ALARM2 0x42 ++#define LM85_REG_ALARM 0x0141 ++ ++#define LM85_REG_VID 0x43 ++ ++/* Automated FAN control */ ++#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr)) ++#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr)) ++#define LM85_REG_AFAN_SPIKE1 0x62 ++#define LM85_REG_AFAN_SPIKE2 0x63 ++#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr)) ++#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr)) ++#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr)) ++#define LM85_REG_AFAN_HYST1 0x6d ++#define LM85_REG_AFAN_HYST2 0x6e ++ ++#define LM85_REG_TACH_MODE 0x74 ++#define LM85_REG_SPINUP_CTL 0x75 ++ ++#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr)) ++#define ADM1027_REG_CONFIG2 0x73 ++#define ADM1027_REG_INTMASK1 0x74 ++#define ADM1027_REG_INTMASK2 0x75 ++#define ADM1027_REG_INTMASK 0x0174 ++#define ADM1027_REG_EXTEND_ADC1 0x76 ++#define ADM1027_REG_EXTEND_ADC2 0x77 ++#define ADM1027_REG_EXTEND_ADC 0x0176 ++#define ADM1027_REG_CONFIG3 0x78 ++#define ADM1027_REG_FAN_PPR 0x7b ++ ++#define ADT7463_REG_THERM 0x79 ++#define ADT7463_REG_THERM_LIMIT 0x7A ++#define ADT7463_REG_CONFIG4 0x7D ++ ++#define EMC6D100_REG_SFR 0x7c ++#define EMC6D100_REG_ALARM3 0x7d ++#define EMC6D100_REG_CONF 0x7f ++#define EMC6D100_REG_INT_EN 0x80 ++/* IN5, IN6 and IN7 */ ++#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5)) ++#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2) ++#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2) ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ */ ++ ++/* IN are scaled 1.000 == 0xc0, mag = 3 */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255)) ++#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0) ++#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0)) ++ ++/* IN are scaled acording to built-in resistors */ ++static int lm85_scaling[] = { /* .001 Volts */ ++ 2500, 2250, 3300, 5000, 12000, ++ 3300, 1500, 1800, /* EMC6D100 */ ++ }; ++#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) ++#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255)) ++#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,lm85_scaling[n])) ++#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0)) ++ ++/* FAN speed is measured using 90kHz clock */ ++#define FAN_TO_REG(val) (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534)) ++#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val)) ++ ++/* Temperature is reported in .01 degC increments */ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+50)/100,-127,127)) ++#define TEMPEXT_FROM_REG(val,ext) ((val)*100 + (ext)*25) ++#define TEMP_FROM_REG(val) (TEMPEXT_FROM_REG(val,0)) ++#define EXTTEMP_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127)) ++#define OPPOINT_TO_REG(val) (SENSORS_LIMIT(val,-127,127)) ++#define OPPOINT_FROM_REG(val) (val) ++ ++#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) ++#define PWM_FROM_REG(val) (val) ++ ++#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03) ++ ++/* ZONEs have the following parameters: ++ * Limit (low) temp, 1. degC ++ * Hysteresis (below limit), 1. degC (0-15) ++ * Range of speed control, .1 degC (2-80) ++ * Critical (high) temp, 1. degC ++ * ++ * FAN PWMs have the following parameters: ++ * Reference Zone, 1, 2, 3, etc. ++ * Spinup time, .05 sec ++ * PWM value at limit/low temp, 1 count ++ * PWM Frequency, 1. Hz ++ * PWM is Min or OFF below limit, flag ++ * Invert PWM output, flag ++ * ++ * Some chips filter the temp, others the fan. ++ * Filter constant (or disabled) .1 seconds ++ */ ++ ++/* These are the zone temperature range encodings */ ++static int lm85_range_map[] = { /* .1 degC */ ++ 20, 25, 33, 40, 50, 66, ++ 80, 100, 133, 160, 200, 266, ++ 320, 400, 533, 800 ++ }; ++static int RANGE_TO_REG( int range ) ++{ ++ int i; ++ ++ if( range >= lm85_range_map[15] ) { return 15 ; } ++ for( i = 0 ; i < 15 ; ++i ) ++ if( range <= lm85_range_map[i] ) ++ break ; ++ return( i & 0x0f ); ++} ++#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f]) ++ ++/* These are the Acoustic Enhancement, or Temperature smoothing encodings ++ * NOTE: The enable/disable bit is INCLUDED in these encodings as the ++ * MSB (bit 3, value 8). If the enable bit is 0, the encoded value ++ * is ignored, or set to 0. ++ */ ++static int lm85_smooth_map[] = { /* .1 sec */ ++ 350, 176, 118, 70, 44, 30, 16, 8 ++/* 35.4 * 1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48 */ ++ }; ++static int SMOOTH_TO_REG( int smooth ) ++{ ++ int i; ++ ++ if( smooth <= 0 ) { return 0 ; } /* Disabled */ ++ for( i = 0 ; i < 7 ; ++i ) ++ if( smooth >= lm85_smooth_map[i] ) ++ break ; ++ return( (i & 0x07) | 0x08 ); ++} ++#define SMOOTH_FROM_REG(val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0) ++ ++/* These are the fan spinup delay time encodings */ ++static int lm85_spinup_map[] = { /* .1 sec */ ++ 0, 1, 2, 4, 7, 10, 20, 40 ++ }; ++static int SPINUP_TO_REG( int spinup ) ++{ ++ int i; ++ ++ if( spinup >= lm85_spinup_map[7] ) { return 7 ; } ++ for( i = 0 ; i < 7 ; ++i ) ++ if( spinup <= lm85_spinup_map[i] ) ++ break ; ++ return( i & 0x07 ); ++} ++#define SPINUP_FROM_REG(val) (lm85_spinup_map[(val)&0x07]) ++ ++/* These are the PWM frequency encodings */ ++static int lm85_freq_map[] = { /* .1 Hz */ ++ 100, 150, 230, 300, 380, 470, 620, 980 ++ }; ++static int FREQ_TO_REG( int freq ) ++{ ++ int i; ++ ++ if( freq >= lm85_freq_map[7] ) { return 7 ; } ++ for( i = 0 ; i < 7 ; ++i ) ++ if( freq <= lm85_freq_map[i] ) ++ break ; ++ return( i & 0x07 ); ++} ++#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07]) ++ ++/* Since we can't use strings, I'm abusing these numbers ++ * to stand in for the following meanings: ++ * 1 -- PWM responds to Zone 1 ++ * 2 -- PWM responds to Zone 2 ++ * 3 -- PWM responds to Zone 3 ++ * 23 -- PWM responds to the higher temp of Zone 2 or 3 ++ * 123 -- PWM responds to highest of Zone 1, 2, or 3 ++ * 0 -- PWM is always at 0% (ie, off) ++ * -1 -- PWM is always at 100% ++ * -2 -- PWM responds to manual control ++ */ ++static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 }; ++static int ZONE_TO_REG( int zone ) ++{ ++ int i; ++ ++ for( i = 0 ; i <= 7 ; ++i ) ++ if( zone == lm85_zone_map[i] ) ++ break ; ++ if( i > 7 ) /* Not found. */ ++ i = 3; /* Always 100% */ ++ return( (i & 0x07)<<5 ); ++} ++#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07]) ++ ++#define HYST_TO_REG(val) (SENSORS_LIMIT((-(val)+5)/10,0,15)) ++#define HYST_FROM_REG(val) (-(val)*10) ++ ++#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127)) ++#define OFFSET_FROM_REG(val) ((val)*25) ++ ++#define PPR_MASK(fan) (0x03<<(fan *2)) ++#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2)) ++#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1) ++ ++/* sensors_vid.h defines vid_from_reg() */ ++#define VID_FROM_REG(val,vrm) (vid_from_reg((val),(vrm))) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++/* When converting to REG, we need to fixup the carry-over bit */ ++#define INTMASK_FROM_REG(val) (val) ++#define INTMASK_TO_REG(val) (SENSORS_LIMIT((val)|((val)&0xff00?0x80:0),0,65535)) ++ ++/* Unlike some other drivers we DO NOT set initial limits. Use ++ * the config file to set limits. Some users have reported ++ * motherboards shutting down when we set limits in a previous ++ * version of this driver. This may be caused by APM/ACPI ++ * detecting an out-of-limit condition when we had the wrong ++ * limits set. ++ */ ++ ++/* Typically used with Pentium 4 systems v9.1 VRM spec */ ++#define LM85_INIT_VRM 91 ++ ++/* Chip sampling rates ++ * ++ * Some sensors are not updated more frequently than once per second ++ * so it doesn't make sense to read them more often than that. ++ * We cache the results and return the saved data if the driver ++ * is called again before a second has elapsed. ++ * ++ * Also, there is significant configuration data for this chip ++ * given the automatic PWM fan control that is possible. There ++ * are about 47 bytes of config data to only 22 bytes of actual ++ * readings. So, we keep the config data up to date in the cache ++ * when it is written and only sample it once every 5 *minutes* ++ */ ++#define LM85_DATA_INTERVAL (1 * HZ) ++#define LM85_CONFIG_INTERVAL (5 * 60 * HZ) ++ ++/* For each registered LM85, we need to keep some data in memory. That ++ data is pointed to by client->data. The structure itself is ++ dynamically allocated, when a new lm85 client is allocated. */ ++ ++/* LM85 can automatically adjust fan speeds based on temperature ++ * This structure encapsulates an entire Zone config. There are ++ * three zones (one for each temperature input) on the lm85 ++ */ ++struct lm85_zone { ++ s8 limit; /* Low temp limit */ ++ u8 hyst; /* Low limit hysteresis. (0-15) */ ++ u8 range; /* Temp range, encoded */ ++ s8 critical; /* "All fans ON" temp limit */ ++}; ++ ++struct lm85_autofan { ++ u8 config; /* Register value */ ++ u8 freq; /* PWM frequency, encoded */ ++ u8 min_pwm; /* Minimum PWM value, encoded */ ++ u8 min_off; /* Min PWM or OFF below "limit", flag */ ++}; ++ ++struct lm85_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ int valid; /* !=0 if following fields are valid */ ++ unsigned long last_reading; /* In jiffies */ ++ unsigned long last_config; /* In jiffies */ ++ ++ u8 in[8]; /* Register value */ ++ u8 in_max[8]; /* Register value */ ++ u8 in_min[8]; /* Register value */ ++ s8 temp[3]; /* Register value */ ++ s8 temp_min[3]; /* Register value */ ++ s8 temp_max[3]; /* Register value */ ++ s8 temp_offset[3]; /* Register value */ ++ u16 fan[4]; /* Register value */ ++ u16 fan_min[4]; /* Register value */ ++ u8 pwm[3]; /* Register value */ ++ u8 spinup_ctl; /* Register encoding, combined */ ++ u8 tach_mode; /* Register encoding, combined */ ++ u16 extend_adc; /* Register value */ ++ u8 fan_ppr; /* Register value */ ++ u8 smooth[3]; /* Register encoding */ ++ u8 vid; /* Register value */ ++ u8 vrm; /* VRM version */ ++ u8 syncpwm3; /* Saved PWM3 for TACH 2,3,4 config */ ++ s8 oppoint[3]; /* Register value */ ++ u16 tmin_ctl; /* Register value */ ++ long therm_total; /* Cummulative therm count */ ++ long therm_ovfl; /* Count of therm overflows */ ++ u8 therm_limit; /* Register value */ ++ u32 alarms; /* Register encoding, combined */ ++ u32 alarm_mask; /* Register encoding, combined */ ++ struct lm85_autofan autofan[3]; ++ struct lm85_zone zone[3]; ++}; ++ ++static int lm85_attach_adapter(struct i2c_adapter *adapter); ++static int lm85_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int lm85_detach_client(struct i2c_client *client); ++static int lm85_read_value(struct i2c_client *client, u16 register); ++static int lm85_write_value(struct i2c_client *client, u16 register, int value); ++static void lm85_update_client(struct i2c_client *client); ++static void lm85_init_client(struct i2c_client *client); ++ ++ ++static void lm85_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++static void lm85_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_zone(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_pwm_config(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_pwm_zone(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_smooth(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static void lm85_spinup_ctl(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm85_tach_mode(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static void adm1027_tach_mode(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1027_temp_offset(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1027_fan_ppr(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adm1027_alarm_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static void adt7463_tmin_ctl(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void adt7463_therm_signal(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static void emc6d100_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static struct i2c_driver lm85_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM85 compatible sensor driver", ++ .id = I2C_DRIVERID_LM85, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = &lm85_attach_adapter, ++ .detach_client = &lm85_detach_client, ++}; ++ ++/* Unique ID assigned to each LM85 detected */ ++static int lm85_id = 0; ++ ++/* -- SENSORS SYSCTL START -- */ ++/* Common parameters */ ++#define LM85_SYSCTL_IN0 1000 ++#define LM85_SYSCTL_IN1 1001 ++#define LM85_SYSCTL_IN2 1002 ++#define LM85_SYSCTL_IN3 1003 ++#define LM85_SYSCTL_IN4 1004 ++#define LM85_SYSCTL_FAN1 1005 ++#define LM85_SYSCTL_FAN2 1006 ++#define LM85_SYSCTL_FAN3 1007 ++#define LM85_SYSCTL_FAN4 1008 ++#define LM85_SYSCTL_TEMP1 1009 ++#define LM85_SYSCTL_TEMP2 1010 ++#define LM85_SYSCTL_TEMP3 1011 ++#define LM85_SYSCTL_VID 1012 ++#define LM85_SYSCTL_ALARMS 1013 ++#define LM85_SYSCTL_PWM1 1014 ++#define LM85_SYSCTL_PWM2 1015 ++#define LM85_SYSCTL_PWM3 1016 ++#define LM85_SYSCTL_VRM 1017 ++#define LM85_SYSCTL_PWM_CFG1 1019 ++#define LM85_SYSCTL_PWM_CFG2 1020 ++#define LM85_SYSCTL_PWM_CFG3 1021 ++#define LM85_SYSCTL_PWM_ZONE1 1022 ++#define LM85_SYSCTL_PWM_ZONE2 1023 ++#define LM85_SYSCTL_PWM_ZONE3 1024 ++#define LM85_SYSCTL_ZONE1 1025 ++#define LM85_SYSCTL_ZONE2 1026 ++#define LM85_SYSCTL_ZONE3 1027 ++#define LM85_SYSCTL_SMOOTH1 1028 ++#define LM85_SYSCTL_SMOOTH2 1029 ++#define LM85_SYSCTL_SMOOTH3 1030 ++ ++/* Vendor specific values */ ++#define LM85_SYSCTL_SPINUP_CTL 1100 ++#define LM85_SYSCTL_TACH_MODE 1101 ++ ++/* Analog Devices variant of the LM85 */ ++#define ADM1027_SYSCTL_TACH_MODE 1200 ++#define ADM1027_SYSCTL_TEMP_OFFSET1 1201 ++#define ADM1027_SYSCTL_TEMP_OFFSET2 1202 ++#define ADM1027_SYSCTL_TEMP_OFFSET3 1203 ++#define ADM1027_SYSCTL_FAN_PPR 1204 ++#define ADM1027_SYSCTL_ALARM_MASK 1205 ++ ++/* Analog Devices variant of the LM85/ADM1027 */ ++#define ADT7463_SYSCTL_TMIN_CTL1 1300 ++#define ADT7463_SYSCTL_TMIN_CTL2 1301 ++#define ADT7463_SYSCTL_TMIN_CTL3 1302 ++#define ADT7463_SYSCTL_THERM_SIGNAL 1303 ++ ++/* SMSC variant of the LM85 */ ++#define EMC6D100_SYSCTL_IN5 1400 ++#define EMC6D100_SYSCTL_IN6 1401 ++#define EMC6D100_SYSCTL_IN7 1402 ++ ++#define LM85_ALARM_IN0 0x0001 ++#define LM85_ALARM_IN1 0x0002 ++#define LM85_ALARM_IN2 0x0004 ++#define LM85_ALARM_IN3 0x0008 ++#define LM85_ALARM_TEMP1 0x0010 ++#define LM85_ALARM_TEMP2 0x0020 ++#define LM85_ALARM_TEMP3 0x0040 ++#define LM85_ALARM_ALARM2 0x0080 ++#define LM85_ALARM_IN4 0x0100 ++#define LM85_ALARM_RESERVED 0x0200 ++#define LM85_ALARM_FAN1 0x0400 ++#define LM85_ALARM_FAN2 0x0800 ++#define LM85_ALARM_FAN3 0x1000 ++#define LM85_ALARM_FAN4 0x2000 ++#define LM85_ALARM_TEMP1_FAULT 0x4000 ++#define LM85_ALARM_TEMP3_FAULT 0x08000 ++#define LM85_ALARM_IN6 0x10000 ++#define LM85_ALARM_IN7 0x20000 ++#define LM85_ALARM_IN5 0x40000 ++/* -- SENSORS SYSCTL END -- */ ++ ++/* The /proc/sys entries */ ++/* These files are created for each detected LM85. This is just a template; ++ * The actual list is built from this and additional per-chip ++ * custom lists below. Note the XXX_LEN macros. These must be ++ * compile time constants because they will be used to allocate ++ * space for the final template passed to i2c_register_entry. ++ * We depend on the ability of GCC to evaluate expressions at ++ * compile time to turn these expressions into compile time ++ * constants, but this can generate a warning. ++ */ ++static ctl_table lm85_common[] = { ++ {LM85_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_in}, ++ {LM85_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_in}, ++ {LM85_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_in}, ++ {LM85_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_in}, ++ {LM85_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_in}, ++ {LM85_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_fan}, ++ {LM85_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_fan}, ++ {LM85_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_fan}, ++ {LM85_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_fan}, ++ {LM85_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_temp}, ++ {LM85_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_temp}, ++ {LM85_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_temp}, ++ {LM85_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_vid}, ++ {LM85_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_vrm}, ++ {LM85_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_alarms}, ++ {LM85_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm}, ++ {LM85_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm}, ++ {LM85_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm}, ++ {LM85_SYSCTL_PWM_CFG1, "pwm1_cfg", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm_config}, ++ {LM85_SYSCTL_PWM_CFG2, "pwm2_cfg", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm_config}, ++ {LM85_SYSCTL_PWM_CFG3, "pwm3_cfg", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_pwm_config}, ++ {LM85_SYSCTL_PWM_ZONE1, "pwm1_zone", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone}, ++ {LM85_SYSCTL_PWM_ZONE2, "pwm2_zone", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone}, ++ {LM85_SYSCTL_PWM_ZONE3, "pwm3_zone", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_pwm_zone}, ++ {LM85_SYSCTL_ZONE1, "zone1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_zone}, ++ {LM85_SYSCTL_ZONE2, "zone2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_zone}, ++ {LM85_SYSCTL_ZONE3, "zone3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_zone}, ++ {LM85_SYSCTL_SMOOTH1, "smooth1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_smooth}, ++ {LM85_SYSCTL_SMOOTH2, "smooth2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_smooth}, ++ {LM85_SYSCTL_SMOOTH3, "smooth3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm85_smooth}, ++ {0} ++}; ++#define CTLTBL_COMMON (sizeof(lm85_common)/sizeof(lm85_common[0])) ++ ++/* NOTE: tach_mode is a shared name, but implemented with ++ * different functions ++ */ ++static ctl_table lm85_specific[] = { ++ {LM85_SYSCTL_SPINUP_CTL, "spinup_ctl", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_spinup_ctl}, ++ {LM85_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm85_tach_mode}, ++/* {0} The doc generator needs this. */ ++}; ++#define CTLTBL_LM85 (sizeof(lm85_specific)/sizeof(lm85_specific[0])) ++ ++static ctl_table adm1027_specific[] = { ++ {ADM1027_SYSCTL_TACH_MODE, "tach_mode", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_tach_mode}, ++ {ADM1027_SYSCTL_TEMP_OFFSET1, "temp1_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset}, ++ {ADM1027_SYSCTL_TEMP_OFFSET2, "temp2_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset}, ++ {ADM1027_SYSCTL_TEMP_OFFSET3, "temp3_offset", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_temp_offset}, ++ {ADM1027_SYSCTL_FAN_PPR, "fan_ppr", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_fan_ppr}, ++ {ADM1027_SYSCTL_ALARM_MASK, "alarm_mask", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adm1027_alarm_mask}, ++/* {0} The doc generator needs this. */ ++}; ++#define CTLTBL_ADM1027 (sizeof(adm1027_specific)/sizeof(adm1027_specific[0])) ++ ++static ctl_table adt7463_specific[] = { ++ {ADT7463_SYSCTL_TMIN_CTL1, "tmin_ctl1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl}, ++ {ADT7463_SYSCTL_TMIN_CTL2, "tmin_ctl2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl}, ++ {ADT7463_SYSCTL_TMIN_CTL3, "tmin_ctl3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_tmin_ctl}, ++ {ADT7463_SYSCTL_THERM_SIGNAL, "therm_signal", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &adt7463_therm_signal}, ++/* {0} The doc generator needs this. */ ++}; ++#define CTLTBL_ADT7463 (sizeof(adt7463_specific)/sizeof(adt7463_specific[0])) ++ ++static ctl_table emc6d100_specific[] = { ++ {EMC6D100_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &emc6d100_in}, ++ {EMC6D100_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &emc6d100_in}, ++ {EMC6D100_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &emc6d100_in}, ++/* {0} The doc generator needs this. */ ++}; ++#define CTLTBL_EMC6D100 (sizeof(emc6d100_specific)/sizeof(emc6d100_specific[0])) ++ ++ ++#define MAX2(a,b) ((a)>(b)?(a):(b)) ++#define MAX3(a,b,c) ((a)>(b)?MAX2((a),(c)):MAX2((b),(c))) ++#define MAX4(a,b,c,d) ((a)>(b)?MAX3((a),(c),(d)):MAX3((b),(c),(d))) ++ ++#define CTLTBL_MAX (CTLTBL_COMMON + MAX3(CTLTBL_LM85, CTLTBL_ADM1027+CTLTBL_ADT7463, CTLTBL_EMC6D100)) ++ ++/* This function is called when: ++ * lm85_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and lm85_driver is still present) */ ++int lm85_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm85_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int lm85_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ int company, verstep ; ++ struct i2c_client *new_client; ++ struct lm85_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ struct ctl_table template[CTLTBL_MAX] ; ++ int template_used ; ++ ++ if (i2c_is_isa_adapter(adapter)) { ++ /* This chip has no ISA interface */ ++ goto ERROR0 ; ++ }; ++ ++ if (!i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) { ++ /* We need to be able to do byte I/O */ ++ goto ERROR0 ; ++ }; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access lm85_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct lm85_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm85_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ company = lm85_read_value(new_client, LM85_REG_COMPANY); ++ verstep = lm85_read_value(new_client, LM85_REG_VERSTEP); ++ ++#ifdef DEBUG ++ printk("lm85: Detecting device at %d,0x%02x with" ++ " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", ++ i2c_adapter_id(new_client->adapter), new_client->addr, ++ company, verstep ++ ); ++#endif ++ ++ /* If auto-detecting, Determine the chip type. */ ++ if (kind <= 0) { ++#ifdef DEBUG ++ printk("lm85: Autodetecting device at %d,0x%02x ...\n", ++ i2c_adapter_id(adapter), address ); ++#endif ++ if( company == LM85_COMPANY_NATIONAL ++ && verstep == LM85_VERSTEP_LM85C ) { ++ kind = lm85c ; ++ } else if( company == LM85_COMPANY_NATIONAL ++ && verstep == LM85_VERSTEP_LM85B ) { ++ kind = lm85b ; ++ } else if( company == LM85_COMPANY_NATIONAL ++ && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { ++ printk("lm85: Detected National Semiconductor chip\n"); ++ printk("lm85: Unrecgonized version/stepping 0x%02x" ++ " Defaulting to Generic LM85.\n", verstep ); ++ kind = any_chip ; ++ } else if( company == LM85_COMPANY_ANALOG_DEV ++ && verstep == LM85_VERSTEP_ADM1027 ) { ++ kind = adm1027 ; ++ } else if( company == LM85_COMPANY_ANALOG_DEV ++ && verstep == LM85_VERSTEP_ADT7463 ) { ++ kind = adt7463 ; ++ } else if( company == LM85_COMPANY_ANALOG_DEV ++ && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { ++ printk("lm85: Detected Analog Devices chip\n"); ++ printk("lm85: Unrecgonized version/stepping 0x%02x" ++ " Defaulting to Generic LM85.\n", verstep ); ++ kind = any_chip ; ++ } else if( company == LM85_COMPANY_SMSC ++ && (verstep == LM85_VERSTEP_EMC6D100_A0 ++ || verstep == LM85_VERSTEP_EMC6D100_A1) ) { ++ /* Unfortunately, we can't tell a '100 from a '101 ++ * from the registers. Since a '101 is a '100 ++ * in a package with fewer pins and therefore no ++ * 3.3V, 1.5V or 1.8V inputs, perhaps if those ++ * inputs read 0, then it's a '101. ++ */ ++ kind = emc6d100 ; ++ } else if( company == LM85_COMPANY_SMSC ++ && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { ++ printk("lm85: Detected SMSC chip\n"); ++ printk("lm85: Unrecognized version/stepping 0x%02x" ++ " Defaulting to Generic LM85.\n", verstep ); ++ kind = any_chip ; ++ } else if( kind == any_chip ++ && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { ++ printk("lm85: Generic LM85 Version 6 detected\n"); ++ /* Leave kind as "any_chip" */ ++ } else { ++#ifdef DEBUG ++ printk("lm85: Autodetection failed\n"); ++#endif ++ /* Not an LM85 ... */ ++ if( kind == any_chip ) { /* User used force=x,y */ ++ printk("lm85: Generic LM85 Version 6 not" ++ " found at %d,0x%02x. Try force_lm85c.\n", ++ i2c_adapter_id(adapter), address ); ++ } ++ err = 0 ; ++ goto ERROR1; ++ } ++ } ++ ++ /* Fill in the chip specific driver values */ ++ switch (kind) { ++ case any_chip : ++ type_name = "lm85"; ++ strcpy(new_client->name, "Generic LM85"); ++ template_used = 0 ; ++ break ; ++ case lm85b : ++ type_name = "lm85b"; ++ strcpy(new_client->name, "National LM85-B"); ++ memcpy( template, lm85_specific, sizeof(lm85_specific) ); ++ template_used = CTLTBL_LM85 ; ++ break ; ++ case lm85c : ++ type_name = "lm85c"; ++ strcpy(new_client->name, "National LM85-C"); ++ memcpy( template, lm85_specific, sizeof(lm85_specific) ); ++ template_used = CTLTBL_LM85 ; ++ break ; ++ case adm1027 : ++ type_name = "adm1027"; ++ strcpy(new_client->name, "Analog Devices ADM1027"); ++ memcpy( template, adm1027_specific, sizeof(adm1027_specific) ); ++ template_used = CTLTBL_ADM1027 ; ++ break ; ++ case adt7463 : ++ type_name = "adt7463"; ++ strcpy(new_client->name, "Analog Devices ADT7463"); ++ memcpy( template, adt7463_specific, sizeof(adt7463_specific) ); ++ template_used = CTLTBL_ADT7463 ; ++ memcpy( template+template_used, adm1027_specific, sizeof(adm1027_specific) ); ++ template_used += CTLTBL_ADM1027 ; ++ break ; ++ case emc6d100 : ++ type_name = "emc6d100"; ++ strcpy(new_client->name, "SMSC EMC6D100"); ++ memcpy(template, emc6d100_specific, sizeof(emc6d100_specific)); ++ template_used = CTLTBL_EMC6D100 ; ++ break ; ++ default : ++ printk("lm85: Internal error, invalid kind (%d)!", kind); ++ err = -EFAULT ; ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields */ ++ new_client->id = lm85_id++; ++ printk("lm85: Assigning ID %d to %s at %d,0x%02x\n", ++ new_client->id, new_client->name, ++ i2c_adapter_id(new_client->adapter), ++ new_client->addr ++ ); ++ ++ /* Housekeeping values */ ++ data->type = kind; ++ data->valid = 0; ++ ++ /* Set the VRM version */ ++ data->vrm = LM85_INIT_VRM ; ++ ++ /* Zero the accumulators */ ++ data->therm_total = 0; ++ data->therm_ovfl = 0; ++ ++ init_MUTEX(&data->update_lock); ++ ++ /* Initialize the LM85 chip */ ++ lm85_init_client(new_client); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* Finish out the template */ ++ memcpy( template + template_used, lm85_common, sizeof(lm85_common) ); ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ template)) < 0) { ++ err = i; ++ goto ERROR2; ++ } ++ data->sysctl_id = i; ++ ++ return 0; ++ ++ /* Error out and cleanup code */ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++int lm85_detach_client(struct i2c_client *client) ++{ ++ int err; ++ int id ; ++ ++ id = client->id; ++ i2c_deregister_entry(((struct lm85_data *)(client->data))->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk("lm85(%d): Client deregistration failed," ++ " client not detached.\n", id ); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++int lm85_read_value(struct i2c_client *client, u16 reg) ++{ ++ int res; ++ ++ /* What size location is it? */ ++ switch( reg ) { ++ case LM85_REG_FAN(0) : /* Read WORD data */ ++ case LM85_REG_FAN(1) : ++ case LM85_REG_FAN(2) : ++ case LM85_REG_FAN(3) : ++ case LM85_REG_FAN_MIN(0) : ++ case LM85_REG_FAN_MIN(1) : ++ case LM85_REG_FAN_MIN(2) : ++ case LM85_REG_FAN_MIN(3) : ++ case LM85_REG_ALARM : /* Read ALARM1 and ALARM2 */ ++ case ADM1027_REG_INTMASK : /* Read MASK1 and MASK2 */ ++ case ADM1027_REG_EXTEND_ADC : /* Read ADC1 and ADC2 */ ++ reg &= 0xff ; /* Pseudo words have address + 0x0100 */ ++ res = i2c_smbus_read_byte_data(client, reg) & 0xff ; ++ res |= (i2c_smbus_read_byte_data(client, reg+1) & 0xff) << 8 ; ++ break ; ++ case ADT7463_REG_TMIN_CTL : /* Read WORD MSB, LSB */ ++ reg &= 0xff ; /* Pseudo words have address + 0x0100 */ ++ res = (i2c_smbus_read_byte_data(client, reg) & 0xff) << 8 ; ++ res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ; ++ break ; ++ default: /* Read BYTE data */ ++ res = i2c_smbus_read_byte_data(client, reg & 0xff) & 0xff ; ++ break ; ++ } ++ ++ return res ; ++} ++ ++int lm85_write_value(struct i2c_client *client, u16 reg, int value) ++{ ++ int res ; ++ ++ switch( reg ) { ++ case LM85_REG_FAN(0) : /* Write WORD data */ ++ case LM85_REG_FAN(1) : ++ case LM85_REG_FAN(2) : ++ case LM85_REG_FAN(3) : ++ case LM85_REG_FAN_MIN(0) : ++ case LM85_REG_FAN_MIN(1) : ++ case LM85_REG_FAN_MIN(2) : ++ case LM85_REG_FAN_MIN(3) : ++ case ADM1027_REG_INTMASK : ++ /* NOTE: ALARM and ADC are read only, so not included here */ ++ reg &= 0xff ; /* Pseudo words have address + 0x0100 */ ++ res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ; ++ res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ; ++ break ; ++ case ADT7463_REG_TMIN_CTL : /* Write WORD MSB, LSB */ ++ reg &= 0xff ; /* Pseudo words have address + 0x0100 */ ++ res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff); ++ res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ; ++ break ; ++ default: /* Write BYTE data */ ++ res = i2c_smbus_write_byte_data(client, reg & 0xff, value); ++ break ; ++ } ++ ++ return res ; ++} ++ ++/* Called when we have found a new LM85. It should set limits, etc. */ ++void lm85_init_client(struct i2c_client *client) ++{ ++ int value; ++ struct lm85_data *data = client->data; ++ ++#ifdef DEBUG ++ printk("lm85(%d): Initializing device\n", client->id); ++#endif ++ ++ /* Warn if part was not "READY" */ ++ value = lm85_read_value(client, LM85_REG_CONFIG); ++#ifdef DEBUG ++ printk("lm85(%d): LM85_REG_CONFIG is: 0x%02x\n", client->id, value ); ++#endif ++ if( value & 0x02 ) { ++ printk("lm85(%d): Client (%d,0x%02x) config is locked.\n", ++ client->id, ++ i2c_adapter_id(client->adapter), client->addr ); ++ }; ++ if( ! (value & 0x04) ) { ++ printk("lm85(%d): Client (%d,0x%02x) is not ready.\n", ++ client->id, ++ i2c_adapter_id(client->adapter), client->addr ); ++ }; ++ if( (data->type == adm1027 || data->type == adt7463) ++ && (value & 0x10) ++ ) { ++ printk("lm85(%d): Client (%d,0x%02x) VxI mode is set. " ++ "Please report this to the lm85 maintainer.\n", ++ client->id, ++ i2c_adapter_id(client->adapter), client->addr ); ++ }; ++ ++ /* See if SYNC to PWM3 is set */ ++ if( data->type == adt7463 ++ && (lm85_read_value(client, LM85_REG_AFAN_SPIKE1) & 0x10) ++ ) { ++ printk("lm85(%d): Sync to PWM3 is set. Expect PWM3 " ++ "to control fans 2, 3, and 4\n", ++ client->id ); ++ }; ++ ++ /* See if PWM2 is #SMBALERT */ ++ if( (data->type == adm1027 || data->type == adt7463) ++ && (lm85_read_value(client, ADM1027_REG_CONFIG3) & 0x01) ++ ) { ++ printk("lm85(%d): PWM2 is SMBALERT. PWM2 not available.\n", ++ client->id ); ++ }; ++ ++ /* Check if 2.5V and 5V inputs are reconfigured */ ++ if( data->type == adt7463 ) { ++ value = lm85_read_value(client, ADT7463_REG_CONFIG4); ++ if( value & 0x01 ) { ++ printk("lm85(%d): 2.5V input (in0) is SMBALERT. " ++ "in0 not available.\n", client->id ); ++ }; ++ if( value & 0x02 ) { ++ printk("lm85(%d): 5V input (in3) is THERM. " ++ "in3 not available.\n", client->id ); ++ } ++ }; ++ ++ /* FIXME? Display EMC6D100 config info? */ ++ ++ /* WE INTENTIONALLY make no changes to the limits, ++ * offsets, pwms, fans and zones. If they were ++ * configured, we don't want to mess with them. ++ * If they weren't, the default is 100% PWM, no ++ * control and will suffice until 'sensors -s' ++ * can be run by the user. ++ */ ++ ++ /* Start monitoring */ ++ value = lm85_read_value(client, LM85_REG_CONFIG); ++ /* Try to clear LOCK, Set START, save everything else */ ++ value = ((value & ~ 0x02) | 0x01) & 0xff ; ++#ifdef DEBUG ++ printk("lm85(%d): Setting CONFIG to: 0x%02x\n", client->id, value ); ++#endif ++ lm85_write_value(client, LM85_REG_CONFIG, value); ++ ++} ++ ++void lm85_update_client(struct i2c_client *client) ++{ ++ struct lm85_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if (!data->valid ++ || (jiffies - data->last_reading > LM85_DATA_INTERVAL )) { ++ /* Things that change quickly */ ++ ++#ifdef DEBUG ++ printk("lm85(%d): Reading sensor values\n", client->id); ++#endif ++ /* Have to read extended bits first to "freeze" the ++ * more significant bits that are read later. ++ */ ++ switch( data->type ) { ++ case adm1027 : ++ case adt7463 : ++ data->extend_adc = ++ lm85_read_value(client, ADM1027_REG_EXTEND_ADC); ++ break ; ++ default : ++ data->extend_adc = 0 ; ++ break ; ++ } ++ ++ for (i = 0; i <= 4; ++i) { ++ data->in[i] = ++ lm85_read_value(client, LM85_REG_IN(i)); ++ } ++ ++ for (i = 0; i <= 3; ++i) { ++ data->fan[i] = ++ lm85_read_value(client, LM85_REG_FAN(i)); ++ } ++ ++ for (i = 0; i <= 2; ++i) { ++ data->temp[i] = ++ lm85_read_value(client, LM85_REG_TEMP(i)); ++ } ++ ++ for (i = 0; i <= 2; ++i) { ++ data->pwm[i] = ++ lm85_read_value(client, LM85_REG_PWM(i)); ++ } ++ ++ data->alarms = lm85_read_value(client, LM85_REG_ALARM); ++ ++ switch( ((struct lm85_data *)(client->data))->type ) { ++ case adt7463 : ++ /* REG_THERM code duplicated in therm_signal() */ ++ i = lm85_read_value(client, ADT7463_REG_THERM); ++ if( data->therm_total < LONG_MAX - 256 ) { ++ data->therm_total += i ; ++ } ++ if( i >= 255 ) { ++ ++data->therm_ovfl ; ++ } ++ break ; ++ case emc6d100 : ++ /* Three more voltage sensors */ ++ for (i = 5; i <= 7; ++i) { ++ data->in[i] = ++ lm85_read_value(client, EMC6D100_REG_IN(i)); ++ } ++ /* More alarm bits */ ++ data->alarms |= ++ lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; ++ ++ break ; ++ default : break ; /* no warnings */ ++ } ++ ++ data->last_reading = jiffies ; ++ }; /* last_reading */ ++ ++ if (!data->valid ++ || (jiffies - data->last_config > LM85_CONFIG_INTERVAL) ) { ++ /* Things that don't change often */ ++ ++#ifdef DEBUG ++ printk("lm85(%d): Reading config values\n", client->id); ++#endif ++ for (i = 0; i <= 4; ++i) { ++ data->in_min[i] = ++ lm85_read_value(client, LM85_REG_IN_MIN(i)); ++ data->in_max[i] = ++ lm85_read_value(client, LM85_REG_IN_MAX(i)); ++ } ++ ++ for (i = 0; i <= 3; ++i) { ++ data->fan_min[i] = ++ lm85_read_value(client, LM85_REG_FAN_MIN(i)); ++ } ++ ++ for (i = 0; i <= 2; ++i) { ++ data->temp_min[i] = ++ lm85_read_value(client, LM85_REG_TEMP_MIN(i)); ++ data->temp_max[i] = ++ lm85_read_value(client, LM85_REG_TEMP_MAX(i)); ++ } ++ ++ data->vid = lm85_read_value(client, LM85_REG_VID); ++ ++ for (i = 0; i <= 2; ++i) { ++ int val ; ++ data->autofan[i].config = ++ lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); ++ val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); ++ data->autofan[i].freq = val & 0x07 ; ++ data->zone[i].range = (val >> 4) & 0x0f ; ++ data->autofan[i].min_pwm = ++ lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); ++ data->zone[i].limit = ++ lm85_read_value(client, LM85_REG_AFAN_LIMIT(i)); ++ data->zone[i].critical = ++ lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i)); ++ } ++ ++ i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); ++ data->smooth[0] = i & 0x0f ; ++ data->syncpwm3 = i & 0x10 ; /* Save PWM3 config */ ++ data->autofan[0].min_off = i & 0x20 ; ++ data->autofan[1].min_off = i & 0x40 ; ++ data->autofan[2].min_off = i & 0x80 ; ++ i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2); ++ data->smooth[1] = (i>>4) & 0x0f ; ++ data->smooth[2] = i & 0x0f ; ++ ++ i = lm85_read_value(client, LM85_REG_AFAN_HYST1); ++ data->zone[0].hyst = (i>>4) & 0x0f ; ++ data->zone[1].hyst = i & 0x0f ; ++ ++ i = lm85_read_value(client, LM85_REG_AFAN_HYST2); ++ data->zone[2].hyst = (i>>4) & 0x0f ; ++ ++ switch( ((struct lm85_data *)(client->data))->type ) { ++ case lm85b : ++ case lm85c : ++ data->tach_mode = lm85_read_value(client, ++ LM85_REG_TACH_MODE ); ++ data->spinup_ctl = lm85_read_value(client, ++ LM85_REG_SPINUP_CTL ); ++ break ; ++ case adt7463 : ++ for (i = 0; i <= 2; ++i) { ++ data->oppoint[i] = lm85_read_value(client, ++ ADT7463_REG_OPPOINT(i) ); ++ } ++ data->tmin_ctl = lm85_read_value(client, ++ ADT7463_REG_TMIN_CTL ); ++ data->therm_limit = lm85_read_value(client, ++ ADT7463_REG_THERM_LIMIT ); ++ /* FALL THROUGH */ ++ case adm1027 : ++ for (i = 0; i <= 2; ++i) { ++ data->temp_offset[i] = lm85_read_value(client, ++ ADM1027_REG_TEMP_OFFSET(i) ); ++ } ++ data->tach_mode = lm85_read_value(client, ++ ADM1027_REG_CONFIG3 ); ++ data->fan_ppr = lm85_read_value(client, ++ ADM1027_REG_FAN_PPR ); ++ data->alarm_mask = lm85_read_value(client, ++ ADM1027_REG_INTMASK ); ++ break ; ++ case emc6d100 : ++ for (i = 5; i <= 7; ++i) { ++ data->in_min[i] = ++ lm85_read_value(client, EMC6D100_REG_IN_MIN(i)); ++ data->in_max[i] = ++ lm85_read_value(client, EMC6D100_REG_IN_MAX(i)); ++ } ++ break ; ++ default : break ; /* no warnings */ ++ } ++ ++ data->last_config = jiffies; ++ }; /* last_config */ ++ ++ data->valid = 1; ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the data ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ */ ++void lm85_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_IN0; ++ ++ if (nr < 0 || nr > 4) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; /* 1.000 */ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ int ext = 0 ; ++ lm85_update_client(client); ++ ext = EXT_FROM_REG(data->extend_adc, nr); ++ results[0] = INS_FROM_REG(nr,data->in_min[nr]); ++ results[1] = INS_FROM_REG(nr,data->in_max[nr]); ++ results[2] = INSEXT_FROM_REG(nr,data->in[nr],ext); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->in_max[nr] = INS_TO_REG(nr,results[1]); ++ lm85_write_value(client, LM85_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->in_min[nr] = INS_TO_REG(nr,results[0]); ++ lm85_write_value(client, LM85_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_FAN1 ; ++ ++ if (nr < 0 || nr > 3) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr]); ++ results[1] = FAN_FROM_REG(data->fan[nr]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->fan_min[nr] = FAN_TO_REG(results[0]); ++ lm85_write_value(client, LM85_REG_FAN_MIN(nr), ++ data->fan_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++ ++void lm85_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_TEMP1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ int ext = 0 ; ++ lm85_update_client(client); ++ ++ /* +5 for offset of temp data in ext reg */ ++ ext = EXT_FROM_REG(data->extend_adc, nr+5); ++ ++ results[0] = TEMP_FROM_REG(data->temp_min[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_max[nr]); ++ results[2] = TEMPEXT_FROM_REG(data->temp[nr],ext); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->temp_max[nr] = TEMP_TO_REG(results[1]); ++ lm85_write_value(client, LM85_REG_TEMP_MAX(nr), ++ data->temp_max[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->temp_min[nr] = TEMP_TO_REG(results[0]); ++ lm85_write_value(client, LM85_REG_TEMP_MIN(nr), ++ data->temp_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_PWM1 ; ++ int pwm_zone ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr]); ++ pwm_zone = ZONE_FROM_REG(data->autofan[nr].config); ++ /* PWM "enabled" if not off (0) nor on (-1) */ ++ results[1] = pwm_zone != 0 && pwm_zone != -1 ; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ /* PWM enable is read-only */ ++ if (*nrels_mag > 0) { ++ data->pwm[nr] = PWM_TO_REG(results[0]); ++ lm85_write_value(client, LM85_REG_PWM(nr), ++ data->pwm[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ ++ if( ctl_name != LM85_SYSCTL_VID ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = VID_FROM_REG((data->vid)&0x3f,data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm85_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ ++ if( ctl_name != LM85_SYSCTL_VRM ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm ; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->vrm = results[0] ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ ++ if( ctl_name != LM85_SYSCTL_ALARMS ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm85_spinup_ctl(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int old; ++ ++ if( ctl_name != LM85_SYSCTL_SPINUP_CTL ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = (data->spinup_ctl & 1) != 0 ; ++ results[1] = (data->spinup_ctl & 2) != 0 ; ++ results[2] = (data->spinup_ctl & 4) != 0 ; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ old = data->spinup_ctl ; ++ if (*nrels_mag > 2) { ++ old = (old & (~4)) | (results[2]?4:0) ; ++ } ++ if (*nrels_mag > 1) { ++ old = (old & (~2)) | (results[1]?2:0) ; ++ } ++ if (*nrels_mag > 0) { ++ old = (old & (~1)) | (results[0]?1:0) ; ++ lm85_write_value(client, LM85_REG_SPINUP_CTL, old); ++ data->spinup_ctl = old ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_tach_mode(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int old; ++ ++ /* Tach Mode 1, Tach Mode 2, Tach Mode 3 & 4 */ ++ ++ if( ctl_name != LM85_SYSCTL_TACH_MODE ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = (data->tach_mode & 0x03) ; ++ results[1] = (data->tach_mode & 0x0c) >> 2 ; ++ results[2] = (data->tach_mode & 0x30) >> 4 ; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ old = data->tach_mode ; ++ if (*nrels_mag > 2) { ++ old = (old & (~0x30)) | ((results[2]&3) << 4) ; ++ } ++ if (*nrels_mag > 1) { ++ old = (old & (~0x0c)) | ((results[1]&3) << 2) ; ++ } ++ if (*nrels_mag > 0) { ++ old = (old & (~0x03)) | (results[0]&3) ; ++ lm85_write_value(client, LM85_REG_TACH_MODE, old); ++ data->tach_mode = old ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1027_tach_mode(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int old; ++ ++ /* Tach/DC 1, Tach/DC 2, Tach/DC 3, Tach/DC 4 */ ++ ++ if( ctl_name != ADM1027_SYSCTL_TACH_MODE ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = (data->tach_mode & 0x10) != 0 ; ++ results[1] = (data->tach_mode & 0x20) != 0 ; ++ results[2] = (data->tach_mode & 0x40) != 0 ; ++ results[3] = (data->tach_mode & 0x80) != 0 ; ++ *nrels_mag = 4; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ old = data->tach_mode ; ++ if (*nrels_mag > 3) { ++ old = (old & (~0x80)) | (results[3] ? 0x80 : 0) ; ++ } ++ if (*nrels_mag > 2) { ++ old = (old & (~0x40)) | (results[2] ? 0x40 : 0) ; ++ } ++ if (*nrels_mag > 1) { ++ old = (old & (~0x20)) | (results[1] ? 0x20 : 0) ; ++ } ++ if (*nrels_mag > 0) { ++ old = (old & (~0x10)) | (results[0] ? 0x10 : 0) ; ++ ++ /* Enable fast measurements if any TACH's are DC */ ++ old = (old & (~0x08)) | ((old&0xf0) ? 0x08 : 0) ; ++ ++ lm85_write_value(client, ADM1027_REG_CONFIG3, old); ++ data->tach_mode = old ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_pwm_config(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_PWM_CFG1 ; ++ ++ /* Spinup, min PWM, PWM Frequency, min below limit, Invert */ ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ ++ results[0] = SPINUP_FROM_REG(data->autofan[nr].config); ++ results[1] = PWM_FROM_REG(data->autofan[nr].min_pwm)*10; ++ results[2] = FREQ_FROM_REG(data->autofan[nr].freq); ++ results[3] = data->autofan[nr].min_off ? 10 : 0 ; ++ results[4] = (data->autofan[nr].config & 0x10) ? 10 : 0 ; ++ *nrels_mag = 5; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ int old_config ; ++ ++ down(&data->update_lock); ++ old_config = data->autofan[nr].config ; ++ if (*nrels_mag > 4) { ++ old_config = (old_config & (~0x10)) | (results[4]?0x10:0) ; ++ } ++ if (*nrels_mag > 3) { ++ data->autofan[nr].min_off = results[3] != 0 ; ++ lm85_write_value(client, LM85_REG_AFAN_SPIKE1, ++ data->smooth[0] ++ | data->syncpwm3 ++ | (data->autofan[0].min_off ? 0x20 : 0) ++ | (data->autofan[1].min_off ? 0x40 : 0) ++ | (data->autofan[2].min_off ? 0x80 : 0) ++ ); ++ } ++ if (*nrels_mag > 2) { ++ data->autofan[nr].freq = FREQ_TO_REG(results[2]) ; ++ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ++ (data->zone[nr].range << 4) ++ | data->autofan[nr].freq ++ ); ++ } ++ if (*nrels_mag > 1) { ++ data->autofan[nr].min_pwm = PWM_TO_REG((results[1]+5)/10); ++ lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr), ++ data->autofan[nr].min_pwm ++ ); ++ } ++ if (*nrels_mag > 0) { ++ old_config = (old_config & (~0x07)) | SPINUP_TO_REG(results[0]) ; ++ lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), old_config); ++ data->autofan[nr].config = old_config ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_smooth(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_SMOOTH1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = SMOOTH_FROM_REG(data->smooth[nr]); ++ *nrels_mag = 1; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if( *nrels_mag > 0 ) { ++ data->smooth[nr] = SMOOTH_TO_REG(results[0]); ++ } ++ if( nr == 0 ) { ++ lm85_write_value(client, LM85_REG_AFAN_SPIKE1, ++ data->smooth[0] ++ | data->syncpwm3 ++ | (data->autofan[0].min_off ? 0x20 : 0) ++ | (data->autofan[1].min_off ? 0x40 : 0) ++ | (data->autofan[2].min_off ? 0x80 : 0) ++ ); ++ } else { ++ lm85_write_value(client, LM85_REG_AFAN_SPIKE2, ++ (data->smooth[1] << 4) | data->smooth[2]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_zone(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_ZONE1 ; ++ ++ /* Limit, Hysteresis (neg), Range, Critical */ ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ ++ results[0] = TEMP_FROM_REG(data->zone[nr].limit) / 10; ++ results[1] = HYST_FROM_REG(data->zone[nr].hyst); ++ results[2] = RANGE_FROM_REG(data->zone[nr].range); ++ results[3] = TEMP_FROM_REG(data->zone[nr].critical) / 10; ++ *nrels_mag = 4; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 3) { ++ data->zone[nr].critical = TEMP_TO_REG(results[3]*10); ++ lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr), ++ data->zone[nr].critical ); ++ } ++ if (*nrels_mag > 2) { ++ data->zone[nr].range = RANGE_TO_REG(results[2]); ++ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ++ (data->zone[nr].range << 4) ++ | data->autofan[nr].freq ++ ); ++ } ++ if (*nrels_mag > 1) { ++ data->zone[nr].hyst = HYST_TO_REG(results[1]); ++ if( nr == 0 || nr == 1 ) { ++ lm85_write_value(client, LM85_REG_AFAN_HYST1, ++ (data->zone[0].hyst << 4) ++ | data->zone[1].hyst ++ ); ++ } else { ++ lm85_write_value(client, LM85_REG_AFAN_HYST2, ++ (data->zone[2].hyst << 4) ++ ); ++ } ++ } ++ if (*nrels_mag > 0) { ++ data->zone[nr].limit = TEMP_TO_REG(results[0]*10); ++ lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr), ++ data->zone[nr].limit ++ ); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void lm85_pwm_zone(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - LM85_SYSCTL_PWM_ZONE1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = ZONE_FROM_REG(data->autofan[nr].config); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->autofan[nr].config = ++ (data->autofan[nr].config & (~0xe0)) ++ | ZONE_TO_REG(results[0]) ; ++ lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), ++ data->autofan[nr].config); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1027_temp_offset(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - ADM1027_SYSCTL_TEMP_OFFSET1 ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ switch( data->type ) { ++ case adm1027 : ++ default : ++ results[0] = TEMP_FROM_REG(data->temp_offset[nr]); ++ break ; ++ case adt7463 : ++ results[0] = TEMPEXT_FROM_REG(0,data->temp_offset[nr]); ++ break ; ++ } ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ switch( data->type ) { ++ case adm1027 : ++ default : ++ data->temp_offset[nr] = TEMP_TO_REG(results[0]); ++ break ; ++ case adt7463 : ++ data->temp_offset[nr] = EXTTEMP_TO_REG(results[0]); ++ break ; ++ }; ++ lm85_write_value(client, ADM1027_REG_TEMP_OFFSET(nr), ++ data->temp_offset[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1027_fan_ppr(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int old ; ++ ++ if (ctl_name != ADM1027_SYSCTL_FAN_PPR) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = PPR_FROM_REG(data->fan_ppr,0); ++ results[1] = PPR_FROM_REG(data->fan_ppr,1); ++ results[2] = PPR_FROM_REG(data->fan_ppr,2); ++ results[3] = PPR_FROM_REG(data->fan_ppr,3); ++ *nrels_mag = 4; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ old = data->fan_ppr ; ++ if (*nrels_mag > 3) { ++ old = (old & ~PPR_MASK(3)) | PPR_TO_REG(results[3],3); ++ }; ++ if (*nrels_mag > 2) { ++ old = (old & ~PPR_MASK(2)) | PPR_TO_REG(results[2],2); ++ }; ++ if (*nrels_mag > 1) { ++ old = (old & ~PPR_MASK(1)) | PPR_TO_REG(results[1],1); ++ }; ++ if (*nrels_mag > 0) { ++ old = (old & ~PPR_MASK(0)) | PPR_TO_REG(results[0],0); ++ lm85_write_value(client, ADM1027_REG_FAN_PPR, old); ++ data->fan_ppr = old ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adm1027_alarm_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ ++ if( ctl_name != ADM1027_SYSCTL_ALARM_MASK ) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = INTMASK_FROM_REG(data->alarm_mask); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 0) { ++ data->alarm_mask = INTMASK_TO_REG(results[0]); ++ lm85_write_value(client, ADM1027_REG_INTMASK, ++ data->alarm_mask); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adt7463_tmin_ctl(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - ADT7463_SYSCTL_TMIN_CTL1 ; ++ u16 old ; ++ ++ if (nr < 0 || nr > 2) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ old = data->tmin_ctl ; ++ results[0] = (old & ( 0x2000 << nr )) != 0 ; ++ results[1] = (old >> (nr*3)) & 0x07 ; ++ results[2] = (old & ( 0x0400 << nr )) != 0 ; ++ results[3] = OPPOINT_FROM_REG(data->oppoint[nr]); ++ *nrels_mag = 4; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ old = data->tmin_ctl ; ++ if (*nrels_mag > 3) { ++ data->oppoint[nr] = OPPOINT_TO_REG(results[3]); ++ lm85_write_value(client, ADT7463_REG_OPPOINT(nr), ++ data->oppoint[nr]); ++ }; ++ if (*nrels_mag > 2) { ++ if( results[2] ) { ++ old |= (0x0400 << nr) ; ++ } else { ++ old &= ~(0x0400 << nr) ; ++ } ++ }; ++ if (*nrels_mag > 1) { ++ old &= ~(0x07 << (nr*3)) ; ++ old |= (results[1] & 0x07) << (nr*3) ; ++ }; ++ if (*nrels_mag > 0) { ++ if( results[0] ) { ++ old |= 0x2000 << nr ; ++ } else { ++ old &= ~(0x2000 << nr) ; ++ } ++ lm85_write_value(client, ADT7463_REG_TMIN_CTL, old); ++ data->tmin_ctl = old ; ++ } ++ up(&data->update_lock); ++ } ++} ++ ++void adt7463_therm_signal(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int counts ; ++ ++ if (ctl_name != ADT7463_SYSCTL_THERM_SIGNAL) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ /* Don't call update_client here because ++ * ADT7463_REG_THERM has to be read every ++ * 5 seconds to prevent lost counts ++ */ ++ down(&data->update_lock); ++ counts = lm85_read_value(client, ADT7463_REG_THERM) & 0xff; ++ if( data->therm_total < LONG_MAX - 256 ) { ++ data->therm_total += counts ; ++ } ++ if( counts >= 255 ) { ++ ++data->therm_ovfl ; ++ } ++ up(&data->update_lock); ++ ++ results[0] = data->therm_limit ; ++ results[1] = data->therm_total ; ++ results[2] = data->therm_ovfl ; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ /* therm_total and therm_ovfl are read only */ ++ if (*nrels_mag > 0) { ++ data->therm_limit = SENSORS_LIMIT(results[0],0,255); ++ lm85_write_value(client, ADT7463_REG_THERM_LIMIT, ++ data->therm_limit); ++ }; ++ up(&data->update_lock); ++ } ++} ++ ++ ++void emc6d100_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm85_data *data = client->data; ++ int nr = ctl_name - EMC6D100_SYSCTL_IN5 +5; ++ ++ if (nr < 5 || nr > 7) ++ return ; /* ERROR */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; /* 1.000 */ ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm85_update_client(client); ++ results[0] = INS_FROM_REG(nr,data->in_min[nr]); ++ results[1] = INS_FROM_REG(nr,data->in_max[nr]); ++ results[2] = INS_FROM_REG(nr,data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ down(&data->update_lock); ++ if (*nrels_mag > 1) { ++ data->in_max[nr] = INS_TO_REG(nr,results[1]); ++ lm85_write_value(client, EMC6D100_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ if (*nrels_mag > 0) { ++ data->in_min[nr] = INS_TO_REG(nr,results[0]); ++ lm85_write_value(client, EMC6D100_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ up(&data->update_lock); ++ } ++} ++ ++ ++static int __init sm_lm85_init(void) ++{ ++ printk("lm85: Version %s (%s)\n", LM_VERSION, LM_DATE); ++ printk("lm85: See http://www.penguincomputing.com/lm_sensors for more info.\n" ); ++ return i2c_add_driver(&lm85_driver); ++} ++ ++static void __exit sm_lm85_exit(void) ++{ ++ i2c_del_driver(&lm85_driver); ++} ++ ++/* Thanks to Richard Barrington for adding the LM85 to sensors-detect. ++ * Thanks to Margit Schubert-While for help with ++ * post 2.7.0 CVS changes ++ */ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Philip Pokorny ++ Philip Edelbrock ++ Stephen Rousset ++ Dan Eaton ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++/* Chip configuration settings. These should be set to reflect the ++HARDWARE configuration of your chip. By default (read: when all of ++these are left commented out), this driver assumes that the ++configuration is the same as National's defaults for the Channel Mode ++register. ++ ++Set to '1' the appropriate defines, as nessesary: ++ ++ - External temp sensors 2 (possible second CPU temp) ++ This will disable the 2.5V and Vccp2 readings. ++ Ironically, National decided that you can read the ++ temperature of a second CPU or it's core voltage, ++ but not both! Comment out if FAULT is reported. */ ++ ++/* #define LM87_EXT2 1 */ ++ ++/* Aux analog input. When enabled, the Fan 1 reading ++ will be disabled */ ++ ++/* #define LM87_AIN1 1 */ ++ ++/* Aux analog input 2. When enabled, the Fan 2 reading ++ will be disabled */ ++ ++/* #define LM87_AIN2 1 */ ++ ++/* Internal Vcc is 5V instead of 3.3V */ ++ ++/* #define LM87_5V_VCC 1 */ ++ ++/* That's the end of the hardware config defines. I would have made ++ them insmod params, but it would be too much work. ;') */ ++ ++ ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(lm87); ++ ++/* The following is the calculation for the register offset ++ * for the monitored items minimum and maximum locations. ++ */ ++#define LM87_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) ++#define LM87_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) ++#define LM87_REG_IN(nr) (0x20 + (nr)) ++ ++/* Initial limits */ ++ ++/* ++ * LM87 register definition ++ * ++ */ ++ ++ /* The LM87 registers */ ++#define LM87_INT_TEMP_HI_LIMIT_LOCKABLE 0x13 ++#define LM87_EXT_TEMP_HI_LIMIT_LOCKABLE 0x14 ++#define LM87_REG_TEST 0x15 ++#define LM87_REG_CHANNEL_MODE 0x16 ++#define LM87_REG_INT_TEMP_HI_LIMIT 0x17 ++#define LM87_REG_EXT_TEMP_HI_LIMIT 0x18 ++#define LM87_REG_ANALOG_OUT 0x19 ++ ++ /* These are all read-only */ ++#define LM87_REG_2_5V_EXT_TEMP_2 0x20 ++#define LM87_REG_VCCP1 0x21 ++#define LM87_REG_3_3V 0x22 ++#define LM87_REG_5V 0x23 ++#define LM87_REG_12V 0x24 ++#define LM87_REG_VCCP2 0x25 ++#define LM87_REG_EXT_TEMP_1 0x26 ++#define LM87_REG_INT_TEMP 0x27 /* LM87 temp. */ ++#define LM87_REG_FAN1_AIN1 0x28 ++#define LM87_REG_FAN2_AIN2 0x29 ++ ++/* These are read/write */ ++#define LM87_REG_AIN1_LOW 0x1A ++#define LM87_REG_AIN2_LOW 0x1B ++#define LM87_REG_2_5V_EXT_TEMP_2_HIGH 0x2B ++#define LM87_REG_2_5V_EXT_TEMP_2_LOW 0x2C ++#define LM87_REG_VCCP1_HIGH 0x2D ++#define LM87_REG_VCCP1_LOW 0x2E ++#define LM87_REG_3_3V_HIGH 0x2F ++#define LM87_REG_3_3V_LOW 0x30 ++#define LM87_REG_5V_HIGH 0x31 ++#define LM87_REG_5V_LOW 0x32 ++#define LM87_REG_12V_HIGH 0x33 ++#define LM87_REG_12V_LOW 0x34 ++#define LM87_REG_VCCP2_HIGH 0x35 ++#define LM87_REG_VCCP2_LOW 0x36 ++#define LM87_REG_EXT_TEMP_1_HIGH 0x37 ++#define LM87_REG_EXT_TEMP_1_LOW 0x38 ++#define LM87_REG_INT_TEMP_HIGH 0x39 ++#define LM87_REG_INT_TEMP_LOW 0x3A ++#define LM87_REG_FAN1_AIN1_LIMIT 0x3B ++#define LM87_REG_FAN2_AIN2_LIMIT 0x3C ++#define LM87_REG_COMPANY_ID 0x3E ++#define LM87_REG_DIE_REV 0x3F ++ ++#define LM87_REG_CONFIG 0x40 ++#define LM87_REG_INT1_STAT 0x41 ++#define LM87_REG_INT2_STAT 0x42 ++#define LM87_REG_INT1_MASK 0x43 ++#define LM87_REG_INT2_MASK 0x44 ++#define LM87_REG_CHASSIS_CLEAR 0x46 ++#define LM87_REG_VID_FAN_DIV 0x47 ++#define LM87_REG_VID4 0x49 ++#define LM87_REG_CONFIG_2 0x4A ++#define LM87_REG_INTRPT_STATUS_1_MIRROR 0x4C ++#define LM87_REG_INTRPT_STATUS_2_MIRROR 0x4D ++#define LM87_REG_SMBALERT_NUM_ENABLE 0x80 ++ ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) ++#define IN_FROM_REG(val,nr) (val) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:\ ++ (val)==255?0:1350000/((div)*(val))) ++ ++#define TEMP_FROM_REG(temp) (temp * 10) ++ ++#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) ++ ++#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),0,255) ++#if 0 ++#define TEMP_FROM_REG(temp) \ ++ ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ ++ ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ ++ ++#define TEMP_LIMIT_FROM_REG(val) (val) ++ ++#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val),0,255) ++#endif ++ ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) ++ ++/* For each registered LM87, we need to keep some data in memory. That ++ data is pointed to by LM87_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new LM87 client is ++ allocated. */ ++struct lm87_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[6]; /* Scaled Register value */ ++ u8 in_max[6]; /* Scaled Register value */ ++ u8 in_min[6]; /* Scaled Register value */ ++ u8 ain1; /* Register value */ ++ u8 ain1_min; /* Register value */ ++ u8 ain1_max; /* Register value */ ++ u8 ain2; /* Register value */ ++ u8 ain2_min; /* Register value */ ++ u8 ain2_max; /* Register value */ ++ u8 fan; /* Register value */ ++ u8 fan_min; /* Register value */ ++ u8 fan_div; /* Register encoding, shifted right */ ++ u8 fan2; /* Register value */ ++ u8 fan2_min; /* Register value */ ++ u8 fan2_div; /* Register encoding, shifted right */ ++ int ext2_temp; /* Temp, shifted right */ ++ int ext_temp; /* Temp, shifted right */ ++ int int_temp; /* Temp, shifted right */ ++ u8 ext_temp_max; /* Register value */ ++ u8 ext_temp_min; /* Register value */ ++ u8 ext2_temp_max; /* Register value */ ++ u8 ext2_temp_min; /* Register value */ ++ u8 int_temp_max; /* Register value */ ++ u8 int_temp_min; /* Register value */ ++ u16 alarms; /* Register encoding, combined */ ++ u8 analog_out; /* Register value */ ++ u8 vid; /* Register value combined */ ++ u8 vrm; /* VRM version * 10 */ ++}; ++ ++static int lm87_attach_adapter(struct i2c_adapter *adapter); ++static int lm87_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int lm87_detach_client(struct i2c_client *client); ++ ++static int lm87_read_value(struct i2c_client *client, u8 register); ++static int lm87_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void lm87_update_client(struct i2c_client *client); ++static void lm87_init_client(struct i2c_client *client); ++ ++ ++static void lm87_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++#if defined (LM87_AIN1) || defined (LM87_AIN2) ++static void lm87_ain(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++#endif ++static void lm87_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm87_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm87_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm87_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm87_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void lm87_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm87_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int lm87_id = 0; ++ ++static struct i2c_driver LM87_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM87 sensor driver", ++ .id = I2C_DRIVERID_LM87, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm87_attach_adapter, ++ .detach_client = lm87_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define LM87_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define LM87_SYSCTL_IN1 1001 ++#define LM87_SYSCTL_IN2 1002 ++#define LM87_SYSCTL_IN3 1003 ++#define LM87_SYSCTL_IN4 1004 ++#define LM87_SYSCTL_IN5 1005 ++#define LM87_SYSCTL_AIN1 1006 ++#define LM87_SYSCTL_AIN2 1007 ++#define LM87_SYSCTL_FAN1 1102 ++#define LM87_SYSCTL_FAN2 1103 ++#define LM87_SYSCTL_TEMP1 1250 /* Degrees Celcius * 100 */ ++#define LM87_SYSCTL_TEMP2 1251 /* Degrees Celcius * 100 */ ++#define LM87_SYSCTL_TEMP3 1252 /* Degrees Celcius * 100 */ ++#define LM87_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define LM87_SYSCTL_ALARMS 2001 /* bitvector */ ++#define LM87_SYSCTL_ANALOG_OUT 2002 ++#define LM87_SYSCTL_VID 2003 ++#define LM87_SYSCTL_VRM 2004 ++ ++#define LM87_ALARM_IN0 0x0001 ++#define LM87_ALARM_IN1 0x0002 ++#define LM87_ALARM_IN2 0x0004 ++#define LM87_ALARM_IN3 0x0008 ++#define LM87_ALARM_TEMP1 0x0010 ++#define LM87_ALARM_TEMP2 0x0020 ++#define LM87_ALARM_TEMP3 0x0020 /* same?? */ ++#define LM87_ALARM_FAN1 0x0040 ++#define LM87_ALARM_FAN2 0x0080 ++#define LM87_ALARM_IN4 0x0100 ++#define LM87_ALARM_IN5 0x0200 ++#define LM87_ALARM_RESERVED1 0x0400 ++#define LM87_ALARM_RESERVED2 0x0800 ++#define LM87_ALARM_CHAS 0x1000 ++#define LM87_ALARM_THERM_SIG 0x2000 ++#define LM87_ALARM_TEMP2_FAULT 0x4000 ++#define LM87_ALARM_TEMP3_FAULT 0x08000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* The /proc/sys entries */ ++/* These files are created for each detected LM87. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++ ++static ctl_table LM87_dir_table_template[] = { ++#ifdef LM87_AIN1 ++ {LM87_SYSCTL_AIN1, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_ain}, ++#endif ++#ifdef LM87_AIN2 ++ {LM87_SYSCTL_AIN2, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_ain}, ++#endif ++#ifndef LM87_EXT2 ++ {LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++ {LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++#endif ++ {LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++ {LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++ {LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++ {LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_in}, ++#ifndef LM87_AIN1 ++ {LM87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_fan}, ++ {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_fan_div}, ++#define LM87_FANDIV_FLAG ++#endif ++#ifndef LM87_AIN2 ++ {LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_fan}, ++#ifndef LM87_FANDIV_FLAG ++ {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_fan_div}, ++#endif /* LM87_FANDIV_FLAG */ ++#endif /* LM87_AIN2 */ ++#ifdef LM87_EXT2 ++ {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_temp}, ++#endif ++ {LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_temp}, ++ {LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_temp}, ++ {LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_alarms}, ++ {LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_analog_out}, ++ {LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_vid}, ++ {LM87_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm87_vrm}, ++ {0} ++}; ++ ++static int lm87_attach_adapter(struct i2c_adapter *adapter) ++{ ++ int error; ++ struct i2c_client_address_data lm87_client_data; ++ ++ lm87_client_data.normal_i2c = addr_data.normal_i2c; ++ lm87_client_data.normal_i2c_range = addr_data.normal_i2c_range; ++ lm87_client_data.probe = addr_data.probe; ++ lm87_client_data.probe_range = addr_data.probe_range; ++ lm87_client_data.ignore = addr_data.ignore; ++ lm87_client_data.ignore_range = addr_data.ignore_range; ++ lm87_client_data.force = addr_data.forces->force; ++ ++ error = i2c_probe(adapter, &lm87_client_data, lm87_detect); ++ i2c_detect(adapter, &addr_data, lm87_detect); ++ ++ return error; ++} ++ ++static int lm87_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct lm87_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access LM87_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &LM87_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if (((lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) ++ != 0x00) || ++ (lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02)) ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put into the global list */ ++ type_name = "lm87"; ++ client_name = "LM87 chip"; ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = lm87_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ LM87_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the LM87 chip */ ++ lm87_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int lm87_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct lm87_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("lm87.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int lm87_read_value(struct i2c_client *client, u8 reg) ++{ ++ return 0xFF & i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new LM87. It should set limits, etc. */ ++static void lm87_init_client(struct i2c_client *client) ++{ ++ struct lm87_data *data = client->data; ++ ++ /* Reset all except Watchdog values and last conversion values ++ This sets fan-divs to 2, among others. This makes most other ++ initializations unnecessary */ ++ lm87_write_value(client, LM87_REG_CONFIG, 0x80); ++ ++ /* Setup Channel Mode register for configuration of monitoring ++ * Default is 00000000b ++ * bit 0 - Configures Fan 1/AIN 1 input (1 = AIN) ++ * bit 1 - Configures Fan 2/AIN 2 input (1 = AIN) ++ * bit 2 - Configures 2.5V&Vccp2/D2 input (1 = 2nd Therm.) ++ * bit 3 - Configures Vcc for 5V/3.3V reading (0 = 3.3V) ++ * bit 4 - Configures IRQ0 Enable if = 1 ++ * bit 5 - Configures IRQ1 Enable if = 1 ++ * bit 6 - Configures IRQ2 Enable if = 1 ++ * bit 7 - Configures VID/IRQ input as interrupts if = 1 ++ */ ++ ++/* I know, not clean, but it works. :'p */ ++ lm87_write_value(client, LM87_REG_CHANNEL_MODE, ++#ifdef LM87_AIN1 ++ 0x01 ++#else ++0 ++#endif ++ | ++#ifdef LM87_AIN2 ++ 0x02 ++#else ++0 ++#endif ++ | ++#ifdef LM87_EXT2 ++ 0x04 ++#else ++0 ++#endif ++ | ++#ifdef LM87_5V_VCC ++0x08 ++#else ++0 ++#endif ++ ); ++ ++ data->vrm = DEFAULT_VRM; ++ ++ /* Start monitoring */ ++ lm87_write_value(client, LM87_REG_CONFIG, 0x01); ++} ++ ++static void lm87_update_client(struct i2c_client *client) ++{ ++ struct lm87_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ) || /* 1 sec cache */ ++ (jiffies < data->last_updated) || ++ !data->valid) { ++ for (i = 0; i <= 5; i++) { ++ data->in[i] = ++ lm87_read_value(client,LM87_REG_IN(i)); ++ data->in_min[i] = ++ lm87_read_value(client,LM87_REG_IN_MIN(i)); ++ data->in_max[i] = ++ lm87_read_value(client,LM87_REG_IN_MAX(i)); ++ } ++ data->ain1 = ++ lm87_read_value(client,LM87_REG_FAN1_AIN1); ++ data->ain1_min = ++ lm87_read_value(client,LM87_REG_AIN1_LOW); ++ data->ain1_max = ++ lm87_read_value(client,LM87_REG_FAN1_AIN1_LIMIT); ++ data->ain2 = ++ lm87_read_value(client,LM87_REG_FAN2_AIN2); ++ data->ain2_min = ++ lm87_read_value(client,LM87_REG_AIN2_LOW); ++ data->ain2_max = ++ lm87_read_value(client,LM87_REG_FAN2_AIN2_LIMIT); ++ ++ data->fan = ++ lm87_read_value(client, LM87_REG_FAN1_AIN1); ++ data->fan_min = ++ lm87_read_value(client, LM87_REG_FAN1_AIN1_LIMIT); ++ data->fan2 = ++ lm87_read_value(client, LM87_REG_FAN2_AIN2); ++ data->fan2_min = ++ lm87_read_value(client, LM87_REG_FAN2_AIN2_LIMIT); ++ ++ data->ext2_temp = ++ lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2); ++ data->ext_temp = ++ lm87_read_value(client, LM87_REG_EXT_TEMP_1); ++ data->int_temp = ++ lm87_read_value(client, LM87_REG_INT_TEMP); ++ ++ data->ext2_temp_max = ++ lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH); ++ data->ext2_temp_min = ++ lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW); ++ ++ data->ext_temp_max = ++ lm87_read_value(client, LM87_REG_EXT_TEMP_1_HIGH); ++ data->ext_temp_min = ++ lm87_read_value(client, LM87_REG_EXT_TEMP_1_LOW); ++ ++ data->int_temp_max = ++ lm87_read_value(client, LM87_REG_INT_TEMP_HIGH); ++ data->int_temp_min = ++ lm87_read_value(client, LM87_REG_INT_TEMP_LOW); ++ ++ i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); ++ data->fan_div = (i >> 4) & 0x03; ++ data->fan2_div = (i >> 6) & 0x03; ++ data->vid = i & 0x0f; ++ data->vid |= ++ (lm87_read_value(client, LM87_REG_VID4) & 0x01) ++ << 4; ++ data->alarms = ++ lm87_read_value(client, LM87_REG_INT1_STAT) + ++ (lm87_read_value(client, LM87_REG_INT2_STAT) << 8); ++ data->analog_out = ++ lm87_read_value(client, LM87_REG_ANALOG_OUT); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void lm87_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ long scales[6] = { 250, 270, ++#ifdef LM87_5V_VCC ++500, ++#else ++330, ++#endif ++ 500, 1200, 270 }; ++ ++ struct lm87_data *data = client->data; ++ int nr = ctl_name - LM87_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ results[0] = ++ ((long)data->in_min[nr] * scales[nr]) / 192; ++ results[1] = ++ ((long)data->in_max[nr] * scales[nr]) / 192; ++ results[2] = ++ ((long)data->in[nr] * scales[nr]) / 192; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = ++ (results[0] * 192) / scales[nr]; ++ lm87_write_value(client, LM87_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = ++ (results[1] * 192) / scales[nr]; ++ lm87_write_value(client, LM87_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++#if defined (LM87_AIN1) || defined (LM87_AIN2) ++void lm87_ain(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ if (ctl_name == LM87_SYSCTL_AIN1) { ++ results[0] = data->ain1_min; ++ results[1] = data->ain1_max; ++ results[2] = data->ain1; ++ } else { ++ results[0] = data->ain2_min; ++ results[1] = data->ain2_max; ++ results[2] = data->ain2; ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (ctl_name == LM87_SYSCTL_AIN1) { ++ data->ain1_min = results[0]; ++ lm87_write_value(client, LM87_REG_AIN1_LOW, ++ data->ain1_min); ++ } else { ++ data->ain2_min = results[0]; ++ lm87_write_value(client, LM87_REG_AIN2_LOW, ++ data->ain2_min); ++ } ++ } ++ if (*nrels_mag >= 2) { ++ if (ctl_name == LM87_SYSCTL_AIN1) { ++ data->ain1_max = results[1]; ++ lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, ++ data->ain1_max); ++ } else { ++ data->ain2_max = results[1]; ++ lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, ++ data->ain2_max); ++ } ++ } ++ } ++} ++#endif ++ ++void lm87_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ if (ctl_name == LM87_SYSCTL_FAN1) { ++ results[0] = FAN_FROM_REG(data->fan_min, ++ DIV_FROM_REG(data->fan_div)); ++ results[1] = FAN_FROM_REG(data->fan, ++ DIV_FROM_REG(data->fan_div)); ++ } else { ++ results[0] = FAN_FROM_REG(data->fan2_min, ++ DIV_FROM_REG(data->fan2_div)); ++ results[1] = FAN_FROM_REG(data->fan2, ++ DIV_FROM_REG(data->fan2_div)); ++ } ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 0) { ++ if (ctl_name == LM87_SYSCTL_FAN1) { ++ data->fan_min = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data->fan_div)); ++ lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, ++ data->fan_min); ++ } else { ++ data->fan2_min = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data->fan2_div)); ++ lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, ++ data->fan2_min); ++ } ++ } ++ } ++} ++ ++ ++void lm87_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm87_update_client(client); ++ ++ /* find out which temp. is being requested */ ++ if (ctl_name == LM87_SYSCTL_TEMP3) ++ { ++ results[0] = TEMP_LIMIT_FROM_REG(data->ext2_temp_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->ext2_temp_min); ++ results[2] = TEMP_FROM_REG(data->ext2_temp); ++ } ++ else if(ctl_name == LM87_SYSCTL_TEMP2) ++ { ++ results[0] = TEMP_LIMIT_FROM_REG(data->ext_temp_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->ext_temp_min); ++ results[2] = TEMP_FROM_REG(data->ext_temp); ++ } ++ else if(ctl_name == LM87_SYSCTL_TEMP1) ++ { ++ results[0] = TEMP_LIMIT_FROM_REG(data->int_temp_max); ++ results[1] = TEMP_LIMIT_FROM_REG(data->int_temp_min); ++ results[2] = TEMP_FROM_REG(data->int_temp); ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (ctl_name == LM87_SYSCTL_TEMP3) { ++ data->ext2_temp_max = TEMP_LIMIT_TO_REG(results[0]); ++ lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH, ++ data->ext2_temp_max); ++ } ++ if (ctl_name == LM87_SYSCTL_TEMP2) { ++ data->ext_temp_max = TEMP_LIMIT_TO_REG(results[0]); ++ lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH, ++ data->ext_temp_max); ++ } ++ if (ctl_name == LM87_SYSCTL_TEMP1) { ++ data->int_temp_max = TEMP_LIMIT_TO_REG(results[0]); ++ lm87_write_value(client, LM87_REG_INT_TEMP_HIGH, ++ data->int_temp_max); ++ } ++ } ++ if (*nrels_mag >= 2) { ++ if (ctl_name == LM87_SYSCTL_TEMP3) { ++ data->ext2_temp_min = TEMP_LIMIT_TO_REG(results[1]); ++ lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW, ++ data->ext2_temp_min); ++ } ++ if (ctl_name == LM87_SYSCTL_TEMP2) { ++ data->ext_temp_min = TEMP_LIMIT_TO_REG(results[1]); ++ lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW, ++ data->ext_temp_min); ++ } ++ if (ctl_name == LM87_SYSCTL_TEMP1) { ++ data->int_temp_min = TEMP_LIMIT_TO_REG(results[1]); ++ lm87_write_value(client, LM87_REG_INT_TEMP_LOW, ++ data->int_temp_min); ++ } ++ } ++ } ++} ++ ++void lm87_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm87_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++/* This gets a little hairy depending on the hardware config */ ++ ++ struct lm87_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++#ifndef LM87_AIN1 ++ results[0] = DIV_FROM_REG(data->fan_div); ++# ifndef LM87_AIN2 ++ results[1] = DIV_FROM_REG(data->fan2_div); ++ *nrels_mag = 2; ++# else ++ *nrels_mag = 1; ++# endif ++#else /* Must be referring to fan 2 */ ++ results[0] = DIV_FROM_REG(data->fan2_div); ++ *nrels_mag = 1; ++#endif ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = lm87_read_value(client, LM87_REG_VID_FAN_DIV); ++/* Note: it's OK to change fan2 div even if fan2 isn't enabled */ ++#ifndef LM87_AIN1 ++ if (*nrels_mag >= 2) { ++ data->fan2_div = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan2_div << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div << 4); ++ lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); ++ } ++#else /* Must be referring to fan 2 */ ++ if (*nrels_mag >= 1) { ++ data->fan2_div = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan2_div << 6); ++ lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); ++ } ++#endif ++ } ++} ++ ++void lm87_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ results[0] = data->analog_out; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->analog_out = results[0]; ++ lm87_write_value(client, LM87_REG_ANALOG_OUT, ++ data->analog_out); ++ } ++ } ++} ++ ++void lm87_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ lm87_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void lm87_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct lm87_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++static int __init sm_lm87_init(void) ++{ ++ printk("lm87.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&LM87_driver); ++} ++ ++static void __exit sm_lm87_exit(void) ++{ ++ i2c_del_driver(&LM87_driver); ++} ++ ++ ++ ++MODULE_LICENSE("GPL"); ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard , Philip Edelbrock , " ++ "Mark Studebaker , and Stephen Rousset "); ++ ++MODULE_DESCRIPTION("LM87 driver"); ++ ++module_init(sm_lm87_init); ++module_exit(sm_lm87_exit); +--- linux-old/drivers/sensors/lm90.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm90.c Mon Dec 13 20:18:49 2004 +@@ -0,0 +1,782 @@ ++/* ++ * lm90.c - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring ++ * Copyright (C) 2003-2004 Jean Delvare ++ * ++ * Based on the lm83 driver. The LM90 is a sensor chip made by National ++ * Semiconductor. It reports up to two temperatures (its own plus up to ++ * one external one) with a 0.125 deg resolution (1 deg for local ++ * temperature) and a 3-4 deg accuracy. Complete datasheet can be ++ * obtained from National's website at: ++ * http://www.national.com/pf/LM/LM90.html ++ * ++ * This driver also supports the LM89 and LM99, two other sensor chips ++ * made by National Semiconductor. Both have an increased remote ++ * temperature measurement accuracy (1 degree), and the LM99 ++ * additionally shifts remote temperatures (measured and limits) by 16 ++ * degrees, which allows for higher temperatures measurement. The ++ * driver doesn't handle it since it can be done easily in user-space. ++ * Complete datasheets can be obtained from National's website at: ++ * http://www.national.com/pf/LM/LM89.html ++ * http://www.national.com/pf/LM/LM99.html ++ * Note that there is no way to differenciate between both chips. ++ * ++ * This driver also supports the LM86, another sensor chip made by ++ * National Semiconductor. It is exactly similar to the LM90 except it ++ * has a higher accuracy. ++ * Complete datasheet can be obtained from National's website at: ++ * http://www.national.com/pf/LM/LM86.html ++ * ++ * This driver also supports the ADM1032, a sensor chip made by Analog ++ * Devices. That chip is similar to the LM90, with a few differences ++ * that are not handled by this driver. Complete datasheet can be ++ * obtained from Analog's website at: ++ * http://products.analog.com/products/info.asp?product=ADM1032 ++ * Among others, it has a higher accuracy than the LM90, much like the ++ * LM86 does. ++ * ++ * This driver also supports the MAX6657 and MAX6658, sensor chips made ++ * by Maxim. These chips are similar to the LM86. Complete datasheet ++ * can be obtained at Maxim's website at: ++ * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 ++ * Note that there is no way to differenciate between both chips (but ++ * no need either). ++ * ++ * Since the LM90 was the first chipset supported by this driver, most ++ * comments will refer to this chipset, but are actually general and ++ * concern all supported chipsets, unless mentioned otherwise. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#ifndef I2C_DRIVERID_LM90 ++#define I2C_DRIVERID_LM90 1042 ++#endif ++ ++/* ++ * Addresses to scan ++ * Address is fully defined internally and cannot be changed. ++ * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c. ++ * LM89-1, and LM99-1 have address 0x4d. ++ */ ++ ++static unsigned short normal_i2c[] = { 0x4c, 0x4d, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* ++ * Insmod parameters ++ */ ++ ++SENSORS_INSMOD_5(lm90, adm1032, lm99, lm86, max6657); ++ ++/* ++ * The LM90 registers ++ */ ++ ++#define LM90_REG_R_MAN_ID 0xFE ++#define LM90_REG_R_CHIP_ID 0xFF ++#define LM90_REG_R_CONFIG1 0x03 ++#define LM90_REG_W_CONFIG1 0x09 ++#define LM90_REG_R_CONFIG2 0xBF ++#define LM90_REG_W_CONFIG2 0xBF ++#define LM90_REG_R_CONVRATE 0x04 ++#define LM90_REG_W_CONVRATE 0x0A ++#define LM90_REG_R_STATUS 0x02 ++#define LM90_REG_R_LOCAL_TEMP 0x00 ++#define LM90_REG_R_LOCAL_HIGH 0x05 ++#define LM90_REG_W_LOCAL_HIGH 0x0B ++#define LM90_REG_R_LOCAL_LOW 0x06 ++#define LM90_REG_W_LOCAL_LOW 0x0C ++#define LM90_REG_R_LOCAL_CRIT 0x20 ++#define LM90_REG_W_LOCAL_CRIT 0x20 ++#define LM90_REG_R_REMOTE_TEMPH 0x01 ++#define LM90_REG_R_REMOTE_TEMPL 0x10 ++#define LM90_REG_R_REMOTE_OFFSH 0x11 ++#define LM90_REG_W_REMOTE_OFFSH 0x11 ++#define LM90_REG_R_REMOTE_OFFSL 0x12 ++#define LM90_REG_W_REMOTE_OFFSL 0x12 ++#define LM90_REG_R_REMOTE_HIGHH 0x07 ++#define LM90_REG_W_REMOTE_HIGHH 0x0D ++#define LM90_REG_R_REMOTE_HIGHL 0x13 ++#define LM90_REG_W_REMOTE_HIGHL 0x13 ++#define LM90_REG_R_REMOTE_LOWH 0x08 ++#define LM90_REG_W_REMOTE_LOWH 0x0E ++#define LM90_REG_R_REMOTE_LOWL 0x14 ++#define LM90_REG_W_REMOTE_LOWL 0x14 ++#define LM90_REG_R_REMOTE_CRIT 0x19 ++#define LM90_REG_W_REMOTE_CRIT 0x19 ++#define LM90_REG_R_TCRIT_HYST 0x21 ++#define LM90_REG_W_TCRIT_HYST 0x21 ++ ++/* ++ * Conversions and various macros ++ * The LM90 uses signed 8-bit values for the local temperatures, ++ * and signed 11-bit values for the remote temperatures (except ++ * T_CRIT). The 11-bit conversion formulas may not round negative ++ * numbers perfectly, but who cares? ++ */ ++ ++#define TEMP1_FROM_REG(val) (val & 0x80 ? val-0x100 : val) ++#define TEMP1_TO_REG(val) (val < 0 ? val+0x100 : val) ++#define TEMP2_FROM_REG(val) (((val & 0x8000 ? val-0x10000 : val) \ ++ * 10 + 128) >> 8) ++#define TEMP2_TO_REG(val) (((val << 8) / 10 + (val < 0 ? \ ++ 0x10000 : 0)) & 0xFFE0) ++#define HYST_TO_REG(val) (val < 0 ? 0 : val > 31 ? 31 : val) ++ ++/* ++ * Functions declaration ++ */ ++ ++static int lm90_attach_adapter(struct i2c_adapter *adapter); ++static int lm90_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void lm90_init_client(struct i2c_client *client); ++static int lm90_detach_client(struct i2c_client *client); ++static void lm90_local_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_local_tcrit(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_remote_tcrit(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_local_hyst(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_remote_hyst(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void lm90_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++/* ++ * Driver data (common to all clients) ++ */ ++ ++static struct i2c_driver lm90_driver = { ++ .owner = THIS_MODULE, ++ .name = "LM90/ADM1032 sensor driver", ++ .id = I2C_DRIVERID_LM90, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm90_attach_adapter, ++ .detach_client = lm90_detach_client ++}; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++ ++struct lm90_data ++{ ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* registers values */ ++ u8 local_temp, local_high, local_low; ++ u16 remote_temp, remote_high, remote_low; /* combined */ ++ u8 local_crit, remote_crit; ++ u8 hyst; /* linked to two sysctl files (hyst1 RW, hyst2 RO) */ ++ u16 alarms; /* bitvector, combined */ ++}; ++ ++/* ++ * Proc entries ++ * These files are created for each detected LM90. ++ */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define LM90_SYSCTL_LOCAL_TEMP 1200 ++#define LM90_SYSCTL_REMOTE_TEMP 1201 ++#define LM90_SYSCTL_LOCAL_TCRIT 1204 ++#define LM90_SYSCTL_REMOTE_TCRIT 1205 ++#define LM90_SYSCTL_LOCAL_HYST 1207 ++#define LM90_SYSCTL_REMOTE_HYST 1208 ++#define LM90_SYSCTL_ALARMS 1210 ++ ++#define LM90_ALARM_LOCAL_HIGH 0x40 ++#define LM90_ALARM_LOCAL_LOW 0x20 ++#define LM90_ALARM_LOCAL_CRIT 0x01 ++#define LM90_ALARM_REMOTE_HIGH 0x10 ++#define LM90_ALARM_REMOTE_LOW 0x08 ++#define LM90_ALARM_REMOTE_CRIT 0x02 ++#define LM90_ALARM_REMOTE_OPEN 0x04 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++ ++static ctl_table lm90_dir_table_template[] = ++{ ++ {LM90_SYSCTL_LOCAL_TEMP, "temp1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_temp}, ++ {LM90_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_temp}, ++ {LM90_SYSCTL_LOCAL_TCRIT, "tcrit1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_tcrit}, ++ {LM90_SYSCTL_REMOTE_TCRIT, "tcrit2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_tcrit}, ++ {LM90_SYSCTL_LOCAL_HYST, "hyst1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_local_hyst}, ++ {LM90_SYSCTL_REMOTE_HYST, "hyst2", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_remote_hyst}, ++ {LM90_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &lm90_alarms}, ++ {0} ++}; ++ ++/* ++ * Internal variables ++ */ ++ ++static int lm90_id = 0; ++ ++/* ++ * Real code ++ */ ++ ++static int lm90_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, lm90_detect); ++} ++ ++/* ++ * The following function does more than just detection. If detection ++ * succeeds, it also registers the new chip. ++ */ ++static int lm90_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ struct i2c_client *new_client; ++ struct lm90_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) ++ { ++ printk("lm90.o: Called for an ISA bus adapter, aborting.\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ { ++#ifdef DEBUG ++ printk("lm90.o: I2C bus doesn't support byte read mode, " ++ "skipping.\n"); ++#endif ++ return 0; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL))) ++ { ++ printk("lm90.o: Out of memory in lm90_detect (new_client).\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * The common I2C client data is placed right before the ++ * LM90-specific data. The LM90-specific data is pointed to by the ++ * data field from the I2C client data. ++ */ ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &lm90_driver; ++ new_client->flags = 0; ++ ++ /* ++ * Now we do the remaining detection. A negative kind means that ++ * the driver was loaded with no force parameter (default), so we ++ * must both detect and identify the chip. A zero kind means that ++ * the driver was loaded with the force parameter, the detection ++ * step shall be skipped. A positive kind means that the driver ++ * was loaded with the force parameter and a given kind of chip is ++ * requested, so both the detection and the identification steps ++ * are skipped. ++ */ ++ ++ /* Default to an LM90 if forced */ ++ if (kind == 0) ++ kind = lm90; ++ ++ if (kind < 0) /* detection and identification */ ++ { ++ u8 man_id, chip_id, reg_config1, reg_convrate; ++ ++ man_id = i2c_smbus_read_byte_data(new_client, ++ LM90_REG_R_MAN_ID); ++ chip_id = i2c_smbus_read_byte_data(new_client, ++ LM90_REG_R_CHIP_ID); ++ reg_config1 = i2c_smbus_read_byte_data(new_client, ++ LM90_REG_R_CONFIG1); ++ reg_convrate = i2c_smbus_read_byte_data(new_client, ++ LM90_REG_R_CONVRATE); ++ ++ if (man_id == 0x01) /* National Semiconductor */ ++ { ++ u8 reg_config2; ++ ++ reg_config2 = i2c_smbus_read_byte_data(new_client, ++ LM90_REG_R_CONFIG2); ++ ++ if ((reg_config1 & 0x2A) == 0x00 ++ && (reg_config2 & 0xF8) == 0x00 ++ && reg_convrate <= 0x09) ++ { ++ if (address == 0x4C ++ && (chip_id & 0xF0) == 0x20) /* LM90 */ ++ kind = lm90; ++ else if ((chip_id & 0xF0) == 0x30) /* LM89/LM99 */ ++ kind = lm99; ++ else if (address == 0x4C ++ && (chip_id & 0xF0) == 0x10) /* LM86 */ ++ kind = lm99; ++ } ++ } ++ else if (man_id == 0x41) /* Analog Devices */ ++ { ++ if (address == 0x4C ++ && (chip_id & 0xF0) == 0x40 /* ADM1032 */ ++ && (reg_config1 & 0x3F) == 0x00 ++ && reg_convrate <= 0x0A) ++ kind = adm1032; ++ } ++ else if (man_id == 0x4D) /* Maxim */ ++ { ++ if (address == 0x4C ++ && (reg_config1 & 0x1F) == 0 ++ && reg_convrate <= 0x09) ++ kind = max6657; ++ } ++ } ++ ++ if (kind <= 0) /* identification failed */ ++ { ++ printk("lm90.o: Unsupported chip.\n"); ++ goto ERROR1; ++ } ++ ++ if (kind == lm90) ++ { ++ type_name = "lm90"; ++ client_name = "LM90 chip"; ++ } ++ else if (kind == adm1032) ++ { ++ type_name = "adm1032"; ++ client_name = "ADM1032 chip"; ++ } ++ else if (kind == lm99) ++ { ++ type_name = "lm99"; ++ client_name = "LM99 chip"; ++ } ++ else if (kind == lm86) ++ { ++ type_name = "lm86"; ++ client_name = "LM86 chip"; ++ } ++ else if (kind == max6657) ++ { ++ type_name = "max6657"; ++ client_name = "MAX6657 chip"; ++ } ++ else ++ { ++ printk("lm90.o: Unknown kind %d.\n", kind); ++ goto ERROR1; ++ } ++ ++ /* ++ * OK, we got a valid chip so we can fill in the remaining client ++ * fields. ++ */ ++ ++ strcpy(new_client->name, client_name); ++ new_client->id = lm90_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* ++ * Tell the I2C layer a new client has arrived. ++ */ ++ ++ if ((err = i2c_attach_client(new_client))) ++ { ++#ifdef DEBUG ++ printk("lm90.o: Failed attaching client.\n"); ++#endif ++ goto ERROR1; ++ } ++ ++ /* ++ * Register a new directory entry. ++ */ ++ ++ if ((err = i2c_register_entry(new_client, type_name, ++ lm90_dir_table_template)) < 0) ++ { ++#ifdef DEBUG ++ printk("lm90.o: Failed registering directory entry.\n"); ++#endif ++ goto ERROR2; ++ } ++ data->sysctl_id = err; ++ ++ /* ++ * Initialize the LM90 chip. ++ */ ++ ++ lm90_init_client(new_client); ++ return 0; ++ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ return err; ++} ++ ++static void lm90_init_client(struct i2c_client *client) ++{ ++ u8 config; ++ ++ /* ++ * Start the conversions. ++ */ ++ ++ i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, ++ 5); /* 2 Hz */ ++ config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); ++ if (config & 0x40) ++ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, ++ config & 0xBF); /* run */ ++} ++ ++ ++static int lm90_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct lm90_data *) (client->data))->sysctl_id); ++ if ((err = i2c_detach_client(client))) ++ { ++ printk("lm90.o: Client deregistration failed, client not " ++ "detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ return 0; ++} ++ ++static void lm90_update_client(struct i2c_client *client) ++{ ++ struct lm90_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ * 2) || ++ (jiffies < data->last_updated) || !data->valid) ++ { ++ u8 oldh, newh; ++#ifdef DEBUG ++ printk("lm90.o: Updating data.\n"); ++#endif ++ ++ data->local_temp = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_TEMP); ++ data->local_high = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_HIGH); ++ data->local_low = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_LOW); ++ data->local_crit = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_LOCAL_CRIT); ++ ++ /* ++ * There is a trick here. We have to read two registers to ++ * have the remote sensor temperature, but we have to beware ++ * a conversion could occur inbetween the readings. The ++ * datasheet says we should either use the one-shot ++ * conversion register, which we don't want to do (disables ++ * hardware monitoring) or monitor the busy bit, which is ++ * impossible (we can't read the values and monitor that bit ++ * at the exact same time). So the solution used here is to ++ * read the high byte once, then the low byte, then the high ++ * byte again. If the new high byte matches the old one, ++ * then we have a valid reading. Else we have to read the low ++ * byte again, and now we believe we have a correct reading. ++ */ ++ ++ oldh = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH); ++ data->remote_temp = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPL); ++ newh = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH); ++ if (newh != oldh) ++ { ++ data->remote_temp = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPL); ++#ifdef DEBUG ++ oldh = /* actually newer */ ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_TEMPH); ++ if (newh != oldh) ++ printk("lm90.o: Remote temperature may be wrong.\n"); ++#endif ++ } ++ data->remote_temp |= (newh << 8); ++ data->remote_high = ++ (i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_HIGHH) << 8) ++ + i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_HIGHL); ++ data->remote_low = ++ (i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_LOWH) << 8) ++ + i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_LOWL); ++ data->remote_crit = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_REMOTE_CRIT); ++ ++ data->hyst = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_TCRIT_HYST); ++ data->alarms = ++ i2c_smbus_read_byte_data(client, LM90_REG_R_STATUS); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++static void lm90_local_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP1_FROM_REG(data->local_high); ++ results[1] = TEMP1_FROM_REG(data->local_low); ++ results[2] = TEMP1_FROM_REG(data->local_temp); ++ *nrels_mag = 3; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->local_high = TEMP1_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_HIGH, ++ data->local_high); ++ } ++ if (*nrels_mag >= 2) ++ { ++ data->local_low = TEMP1_TO_REG(results[1]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_LOW, ++ data->local_low); ++ } ++ } ++} ++ ++static void lm90_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP2_FROM_REG(data->remote_high); ++ results[1] = TEMP2_FROM_REG(data->remote_low); ++ results[2] = TEMP2_FROM_REG(data->remote_temp); ++ *nrels_mag = 3; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->remote_high = TEMP2_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_HIGHH, ++ data->remote_high >> 8); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_HIGHL, ++ data->remote_high & 0xFF); ++ } ++ if (*nrels_mag >= 2) ++ { ++ data->remote_low = TEMP2_TO_REG(results[1]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_LOWH, ++ data->remote_low >> 8); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_LOWL, ++ data->remote_low & 0xFF); ++ } ++ } ++} ++ ++static void lm90_local_tcrit(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP1_FROM_REG(data->local_crit); ++ *nrels_mag = 1; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->local_crit = TEMP1_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_LOCAL_CRIT, ++ data->local_crit); ++ } ++ } ++} ++ ++static void lm90_remote_tcrit(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP1_FROM_REG(data->remote_crit); ++ *nrels_mag = 1; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->remote_crit = TEMP1_TO_REG(results[0]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_REMOTE_CRIT, ++ data->remote_crit); ++ } ++ } ++} ++ ++/* ++ * One quick note about hysteresis. Internally, the hysteresis value ++ * is held in a single register by the LM90, as a relative value. ++ * This relative value applies to both the local critical temperature ++ * and the remote critical temperature. Since all temperatures exported ++ * through procfs have to be absolute, we have to do some conversions. ++ * The solution retained here is to export two absolute values, one for ++ * each critical temperature. In order not to confuse the users too ++ * much, only one file is writable. Would we fail to do so, users ++ * would probably attempt to write to both files, as if they were ++ * independant, and since they aren't, they wouldn't understand why ++ * setting one affects the other one (and would probably claim there's ++ * a bug in the driver). ++ */ ++ ++static void lm90_local_hyst(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP1_FROM_REG(data->local_crit) - ++ TEMP1_FROM_REG(data->hyst); ++ *nrels_mag = 1; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) ++ { ++ if (*nrels_mag >= 1) ++ { ++ data->hyst = HYST_TO_REG(data->local_crit - results[0]); ++ i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, ++ data->hyst); ++ } ++ } ++} ++ ++static void lm90_remote_hyst(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = TEMP1_FROM_REG(data->remote_crit) - ++ TEMP1_FROM_REG(data->hyst); ++ *nrels_mag = 1; ++ } ++} ++ ++static void lm90_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct lm90_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; /* magnitude */ ++ else if (operation == SENSORS_PROC_REAL_READ) ++ { ++ lm90_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++static int __init sm_lm90_init(void) ++{ ++ printk(KERN_INFO "lm90.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&lm90_driver); ++} ++ ++static void __exit sm_lm90_exit(void) ++{ ++ i2c_del_driver(&lm90_driver); ++} ++ ++MODULE_AUTHOR("Jean Delvare "); ++MODULE_DESCRIPTION("LM90/ADM1032 sensor driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_lm90_init); ++module_exit(sm_lm90_exit); +--- linux-old/drivers/sensors/lm92.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/lm92.c Mon Dec 13 20:18:49 2004 +@@ -0,0 +1,421 @@ ++ ++/* ++ * LM92 - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring ++ * ++ * Author: Abraham van der Merwe ++ * ++ * Linux support for the National Semiconductor LM92 Temperature ++ * Sensor. ++ * ++ * Based on code from the lm-sensors project which is available ++ * at http://www.lm-sensors.nu/. lm87.c have been particularly ++ * helpful (: ++ * ++ * This source code is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* if defined, 4 faults must occur consecutively to set alarm flags */ ++/* #define ENABLE_FAULT_QUEUE */ ++ ++#define LM92_REG_TEMPERATURE 0x00 /* ro, 16-bit */ ++#define LM92_REG_CONFIGURATION 0x01 /* rw, 8-bit */ ++#define LM92_REG_TRIP_HYSTERESIS 0x02 /* rw, 16-bit */ ++#define LM92_REG_TRIP_CRITICAL 0x03 /* rw, 16-bit */ ++#define LM92_REG_TRIP_LOW 0x04 /* rw, 16-bit */ ++#define LM92_REG_TRIP_HIGH 0x05 /* rw, 16-bit */ ++#define LM92_REG_MANUFACTURER 0x07 /* ro, 16-bit */ ++ ++#define LM92_MANUFACTURER_ID 0x8001 ++ ++#define TEMP_MIN (-4096) ++#define TEMP_MAX 4095 ++ ++#define LIMIT(x) do { \ ++ if ((x) < TEMP_MIN) (x) = TEMP_MIN; \ ++ if ((x) > TEMP_MAX) (x) = TEMP_MAX; \ ++ } while (0) ++ ++#define PROC_TO_NATIVE(x) ((x) / 625) ++#define NATIVE_TO_PROC(x) ((x) * 625) ++#define CELSIUS(x) ((x) * 16) ++ ++static void lm92_temp (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results); ++static void lm92_alarms (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results); ++ ++/* -- SENSORS SYSCTL START -- */ ++#define LM92_SYSCTL_ALARMS 2001 /* high, low, critical */ ++#define LM92_SYSCTL_TEMP 1200 /* high, low, critical, hysteresis, input */ ++ ++#define LM92_ALARM_TEMP_HIGH 0x01 ++#define LM92_ALARM_TEMP_LOW 0x02 ++#define LM92_ALARM_TEMP_CRIT 0x04 ++#define LM92_TEMP_HIGH 0x08 ++#define LM92_TEMP_LOW 0x10 ++#define LM92_TEMP_CRIT 0x20 ++#define LM92_TEMP_HYST 0x40 ++#define LM92_TEMP_INPUT 0x80 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++static ctl_table lm92_dir_table[] = { ++ {LM92_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm92_temp, NULL}, ++ {LM92_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &lm92_alarms, NULL}, ++ {0} ++}; ++ ++/* NOTE: all temperatures are degrees centigrade * 16 */ ++typedef struct { ++ struct i2c_client client; ++ int sysctl_id; ++ unsigned long timestamp; ++ struct { ++ long high; ++ long low; ++ long crit; ++ long hyst; ++ long input; ++ } temp; ++ struct { ++ long low; ++ long high; ++ long crit; ++ } alarms; ++} lm92_t; ++ ++/* this is needed for each client driver method */ ++static struct i2c_driver lm92_driver; ++ ++/* ensure exclusive access to chip and static variables */ ++static DECLARE_MUTEX (mutex); ++ ++/* addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* insmod parameters */ ++SENSORS_INSMOD_1 (lm92); ++ ++static inline int lm92_write8 (struct i2c_client *client,u8 reg,u8 value) ++{ ++ return (i2c_smbus_write_byte_data (client,reg,value) < 0 ? -EIO : 0); ++} ++ ++static inline int lm92_read16 (struct i2c_client *client,u8 reg,u16 *value) ++{ ++ s32 tmp = i2c_smbus_read_word_data (client,reg); ++ ++ if (tmp < 0) return (-EIO); ++ ++ /* convert the data to little endian format */ ++ *value = swab16((u16) tmp); ++ ++ return (0); ++} ++ ++static inline int lm92_write16 (struct i2c_client *client,u8 reg,u16 value) ++{ ++ /* convert the data to big endian format */ ++ if (i2c_smbus_write_word_data(client, reg, swab16(value)) < 0) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int lm92_read (struct i2c_client *client) ++{ ++ lm92_t *data = (lm92_t *) client->data; ++ u16 value[5]; ++ ++ if ((jiffies - data->timestamp) > HZ) { ++ if (lm92_read16 (client,LM92_REG_TEMPERATURE,value) < 0 || ++ lm92_read16 (client,LM92_REG_TRIP_HYSTERESIS,value + 1) < 0 || ++ lm92_read16 (client,LM92_REG_TRIP_CRITICAL,value + 2) < 0 || ++ lm92_read16 (client,LM92_REG_TRIP_LOW,value + 3) < 0 || ++ lm92_read16 (client,LM92_REG_TRIP_HIGH,value + 4) < 0) ++ return (-EIO); ++ ++ data->temp.input = (s16) value[0] >> 3; ++ data->temp.hyst = (s16) value[1] >> 3; ++ data->temp.crit = (s16) value[2] >> 3; ++ data->temp.low = (s16) value[3] >> 3; ++ data->temp.high = (s16) value[4] >> 3; ++ ++ data->alarms.low = value[0] & 1; ++ data->alarms.high = (value[0] & 2) >> 1; ++ data->alarms.crit = (value[0] & 4) >> 2; ++ ++ data->timestamp = jiffies; ++ } ++ ++ return (0); ++} ++ ++static int lm92_write (struct i2c_client *client) ++{ ++ lm92_t *data = (lm92_t *) client->data; ++ ++ LIMIT (data->temp.hyst); ++ LIMIT (data->temp.crit); ++ LIMIT (data->temp.low); ++ LIMIT (data->temp.high); ++ ++ if (lm92_write16 (client,LM92_REG_TRIP_HYSTERESIS,((s16) data->temp.hyst << 3)) < 0 || ++ lm92_write16 (client,LM92_REG_TRIP_CRITICAL,((s16) data->temp.crit << 3)) < 0 || ++ lm92_write16 (client,LM92_REG_TRIP_LOW,((s16) data->temp.low << 3)) < 0 || ++ lm92_write16 (client,LM92_REG_TRIP_HIGH,((s16) data->temp.high << 3)) < 0) ++ return (-EIO); ++ ++ return (0); ++} ++ ++static void lm92_temp (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results) ++{ ++ if (!down_interruptible (&mutex)) { ++ lm92_t *data = (lm92_t *) client->data; ++ ++ if (operation == SENSORS_PROC_REAL_READ) { ++ lm92_read (client); ++ results[0] = NATIVE_TO_PROC (data->temp.input); ++ results[1] = NATIVE_TO_PROC (data->temp.high); ++ results[2] = NATIVE_TO_PROC (data->temp.low); ++ results[3] = NATIVE_TO_PROC (data->temp.crit); ++ results[4] = NATIVE_TO_PROC (data->temp.hyst); ++ *nrels_mag = 5; ++ } else if (operation == SENSORS_PROC_REAL_WRITE && *nrels_mag == 4) { ++ data->temp.high = PROC_TO_NATIVE (results[0]); ++ data->temp.low = PROC_TO_NATIVE (results[1]); ++ data->temp.crit = PROC_TO_NATIVE (results[2]); ++ data->temp.hyst = PROC_TO_NATIVE (results[3]); ++ lm92_write (client); ++ } else if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 4; ++ } ++ ++ up (&mutex); ++ } ++} ++ ++static void lm92_alarms (struct i2c_client *client,int operation,int ctl_name,int *nrels_mag,long *results) ++{ ++ if (!down_interruptible (&mutex)) { ++ lm92_t *data = (lm92_t *) client->data; ++ ++ if (operation == SENSORS_PROC_REAL_READ) { ++ lm92_read (client); ++ results[0] = data->alarms.high || (data->alarms.low << 1) || (data->alarms.crit << 2); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 0; ++ } ++ ++ up (&mutex); ++ } ++} ++ ++static int max6635_check(struct i2c_client *client) ++{ ++ int i; ++ u16 temp_low, temp_high, temp_hyst, temp_crit; ++ u8 conf; ++ ++ temp_low = i2c_smbus_read_word_data(client, LM92_REG_TRIP_LOW); ++ temp_high = i2c_smbus_read_word_data(client, LM92_REG_TRIP_HIGH); ++ temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TRIP_HYSTERESIS); ++ temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TRIP_CRITICAL); ++ ++ if ((temp_low & 0x7f00) || (temp_high & 0x7f00) ++ || (temp_hyst & 0x7f00) || (temp_crit & 0x7f00)) ++ return 0; ++ ++ conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIGURATION); ++ ++ for (i=0; i<128; i+=16) { ++ if (temp_low != i2c_smbus_read_word_data(client, LM92_REG_TRIP_LOW + i) ++ || temp_high != i2c_smbus_read_word_data(client, LM92_REG_TRIP_HIGH + i) ++ || temp_hyst != i2c_smbus_read_word_data(client, LM92_REG_TRIP_HYSTERESIS + i) ++ || temp_crit != i2c_smbus_read_word_data(client, LM92_REG_TRIP_CRITICAL + i) ++ || conf != i2c_smbus_read_byte_data(client, LM92_REG_CONFIGURATION + i)) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int lm92_init_client (struct i2c_client *client) ++{ ++ lm92_t *data = (lm92_t *) client->data; ++ u8 value = 0; ++ int result; ++ ++ /* force reads to query the chip */ ++ data->timestamp = 0; ++ ++ /* setup the configuration register */ ++ ++#ifdef ENABLE_FAULT_QUEUE ++ value |= 0x10; ++#endif /* #ifdef ENABLE_FAULT_QUEUE */ ++ ++ if (lm92_write8 (client,LM92_REG_CONFIGURATION,value) < 0) ++ return (-ENODEV); ++ ++ /* set default alarm trigger values */ ++ ++ data->temp.high = CELSIUS (64); ++ data->temp.low = CELSIUS (10); ++ data->temp.crit = CELSIUS (80); ++ data->temp.hyst = CELSIUS (2); ++ ++ if ((result = lm92_write (client)) < 0) ++ return (result); ++ ++ /* read everything once so that our cached data is updated */ ++ ++ if ((result = lm92_read (client)) < 0) ++ return (result); ++ ++ return (0); ++} ++ ++static int lm92_detect (struct i2c_adapter *adapter,int address,unsigned short flags,int kind) ++{ ++ static int id = 0; ++ struct i2c_client *client; ++ lm92_t *data; ++ int result; ++ u16 manufacturer; ++ ++ if (!i2c_check_functionality (adapter,I2C_FUNC_SMBUS_BYTE_DATA)) ++ return (-ENODEV); ++ ++ if (!(data = kmalloc(sizeof(lm92_t), GFP_KERNEL))) ++ return (-ENOMEM); ++ ++ client = &data->client; ++ client->addr = address; ++ client->data = data; ++ client->adapter = adapter; ++ client->driver = &lm92_driver; ++ client->flags = 0; ++ strcpy (client->name,lm92_driver.name); ++ ++ if (down_interruptible (&mutex)) { ++ result = -ERESTARTSYS; ++ goto ERROR1; ++ } ++ ++ if (kind < 0) { ++ /* Is it an lm92? */ ++ if (address < 0x4c ++ && (lm92_read16(client,LM92_REG_MANUFACTURER,&manufacturer) < 0 ++ || manufacturer != LM92_MANUFACTURER_ID)) { ++ /* Is it a MAX6635/MAX6635/MAX6635? */ ++ if (!max6635_check(client)) { ++ result = -ENODEV; ++ goto ERROR2; ++ } ++ } ++ } ++ ++ if ((result = i2c_attach_client (client))) { ++ goto ERROR2; ++ } ++ ++ if ((result = i2c_register_entry (client,client->name,lm92_dir_table)) < 0) { ++ goto ERROR3; ++ } ++ data->sysctl_id = result; ++ ++ if ((result = lm92_init_client (client)) < 0) { ++ goto ERROR4; ++ } ++ ++ client->id = id++; ++ ++ up (&mutex); ++ ++ return (0); ++ ++ERROR4: ++ i2c_deregister_entry(data->sysctl_id); ++ERROR3: ++ i2c_detach_client(client); ++ERROR2: ++ up(&mutex); ++ERROR1: ++ kfree(data); ++ return result; ++} ++ ++static int lm92_attach_adapter (struct i2c_adapter *adapter) ++{ ++ return i2c_detect (adapter,&addr_data,lm92_detect); ++} ++ ++static int lm92_detach_client (struct i2c_client *client) ++{ ++ int result; ++ ++ i2c_deregister_entry (((lm92_t *) (client->data))->sysctl_id); ++ ++ if ((result = i2c_detach_client (client))) ++ return (result); ++ ++ kfree(client->data); ++ ++ return (0); ++} ++ ++ ++static struct i2c_driver lm92_driver = { ++ .owner = THIS_MODULE, ++ .name = "lm92", ++ .id = I2C_DRIVERID_LM92, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = lm92_attach_adapter, ++ .detach_client = lm92_detach_client, ++}; ++ ++static int __init sm_lm92_init(void) ++{ ++ printk ("lm92.o version %s (%s)\n",LM_VERSION,LM_DATE); ++ return i2c_add_driver(&lm92_driver); ++} ++ ++ ++static void __exit sm_lm92_exit(void) ++{ ++ i2c_del_driver(&lm92_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ("Abraham van der Merwe "); ++MODULE_DESCRIPTION ("Linux support for LM92 Temperature Sensor"); ++ ++MODULE_LICENSE ("GPL"); ++ ++module_init(sm_lm92_init); ++module_exit(sm_lm92_exit); ++ +--- linux-old/drivers/sensors/matorb.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/matorb.c Mon Dec 13 20:18:49 2004 +@@ -0,0 +1,286 @@ ++/* ++ matorb.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard ++ and Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++ ++#define DEBUG 1 ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x2E, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(matorb); ++ ++/* Many MATORB constants specified below */ ++ ++ ++/* Each client has this additional data */ ++struct matorb_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++}; ++ ++static int matorb_attach_adapter(struct i2c_adapter *adapter); ++static int matorb_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void matorb_init_client(struct i2c_client *client); ++static int matorb_detach_client(struct i2c_client *client); ++ ++static int matorb_write_value(struct i2c_client *client, u8 reg, ++ u16 value); ++static void matorb_disp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void matorb_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver matorb_driver = { ++ .owner = THIS_MODULE, ++ .name = "Matrix Orbital LCD driver", ++ .id = I2C_DRIVERID_MATORB, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = matorb_attach_adapter, ++ .detach_client = matorb_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define MATORB_SYSCTL_DISP 1000 ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected MATORB. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table matorb_dir_table_template[] = { ++ {MATORB_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &matorb_disp}, ++ {0} ++}; ++ ++static int matorb_id = 0; ++ ++static int matorb_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, matorb_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int matorb_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, cur; ++ struct i2c_client *new_client; ++ struct matorb_data *data; ++ int err = 0; ++ const char *type_name = "matorb"; ++ const char *client_name = "matorb"; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("matorb.o: matorb_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE | ++ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ goto ERROR0; ++ ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access matorb_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct matorb_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &matorb_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. It is lousy. */ ++ cur = i2c_smbus_write_byte_data(new_client, 0x0FE, 0x58); /* clear screen */ ++ ++ printk("matorb.o: debug detect 0x%X\n", cur); ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = matorb_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ matorb_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ matorb_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int matorb_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct matorb_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("matorb.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++#if 0 ++/* All registers are word-sized, except for the configuration register. ++ MATORB uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int matorb_read_value(struct i2c_client *client, u8 reg) ++{ ++ return -1; /* Doesn't support reads */ ++} ++#endif ++ ++/* All registers are word-sized, except for the configuration register. ++ MATORB uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int matorb_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if (reg == 0) { ++ return i2c_smbus_write_byte(client, value); ++ } else { ++ return i2c_smbus_write_byte_data(client, reg, value); ++ } ++} ++ ++static void matorb_init_client(struct i2c_client *client) ++{ ++ /* Initialize the MATORB chip */ ++} ++ ++static void matorb_update_client(struct i2c_client *client) ++{ ++ struct matorb_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting matorb update\n"); ++#endif ++ ++/* nothing yet */ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void matorb_disp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int i; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ matorb_update_client(client); ++ results[0] = 0; ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ for (i = 1; i <= *nrels_mag; i++) { ++ matorb_write_value(client, 0, results[i - 1]); ++ } ++ } ++} ++ ++static int __init sm_matorb_init(void) ++{ ++ printk("matorb.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&matorb_driver); ++} ++ ++static void __exit sm_matorb_exit(void) ++{ ++ i2c_del_driver(&matorb_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("MATORB driver"); ++ ++module_init(sm_matorb_init); ++module_exit(sm_matorb_exit); +--- linux-old/drivers/sensors/max6650.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/max6650.c Mon Dec 13 20:18:50 2004 +@@ -0,0 +1,545 @@ ++/* ++ * max6650.c - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring. ++ * ++ * Author: John Morris ++ * ++ * Copyright (c) 2003 Spirent Communications ++ * ++ * This module has only been tested with the MAX6651 chip. It should ++ * work with the MAX6650 also, though with reduced functionality. It ++ * does not yet distinguish max6650 and max6651 chips. ++ * ++ * Tha datasheet was last seen at: ++ * ++ * http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#ifndef I2C_DRIVERID_MAX6650 ++#define I2C_DRIVERID_MAX6650 1044 ++#endif ++ ++/* ++ * Addresses to scan. There are four disjoint possibilities, by pin config. ++ */ ++ ++static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, SENSORS_I2C_END}; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* ++ * Insmod parameters ++ */ ++ ++SENSORS_INSMOD_1(max6650); ++ ++/* ++ * MAX 6650/6651 registers ++ */ ++ ++#define MAX6650_REG_SPEED 0x00 ++#define MAX6650_REG_CONFIG 0x02 ++#define MAX6650_REG_GPIO_DEF 0x04 ++#define MAX6650_REG_DAC 0x06 ++#define MAX6650_REG_ALARM_EN 0x08 ++#define MAX6650_REG_ALARM 0x0A ++#define MAX6650_REG_TACH0 0x0C ++#define MAX6650_REG_TACH1 0x0E ++#define MAX6650_REG_TACH2 0x10 ++#define MAX6650_REG_TACH3 0x12 ++#define MAX6650_REG_GPIO_STAT 0x14 ++#define MAX6650_REG_COUNT 0x16 ++ ++/* ++ * Config register bits ++ */ ++ ++#define MAX6650_CFG_MODE_MASK 0x30 ++#define MAX6650_CFG_MODE_ON 0x00 ++#define MAX6650_CFG_MODE_OFF 0x10 ++#define MAX6650_CFG_MODE_CLOSED_LOOP 0x20 ++#define MAX6650_CFG_MODE_OPEN_LOOP 0x30 ++ ++static const u8 tach_reg[] = ++{ ++ MAX6650_REG_TACH0, MAX6650_REG_TACH1, ++ MAX6650_REG_TACH2, MAX6650_REG_TACH3 ++}; ++ ++#define MAX6650_INT_CLK 254000 /* Default clock speed - 254 kHz */ ++ ++/* ++ * Functions declaration ++ */ ++ ++static void max6650_fan (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++static void max6650_speed (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++static void max6650_xdump (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned ++ short flags, int kind); ++static int max6650_attach_adapter(struct i2c_adapter *adapter); ++static int max6650_detach_client(struct i2c_client *client); ++static void max6650_init_client(struct i2c_client *client); ++static int max6650_read(struct i2c_client *client, u8 reg); ++ ++/* ++ * Driver data (common to all clients) ++ */ ++ ++ ++static struct i2c_driver max6650_driver = { ++ .owner = THIS_MODULE, ++ .name = "MAX6650/1 sensor driver", ++ .id = I2C_DRIVERID_MAX6650, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = max6650_attach_adapter, ++ .detach_client = max6650_detach_client ++}; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++ ++struct max6650_data ++{ ++ struct i2c_client client; ++ int sysctl_id; ++ struct semaphore update_lock; ++ char valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* register values */ ++ ++ u8 speed; ++ u8 config; ++ u8 tach[4]; ++ u8 count; ++}; ++ ++/* ++ * Proc entries ++ * These files are created for each detected max6650. ++ */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define MAX6650_SYSCTL_FAN1 1101 ++#define MAX6650_SYSCTL_FAN2 1102 ++#define MAX6650_SYSCTL_FAN3 1103 ++#define MAX6650_SYSCTL_FAN4 1104 ++#define MAX6650_SYSCTL_SPEED 1105 ++#define MAX6650_SYSCTL_XDUMP 1106 ++ ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++ ++static ctl_table max6650_dir_table_template[] = ++{ ++ {MAX6650_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan}, ++ {MAX6650_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan}, ++ {MAX6650_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan}, ++ {MAX6650_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_fan}, ++ {MAX6650_SYSCTL_SPEED, "speed", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_speed}, ++ {MAX6650_SYSCTL_XDUMP, "xdump", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &max6650_xdump}, ++ {0} ++}; ++ ++/* ++ * Internal variables ++ */ ++ ++static int max6650_id = 0; ++ ++/* ++ * Real code ++ */ ++ ++static int max6650_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, max6650_detect); ++} ++ ++/* ++ * The following function does more than just detection. If detection ++ * succeeds, it also registers the new chip. ++ */ ++ ++static int max6650_detect(struct i2c_adapter *adapter, int address, unsigned ++ short flags, int kind) ++{ ++ struct i2c_client *new_client; ++ struct max6650_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk("max6650.o: Called for an ISA bus adapter, aborting.\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++#ifdef DEBUG ++ printk("max6650.o: I2C bus doesn't support byte read mode, skipping.\n"); ++#endif ++ return 0; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct max6650_data), GFP_KERNEL))) { ++ printk("max6650.o: Out of memory in max6650_detect (new_client).\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * The common I2C client data is placed right before the ++ * max6650-specific data. The max6650-specific data is pointed to by the ++ * data field from the I2C client data. ++ */ ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &max6650_driver; ++ new_client->flags = 0; ++ ++ /* ++ * Now we do the remaining detection. A negative kind means that ++ * the driver was loaded with no force parameter (default), so we ++ * must both detect and identify the chip (actually there is only ++ * one possible kind of chip for now, max6650). A zero kind means that ++ * the driver was loaded with the force parameter, the detection ++ * step shall be skipped. A positive kind means that the driver ++ * was loaded with the force parameter and a given kind of chip is ++ * requested, so both the detection and the identification steps ++ * are skipped. ++ * ++ * Currently I can find no way to distinguish between a MAX6650 and ++ * a MAX6651. This driver has only been tried on the latter. ++ */ ++ ++ if (kind < 0) { /* detection */ ++ if ( ++ (max6650_read(new_client, MAX6650_REG_CONFIG) & 0xC0) || ++ (max6650_read(new_client, MAX6650_REG_GPIO_STAT) & 0xE0) || ++ (max6650_read(new_client, MAX6650_REG_ALARM_EN) & 0xE0) || ++ (max6650_read(new_client, MAX6650_REG_ALARM) & 0xE0) || ++ (max6650_read(new_client, MAX6650_REG_COUNT) & 0xFC) ++ ) ++ { ++#ifdef DEBUG ++ printk("max6650.o: max6650 detection failed at 0x%02x.\n", ++ address); ++#endif ++ goto ERROR1; ++ } ++ } ++ ++ if (kind <= 0) { /* identification */ ++ kind = max6650; ++ } ++ ++ if (kind <= 0) { /* identification failed */ ++ printk("max6650.o: Unsupported chip.\n"); ++ goto ERROR1; ++ } ++ ++ if (kind == max6650) { ++ type_name = "max6650"; ++ client_name = "max6650 chip"; ++ } else { ++ printk("max6650.o: Unknown kind %d.\n", kind); ++ goto ERROR1; ++ } ++ ++ /* ++ * OK, we got a valid chip so we can fill in the remaining client ++ * fields. ++ */ ++ ++ strcpy(new_client->name, client_name); ++ new_client->id = max6650_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* ++ * Tell the I2C layer a new client has arrived. ++ */ ++ ++ if ((err = i2c_attach_client(new_client))) { ++#ifdef DEBUG ++ printk("max6650.o: Failed attaching client.\n"); ++#endif ++ goto ERROR1; ++ } ++ ++ /* ++ * Register a new directory entry. ++ */ ++ if ((err = i2c_register_entry(new_client, type_name, ++ max6650_dir_table_template)) < 0) { ++#ifdef DEBUG ++ printk("max6650.o: Failed registering directory entry.\n"); ++#endif ++ goto ERROR2; ++ } ++ data->sysctl_id = err; ++ ++ /* ++ * Initialize the max6650 chip ++ */ ++ max6650_init_client(new_client); ++ return 0; ++ ++ERROR2: ++ i2c_detach_client(new_client); ++ERROR1: ++ kfree(data); ++ return err; ++} ++ ++static void max6650_init_client(struct i2c_client *client) ++{ ++ /* Nothing to do here - assume the BIOS has initialized the chip */ ++} ++ ++static int max6650_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct max6650_data *) (client->data))->sysctl_id); ++ if ((err = i2c_detach_client(client))) { ++ printk("max6650.o: Client deregistration failed, " ++ "client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ return 0; ++} ++ ++static int max6650_read(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int max6650_write(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void max6650_update_client(struct i2c_client *client) ++{ ++ int i; ++ struct max6650_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ) || ++ (jiffies < data->last_updated) || !data->valid) { ++#ifdef DEBUG ++ printk("max6650.o: Updating max6650 data.\n"); ++#endif ++ data->speed = max6650_read (client, MAX6650_REG_SPEED); ++ data->config = max6650_read (client, MAX6650_REG_CONFIG); ++ for (i = 0; i < 4; i++) { ++ data->tach[i] = max6650_read(client, tach_reg[i]); ++ } ++ data->count = max6650_read (client, MAX6650_REG_COUNT); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ up(&data->update_lock); ++} ++ ++static void max6650_fan (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ int index = ctl_name - MAX6650_SYSCTL_FAN1; ++ struct max6650_data *data = client->data; ++ int tcount; /* Tachometer count time, 0.25 second units */ ++ ++ if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 0; ++ } else if (operation == SENSORS_PROC_REAL_READ) { ++ max6650_update_client(client); ++ ++ /* ++ * Calculation details: ++ * ++ * Each tachometer counts over an interval given by the "count" ++ * register (0.25, 0.5, 1 or 2 seconds). This module assumes ++ * that the fans produce two pulses per revolution (this seems ++ * to be the most common). ++ */ ++ ++ tcount = 1 << data->count; /* 0.25 second units */ ++ results[0] = (data->tach[index] * 240) / tcount; /* counts per min */ ++ results[0] /= 2; /* Assume two counts per rev */ ++ *nrels_mag = 1; ++ } ++} ++ ++/* ++ * Set the fan speed to the specified RPM (or read back the RPM setting). ++ * ++ * The MAX6650/1 will automatically control fan speed when in closed loop ++ * mode. ++ * ++ * Assumptions: ++ * ++ * 1) The MAX6650/1 is running from its internal 254kHz clock (perhaps ++ * this should be made a module parameter). ++ * ++ * 2) The prescaler (low three bits of the config register) has already ++ * been set to an appropriate value. ++ * ++ * The relevant equations are given on pages 21 and 22 of the datasheet. ++ * ++ * From the datasheet, the relevant equation when in regulation is: ++ * ++ * [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE ++ * ++ * where: ++ * ++ * fCLK is the oscillator frequency (either the 254kHz internal ++ * oscillator or the externally applied clock) ++ * ++ * KTACH is the value in the speed register ++ * ++ * FanSpeed is the speed of the fan in rps ++ * ++ * KSCALE is the prescaler value (1, 2, 4, 8, or 16) ++ * ++ * When reading, we need to solve for FanSpeed. When writing, we need to ++ * solve for KTACH. ++ * ++ * Note: this tachometer is completely separate from the tachometers ++ * used to measure the fan speeds. Only one fan's speed (fan1) is ++ * controlled. ++ */ ++ ++static void max6650_speed (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ struct max6650_data *data = client->data; ++ int kscale, ktach, fclk, rpm; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 0; ++ } else if (operation == SENSORS_PROC_REAL_READ) { ++ /* ++ * Use the datasheet equation: ++ * ++ * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] ++ * ++ * then multiply by 60 to give rpm. ++ */ ++ ++ max6650_update_client(client); ++ ++ kscale = 1 << (data->config & 7); ++ ktach = data->speed; ++ fclk = MAX6650_INT_CLK; ++ rpm = 60 * kscale * fclk / (256 * (ktach + 1)); ++ ++ results[0] = rpm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE && *nrels_mag >= 1) { ++ /* ++ * Divide the required speed by 60 to get from rpm to rps, then ++ * use the datasheet equation: ++ * ++ * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 ++ */ ++ ++ max6650_update_client(client); ++ ++ rpm = results[0]; ++ kscale = 1 << (data->config & 7); ++ fclk = MAX6650_INT_CLK; ++ ktach = ((fclk * kscale) / (256 * rpm / 60)) - 1; ++ ++ data->speed = ktach; ++ data->config = (data->config & ~MAX6650_CFG_MODE_MASK) | ++ MAX6650_CFG_MODE_CLOSED_LOOP; ++ max6650_write (client, MAX6650_REG_CONFIG, data->config); ++ max6650_write (client, MAX6650_REG_SPEED, data->speed); ++ } ++} ++ ++/* ++ * Debug - dump all registers except the tach counts. ++ */ ++ ++static void max6650_xdump (struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results) ++{ ++ if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 0; ++ } else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = max6650_read (client, MAX6650_REG_SPEED); ++ results[1] = max6650_read (client, MAX6650_REG_CONFIG); ++ results[2] = max6650_read (client, MAX6650_REG_GPIO_DEF); ++ results[3] = max6650_read (client, MAX6650_REG_DAC); ++ results[4] = max6650_read (client, MAX6650_REG_ALARM_EN); ++ results[5] = max6650_read (client, MAX6650_REG_ALARM); ++ results[6] = max6650_read (client, MAX6650_REG_GPIO_STAT); ++ results[7] = max6650_read (client, MAX6650_REG_COUNT); ++ *nrels_mag = 8; ++ } ++} ++ ++static int __init sm_max6650_init(void) ++{ ++ printk(KERN_INFO "max6650.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&max6650_driver); ++} ++ ++static void __exit sm_max6650_exit(void) ++{ ++ i2c_del_driver(&max6650_driver); ++} ++ ++MODULE_AUTHOR("john.morris@spirentcom.com"); ++MODULE_DESCRIPTION("max6650 sensor driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_max6650_init); ++module_exit(sm_max6650_exit); +--- linux-old/drivers/sensors/maxilife.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/maxilife.c Mon Dec 13 20:18:50 2004 +@@ -0,0 +1,1387 @@ ++/* ++ maxilife.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1999-2000 Fons Rademakers ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* The is the driver for the HP MaxiLife Health monitoring system ++ as used in the line of HP Kayak Workstation PC's. ++ ++ The driver supports the following MaxiLife firmware versions: ++ ++ 0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath) ++ 1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) ++ 2) HP KAYAK XA (Pentium II Slot 1, monoprocessor) ++ ++ Currently firmware auto detection is not implemented. To use the ++ driver load it with the correct option for you Kayak. For example: ++ ++ insmod maxilife.o maxi_version=0 | 1 | 2 ++ ++ maxi_version=0 is the default ++ ++ This version of MaxiLife is called MaxiLife'98 and has been ++ succeeded by MaxiLife'99, see below. ++ ++ The new version of the driver also supports MaxiLife NBA (New BIOS ++ Architecture). This new MaxiLife controller provides a much cleaner ++ machine independent abstraction layer to the MaxiLife controller. ++ Instead of accessing directly registers (different for each revision) ++ one now accesses the sensors via unique mailbox tokens that do not ++ change between revisions. Also the quantities are already in physical ++ units (degrees, rpms, voltages, etc.) and don't need special conversion ++ formulas. This new MaxiLife is available on the new 2000 machines, ++ like the Kayak XU800 and XM600. This hardware is also autodetected. ++*/ ++ ++static const char *version_str = "2.00 29/2/2000 Fons Rademakers"; ++ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++#undef AUTODETECT /* try to autodetect MaxiLife version */ ++/*#define AUTODETECT*/ ++#define NOWRITE /* don't allow writing to MaxiLife registers */ ++ ++#ifdef AUTODETECT ++#include ++#include ++#endif ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x10, 0x14, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(maxilife); ++ ++/* Macro definitions */ ++#define LOW(MyWord) ((u8) (MyWord)) ++#define HIGH(MyWord) ((u8) (((u16)(MyWord) >> 8) & 0xFF)) ++ ++/*----------------- MaxiLife'98 registers and conversion formulas ------------*/ ++#define MAXI_REG_TEMP(nr) (0x60 + (nr)) ++ ++#define MAXI_REG_FAN(nr) (0x65 + (nr)) ++#define MAXI_REG_FAN_MIN(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab) ++#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3) ++#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9) ++ ++#define MAXI_REG_PLL 0xb9 ++#define MAXI_REG_PLL_MIN 0xba ++#define MAXI_REG_PLL_MAX 0xbb ++ ++#define MAXI_REG_VID(nr) ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \ ++ (nr)==2 ? 0xd4 : 0xc5) ++#define MAXI_REG_VID_MIN(nr) MAXI_REG_VID(nr)+1 ++#define MAXI_REG_VID_MAX(nr) MAXI_REG_VID(nr)+2 ++ ++#define MAXI_REG_DIAG_RT1 0x2c ++#define MAXI_REG_DIAG_RT2 0x2d ++ ++#define MAXI_REG_BIOS_CTRL 0x2a ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++ /* 0xfe: fan off, 0xff: stopped (alarm) */ ++ /* 19531 / val * 60 == 1171860 / val */ ++#define FAN_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : \ ++ (val)==0x00 ? -1 : (1171860 / (val))) ++ ++static inline u8 FAN_TO_REG(long rpm) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1171860 + rpm / 2) / (rpm), 1, 254); ++} ++ ++#define TEMP_FROM_REG(val) ((val) * 5) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT((val+2) / 5),0,0xff) ++#define PLL_FROM_REG(val) (((val) * 1000) / 32) ++#define PLL_TO_REG(val) (SENSORS_LIMIT((((val) * 32 + 500) / 1000),\ ++ 0,0xff)) ++#define VID_FROM_REG(val) ((val) ? (((val) * 27390) / 256) + 3208 : 0) ++#define VID_TO_REG(val) (SENSORS_LIMIT((((val) - 3208) * 256) / 27390, \ ++ 0,255)) ++#define ALARMS_FROM_REG(val) (val) ++ ++/*----------------- MaxiLife'99 mailbox and token definitions ----------------*/ ++/* MaxiLife mailbox data register map */ ++#define MAXI_REG_MBX_STATUS 0x5a ++#define MAXI_REG_MBX_CMD 0x5b ++#define MAXI_REG_MBX_TOKEN_H 0x5c ++#define MAXI_REG_MBX_TOKEN_L 0x5d ++#define MAXI_REG_MBX_DATA 0x60 ++ ++/* Mailbox status register definition */ ++#define MAXI_STAT_IDLE 0xff ++#define MAXI_STAT_OK 0x00 ++#define MAXI_STAT_BUSY 0x0b ++/* other values not used */ ++ ++/* Mailbox command register opcodes */ ++#define MAXI_CMD_READ 0x02 ++#define MAXI_CMD_WRITE 0x03 ++/* other values not used */ ++ ++/* MaxiLife NBA Hardware monitoring tokens */ ++ ++/* Alarm tokens (0x1xxx) */ ++#define MAXI_TOK_ALARM(nr) (0x1000 + (nr)) ++#define MAXI_TOK_ALARM_EVENT 0x1000 ++#define MAXI_TOK_ALARM_FAN 0x1001 ++#define MAXI_TOK_ALARM_TEMP 0x1002 ++#define MAXI_TOK_ALARM_VID 0x1003 /* voltages */ ++#define MAXI_TOK_ALARM_AVID 0x1004 /* additional voltages */ ++#define MAXI_TOK_ALARM_PWR 0x1101 /* power supply glitch */ ++ ++/* Fan status tokens (0x20xx) */ ++#define MAXI_TOK_FAN(nr) (0x2000 + (nr)) ++#define MAXI_TOK_FAN_CPU 0x2000 ++#define MAXI_TOK_FAN_PCI 0x2001 ++#define MAXI_TOK_FAN_HDD 0x2002 /* hard disk bay fan */ ++#define MAXI_TOK_FAN_SINK 0x2003 /* heatsink */ ++ ++/* Temperature status tokens (0x21xx) */ ++#define MAXI_TOK_TEMP(nr) (0x2100 + (nr)) ++#define MAXI_TOK_TEMP_CPU1 0x2100 ++#define MAXI_TOK_TEMP_CPU2 0x2101 ++#define MAXI_TOK_TEMP_PCI 0x2102 /* PCI/ambient temp */ ++#define MAXI_TOK_TEMP_HDD 0x2103 /* hard disk bay temp */ ++#define MAXI_TOK_TEMP_MEM 0x2104 /* mother board temp */ ++#define MAXI_TOK_TEMP_CPU 0x2105 /* CPU reference temp */ ++ ++/* Voltage status tokens (0x22xx) */ ++#define MAXI_TOK_VID(nr) (0x2200 + (nr)) ++#define MAXI_TOK_VID_12 0x2200 /* +12 volt */ ++#define MAXI_TOK_VID_CPU1 0x2201 /* cpu 1 voltage */ ++#define MAXI_TOK_VID_CPU2 0x2202 /* cpu 2 voltage */ ++#define MAXI_TOK_VID_L2 0x2203 /* level 2 cache voltage */ ++#define MAXI_TOK_VID_M12 0x2204 /* -12 volt */ ++ ++/* Additive voltage status tokens (0x23xx) */ ++#define MAXI_TOK_AVID(nr) (0x2300 + (nr)) ++#define MAXI_TOK_AVID_15 0x2300 /* 1.5 volt */ ++#define MAXI_TOK_AVID_18 0x2301 /* 1.8 volt */ ++#define MAXI_TOK_AVID_25 0x2302 /* 2.5 volt */ ++#define MAXI_TOK_AVID_33 0x2303 /* 3.3 volt */ ++#define MAXI_TOK_AVID_5 0x2304 /* 5 volt */ ++#define MAXI_TOK_AVID_M5 0x2305 /* -5 volt */ ++#define MAXI_TOK_AVID_BAT 0x2306 /* battery voltage */ ++ ++/* Threshold tokens (0x3xxx) */ ++#define MAXI_TOK_MIN(token) ((token) + 0x1000) ++#define MAXI_TOK_MAX(token) ((token) + 0x1800) ++ ++/* LCD Panel (0x4xxx) */ ++#define MAXI_TOK_LCD(nr) (0x4000 + (nr)) ++#define MAXI_TOK_LCD_LINE1 0x4000 ++#define MAXI_TOK_LCD_LINE2 0x4001 ++#define MAXI_TOK_LCD_LINE3 0x4002 ++#define MAXI_TOK_LCD_LINE4 0x4003 ++ ++ /* 0xfe: fan off, 0xff: stopped (alarm) */ ++ /* or not available */ ++#define FAN99_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : ((val)*39)) ++ ++ /* when no CPU2 temp is 127 (0x7f) */ ++#define TEMP99_FROM_REG(val) ((val)==0x7f ? -1 : (val)==0xff ? -1 : (val)) ++ ++#define VID99_FROM_REG(nr,val) ((val)==0xff ? 0 : \ ++ (nr)==1 ? ((val) * 608) : \ ++ (nr)==2 ? ((val) * 160) : \ ++ (nr)==3 ? ((val) * 160) : \ ++ (nr)==4 ? (val) /* no formula spcified */ : \ ++ (nr)==5 ? ((val) * 823 - 149140) : 0) ++ ++ ++/* The following product codenames apply: ++ Cristal/Geronimo: HP KAYAK XU/XAs ++ (Dual Pentium II Slot 1, Deschutes/Klamath) ++ Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) ++ Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor) ++ NBA: New BIOS Architecture, Kayak XU800, XM600, ... */ ++ ++enum maxi_type { cristal, cognac, ashaki, nba }; ++enum sensor_type { fan, temp, vid, pll, lcd, alarm }; ++ ++/* For each registered MaxiLife controller, we need to keep some data in ++ memory. That data is pointed to by maxi_list[NR]->data. The structure ++ itself is dynamically allocated, at the same time when a new MaxiLife ++ client is allocated. We assume MaxiLife will only be present on the ++ SMBus and not on the ISA bus. */ ++struct maxi_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum maxi_type type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 fan[4]; /* Register value */ ++ u8 fan_min[4]; /* Register value */ ++ u8 fan_speed[4]; /* Register value */ ++ u8 fan_div[4]; /* Static value */ ++ u8 temp[6]; /* Register value */ ++ u8 temp_max[6]; /* Static value */ ++ u8 temp_hyst[6]; /* Static value */ ++ u8 pll; /* Register value */ ++ u8 pll_min; /* Register value */ ++ u8 pll_max; /* register value */ ++ u8 vid[5]; /* Register value */ ++ u8 vid_min[5]; /* Register value */ ++ u8 vid_max[5]; /* Register value */ ++ u8 lcd[4][17]; /* Four LCD lines */ ++ u16 alarms; /* Register encoding, combined */ ++}; ++ ++ ++static int maxi_attach_adapter(struct i2c_adapter *adapter); ++static int maxi_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int maxi_detach_client(struct i2c_client *client); ++ ++static int maxi_read_value(struct i2c_client *client, u8 register); ++static int maxi_read_token(struct i2c_client *client, u16 token); ++#ifndef NOWRITE ++static int maxi_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++#endif ++static int maxi_write_token_loop(struct i2c_client *client, u16 token, ++ u8 len, u8 * values); ++ ++static void maxi_update_client(struct i2c_client *client); ++static void maxi99_update_client(struct i2c_client *client, ++ enum sensor_type sensor, int which); ++static void maxi_init_client(struct i2c_client *client); ++ ++static void maxi_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi99_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi99_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi_pll(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi99_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi_lcd(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void maxi_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++/* The driver. I choose to use type i2c_driver, as at is identical to ++ the smbus_driver. */ ++static struct i2c_driver maxi_driver = { ++ .owner = THIS_MODULE, ++ .name = "HP MaxiLife driver", ++ .id = I2C_DRIVERID_MAXILIFE, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = maxi_attach_adapter, ++ .detach_client = maxi_detach_client, ++}; ++ ++static int maxi_id = 0; ++ ++/* Default firmware version. Use module option "maxi_version" ++ to set desired version. Auto detect is not yet working */ ++static int maxi_version = cristal; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define MAXI_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define MAXI_SYSCTL_FAN2 1102 /* Rotations/min */ ++#define MAXI_SYSCTL_FAN3 1103 /* Rotations/min */ ++#define MAXI_SYSCTL_FAN4 1104 /* Rotations/min */ ++#define MAXI_SYSCTL_TEMP1 1201 /* Degrees Celcius */ ++#define MAXI_SYSCTL_TEMP2 1202 /* Degrees Celcius */ ++#define MAXI_SYSCTL_TEMP3 1203 /* Degrees Celcius */ ++#define MAXI_SYSCTL_TEMP4 1204 /* Degrees Celcius */ ++#define MAXI_SYSCTL_TEMP5 1205 /* Degrees Celcius */ ++#define MAXI_SYSCTL_TEMP6 1206 /* Degrees Celcius */ ++#define MAXI_SYSCTL_PLL 1301 /* MHz */ ++#define MAXI_SYSCTL_VID1 1401 /* Volts / 6.337, for nba just Volts */ ++#define MAXI_SYSCTL_VID2 1402 /* Volts */ ++#define MAXI_SYSCTL_VID3 1403 /* Volts */ ++#define MAXI_SYSCTL_VID4 1404 /* Volts */ ++#define MAXI_SYSCTL_VID5 1405 /* Volts */ ++#define MAXI_SYSCTL_LCD1 1501 /* Line 1 of LCD */ ++#define MAXI_SYSCTL_LCD2 1502 /* Line 2 of LCD */ ++#define MAXI_SYSCTL_LCD3 1503 /* Line 3 of LCD */ ++#define MAXI_SYSCTL_LCD4 1504 /* Line 4 of LCD */ ++#define MAXI_SYSCTL_ALARMS 2001 /* Bitvector (see below) */ ++ ++#define MAXI_ALARM_VID4 0x0001 ++#define MAXI_ALARM_TEMP2 0x0002 ++#define MAXI_ALARM_VID1 0x0004 ++#define MAXI_ALARM_VID2 0x0008 ++#define MAXI_ALARM_VID3 0x0010 ++#define MAXI_ALARM_PLL 0x0080 ++#define MAXI_ALARM_TEMP4 0x0100 ++#define MAXI_ALARM_TEMP5 0x0200 ++#define MAXI_ALARM_FAN1 0x1000 ++#define MAXI_ALARM_FAN2 0x2000 ++#define MAXI_ALARM_FAN3 0x4000 ++ ++#define MAXI_ALARM_FAN 0x0100 /* To be used with MaxiLife'99 */ ++#define MAXI_ALARM_VID 0x0200 /* The MSB specifies which sensor */ ++#define MAXI_ALARM_TEMP 0x0400 /* in the alarm group failed, i.e.: */ ++#define MAXI_ALARM_VADD 0x0800 /* 0x0402 = TEMP2 failed = CPU2 temp */ ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected MaxiLife processor. ++ This is just a template; though at first sight, you might think we ++ could use a statically allocated list, we need some way to get back ++ to the parent - which is done through one of the 'extra' fields ++ which are initialized when a new copy is allocated. */ ++static ctl_table maxi_dir_table_template[] = { ++ {MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_fan}, ++ {MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_fan}, ++ {MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_fan}, ++ {MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_fan}, ++ {MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_temp}, ++ {MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_pll}, ++ {MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_vid}, ++ {MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_vid}, ++ {MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_vid}, ++ {MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_vid}, ++ {MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_vid}, ++ {MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_lcd}, ++ {MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_lcd}, ++ {MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_lcd}, ++ {MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_lcd}, ++ {MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &maxi_alarms}, ++ {0} ++}; ++ ++/* This function is called when: ++ - maxi_driver is inserted (when this module is loaded), for each ++ available adapter ++ - when a new adapter is inserted (and maxi_driver is still present) */ ++static int maxi_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, maxi_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int maxi_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ struct i2c_client *new_client; ++ struct maxi_data *data; ++ enum maxi_type type = 0; ++ int i, j, err = 0; ++ const char *type_name = NULL, *client_name = NULL; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access maxi_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct maxi_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ /* Fill the new client structure with data */ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &maxi_driver; ++ new_client->flags = 0; ++ ++ /* Now we do the remaining detection. */ ++ if (kind < 0) { ++ if (i2c_smbus_read_byte_data ++ (new_client, MAXI_REG_MBX_STATUS) < 0) ++ goto ERROR2; ++ } ++ ++ /* Determine the chip type - only one kind supported */ ++ if (kind <= 0) ++ kind = maxilife; ++ ++ if (kind == maxilife) { ++ /* Detect if the machine has a MaxiLife NBA controller. ++ The right way to perform this check is to do a read/modify/write ++ on register MbxStatus (5A): ++ - Read 5A (value 0 for non-NBA firmware, FF (MbxIdle on NBA-firmware) ++ - Write 55 on 5A, then read back 5A ++ Non-NBA firmware: value is 55 (reg 5A is a standard writable reg) ++ NBA firmaware: value is FF (write-protect on MbxStatus active) */ ++ int stat; ++ i2c_smbus_write_byte_data(new_client, MAXI_REG_MBX_STATUS, ++ 0x55); ++ stat = ++ i2c_smbus_read_byte_data(new_client, ++ MAXI_REG_MBX_STATUS); ++ ++ /*if (stat == MAXI_STAT_IDLE || stat == MAXI_STAT_OK) */ ++ if (stat != 0x55) ++ maxi_version = nba; ++#ifdef AUTODETECT ++ else { ++ /* The right way to get the platform info is to read the firmware ++ revision from serial EEPROM (addr=0x54), at offset 0x0045. ++ This is a string as: ++ "CG 00.04" -> Cristal [XU] / Geronimo [XAs] ++ "CO 00.03" -> Cognac [XU] ++ "AS 00.01" -> Ashaki [XA] */ ++#if 0 ++ int biosctl; ++ biosctl = ++ i2c_smbus_read_byte_data(new_client, ++ MAXI_REG_BIOS_CTRL); ++ i2c_smbus_write_byte_data(new_client, ++ MAXI_REG_BIOS_CTRL, ++ biosctl | 4); ++ err = eeprom_read_byte_data(adapter, 0x54, 0x45); ++ i2c_smbus_write_byte_data(new_client, ++ MAXI_REG_BIOS_CTRL, ++ biosctl); ++#endif ++ int i; ++ char *biosmem, *bm; ++ bm = biosmem = ioremap(0xe0000, 0x20000); ++ if (biosmem) { ++ printk("begin of bios search\n"); ++ for (i = 0; i < 0x20000; i++) { ++ if (*bm == 'C') { ++ char *s = bm; ++ while (s && isprint(*s)) { ++ printk("%c", *s); ++ s++; ++ } ++ printk("\n"); ++ if (!strncmp ++ (bm, "CG 00.04", 8)) { ++ maxi_version = ++ cristal; ++ printk ++ ("maxilife: found MaxiLife Rev CG 00.04\n"); ++ break; ++ } ++ if (!strncmp ++ (bm, "CO 00.03", 8)) { ++ maxi_version = ++ cognac; ++ printk ++ ("maxilife: found MaxiLife Rev CO 00.03\n"); ++ break; ++ } ++ } ++ if (*bm == 'A' && *(bm + 1) == 'S') { ++ char *s = bm; ++ while (s && isprint(*s)) { ++ printk("%c", *s); ++ s++; ++ } ++ printk("\n"); ++ if (!strncmp ++ (bm, "AS 00.01", 8)) { ++ maxi_version = ++ ashaki; ++ printk ++ ("maxilife: found MaxiLife Rev AS 00.01\n"); ++ break; ++ } ++ } ++ bm++; ++ } ++ printk("end of bios search\n"); ++ } else ++ printk("could not map bios memory\n"); ++ } ++#endif ++ ++ if (maxi_version == cristal) { ++ type = cristal; ++ type_name = "maxilife-cg"; ++ client_name = "HP MaxiLife Rev CG 00.04"; ++ printk ++ ("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n"); ++ } else if (maxi_version == cognac) { ++ type = cognac; ++ type_name = "maxilife-co"; ++ client_name = "HP MaxiLife Rev CO 00.03"; ++ printk ++ ("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n"); ++ } else if (maxi_version == ashaki) { ++ type = ashaki; ++ type_name = "maxilife-as"; ++ client_name = "HP MaxiLife Rev AS 00.01"; ++ printk ++ ("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n"); ++ } else if (maxi_version == nba) { ++ type = nba; ++ type_name = "maxilife-nba"; ++ client_name = "HP MaxiLife NBA"; ++ printk("maxilife: HP KAYAK XU800/XM600\n"); ++ } else { ++#ifdef AUTODETECT ++ printk ++ ("maxilife: Warning: probed non-maxilife chip?!? (%x)\n", ++ err); ++#else ++ printk ++ ("maxilife: Error: specified wrong maxi_version (%d)\n", ++ maxi_version); ++#endif ++ goto ERROR2; ++ } ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ((struct maxi_data *) (new_client->data))->type = type; ++ ++ for (i = 0; i < 4; i++) ++ for (j = 0; j < 17; j++) ++ ((struct maxi_data *) (new_client->data))-> ++ lcd[i][j] = (u8) 0; ++ ++ new_client->id = maxi_id++; ++ ++ data->valid = 0; ++ init_MUTEX(&data->lock); ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell i2c-core that a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR2; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((err = i2c_register_entry(new_client, type_name, ++ maxi_dir_table_template)) < 0) ++ goto ERROR4; ++ data->sysctl_id = err; ++ ++ /* Initialize the MaxiLife chip */ ++ maxi_init_client(new_client); ++ return 0; ++ ++ /* OK, this is not exactly good programming practice, usually. ++ But it is very code-efficient in this case. */ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR2: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++/* This function is called whenever a client should be removed: ++ - maxi_driver is removed (when this module is unloaded) ++ - when an adapter is removed which has a maxi client (and maxi_driver ++ is still present). */ ++static int maxi_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct maxi_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("maxilife: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ kfree(client->data); ++ return 0; ++} ++ ++/* Read byte from specified register (-1 in case of error, value otherwise). */ ++static int maxi_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* Read the byte value for a MaxiLife token (-1 in case of error, value otherwise */ ++static int maxi_read_token(struct i2c_client *client, u16 token) ++{ ++ u8 lowToken, highToken; ++ int error, value; ++ ++ lowToken = LOW(token); ++ highToken = HIGH(token); ++ ++ /* Set mailbox status register to idle state. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, ++ MAXI_STAT_IDLE); ++ if (error < 0) ++ return error; ++ ++ /* Check for mailbox idle state. */ ++ error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); ++ if (error != MAXI_STAT_IDLE) ++ return -1; ++ ++ /* Write the most significant byte of the token we want to read. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, ++ highToken); ++ if (error < 0) ++ return error; ++ ++ /* Write the least significant byte of the token we want to read. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, ++ lowToken); ++ if (error < 0) ++ return error; ++ ++ /* Write the read token opcode to the mailbox. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, ++ MAXI_CMD_READ); ++ if (error < 0) ++ return error; ++ ++ /* Check for transaction completion */ ++ do { ++ error = ++ i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); ++ } while (error == MAXI_STAT_BUSY); ++ if (error != MAXI_STAT_OK) ++ return -1; ++ ++ /* Read the value of the token. */ ++ value = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_DATA); ++ if (value == -1) ++ return -1; ++ ++ /* set mailbox status to idle to complete transaction. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, ++ MAXI_STAT_IDLE); ++ if (error < 0) ++ return error; ++ ++ return value; ++} ++ ++#ifndef NOWRITE ++/* Write byte to specified register (-1 in case of error, 0 otherwise). */ ++static int maxi_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++#endif ++ ++/* Write a set of len byte values to MaxiLife token (-1 in case of error, 0 otherwise). */ ++int maxi_write_token_loop(struct i2c_client *client, u16 token, u8 len, ++ u8 * values) ++{ ++ u8 lowToken, highToken, bCounter; ++ int error; ++ ++ lowToken = LOW(token); ++ highToken = HIGH(token); ++ ++ /* Set mailbox status register to idle state. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, ++ MAXI_STAT_IDLE); ++ if (error < 0) ++ return error; ++ ++ /* Check for mailbox idle state. */ ++ error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); ++ if (error != MAXI_STAT_IDLE) ++ return -1; ++ ++ for (bCounter = 0; (bCounter < len && bCounter < 32); bCounter++) { ++ error = ++ i2c_smbus_write_byte_data(client, ++ (u8) (MAXI_REG_MBX_DATA + ++ bCounter), ++ values[bCounter]); ++ if (error < 0) ++ return error; ++ } ++ ++ /* Write the most significant byte of the token we want to read. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, ++ highToken); ++ if (error < 0) ++ return error; ++ ++ /* Write the least significant byte of the token we want to read. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, ++ lowToken); ++ if (error < 0) ++ return error; ++ ++ /* Write the write token opcode to the mailbox. */ ++ error = ++ i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, ++ MAXI_CMD_WRITE); ++ if (error < 0) ++ return error; ++ ++ /* Check for transaction completion */ ++ do { ++ error = ++ i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); ++ } while (error == MAXI_STAT_BUSY); ++ if (error != MAXI_STAT_OK) ++ return -1; ++ ++ /* set mailbox status to idle to complete transaction. */ ++ return i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, ++ MAXI_STAT_IDLE); ++} ++ ++/* Called when we have found a new MaxiLife. It should set limits, etc. */ ++static void maxi_init_client(struct i2c_client *client) ++{ ++ struct maxi_data *data = client->data; ++ ++ if (data->type == nba) { ++ strcpy(data->lcd[2], " Linux MaxiLife"); ++ maxi_write_token_loop(client, MAXI_TOK_LCD(2), ++ strlen(data->lcd[2]) + 1, ++ data->lcd[2]); ++ } ++} ++ ++static void maxi_update_client(struct i2c_client *client) ++{ ++ struct maxi_data *data = client->data; ++ int i; ++ ++ if (data->type == nba) { ++ printk ++ ("maxi_update_client should never be called by nba\n"); ++ return; ++ } ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("maxilife: Starting MaxiLife update\n"); ++#endif ++ for (i = 0; i < 5; i++) ++ data->temp[i] = ++ maxi_read_value(client, MAXI_REG_TEMP(i)); ++ switch (data->type) { ++ case cristal: ++ data->temp[0] = 0; /* not valid */ ++ data->temp_max[0] = 0; ++ data->temp_hyst[0] = 0; ++ data->temp_max[1] = 110; /* max PCI slot temp */ ++ data->temp_hyst[1] = 100; ++ data->temp_max[2] = 120; /* max BX chipset temp */ ++ data->temp_hyst[2] = 110; ++ data->temp_max[3] = 100; /* max HDD temp */ ++ data->temp_hyst[3] = 90; ++ data->temp_max[4] = 120; /* max CPU temp */ ++ data->temp_hyst[4] = 110; ++ break; ++ ++ case cognac: ++ data->temp_max[0] = 120; /* max CPU1 temp */ ++ data->temp_hyst[0] = 110; ++ data->temp_max[1] = 110; /* max PCI slot temp */ ++ data->temp_hyst[1] = 100; ++ data->temp_max[2] = 120; /* max CPU2 temp */ ++ data->temp_hyst[2] = 110; ++ data->temp_max[3] = 100; /* max HDD temp */ ++ data->temp_hyst[3] = 90; ++ data->temp_max[4] = 120; /* max reference CPU temp */ ++ data->temp_hyst[4] = 110; ++ break; ++ ++ case ashaki: ++ data->temp[0] = 0; /* not valid */ ++ data->temp_max[0] = 0; ++ data->temp_hyst[0] = 0; ++ data->temp_max[1] = 110; /* max PCI slot temp */ ++ data->temp_hyst[1] = 100; ++ data->temp[2] = 0; /* not valid */ ++ data->temp_max[2] = 0; ++ data->temp_hyst[2] = 0; ++ data->temp_max[3] = 100; /* max HDD temp */ ++ data->temp_hyst[3] = 90; ++ data->temp_max[4] = 120; /* max CPU temp */ ++ data->temp_hyst[4] = 110; ++ break; ++ ++ default: ++ printk("maxilife: Unknown MaxiLife chip\n"); ++ } ++ data->temp[5] = 0; /* only used by MaxiLife'99 */ ++ data->temp_max[5] = 0; ++ data->temp_hyst[5] = 0; ++ ++ for (i = 0; i < 3; i++) { ++ data->fan[i] = ++ maxi_read_value(client, MAXI_REG_FAN(i)); ++ data->fan_speed[i] = ++ maxi_read_value(client, MAXI_REG_FAN_SPEED(i)); ++ data->fan_div[i] = 4; ++ if (data->type == ashaki) ++ data->fan_min[i] = ++ maxi_read_value(client, ++ MAXI_REG_FAN_MINAS(i)); ++ else ++ data->fan_min[i] = ++ maxi_read_value(client, ++ MAXI_REG_FAN_MIN(i)); ++ } ++ data->fan[3] = 0xff; /* only used by MaxiLife'99 */ ++ data->fan_speed[3] = 0; ++ data->fan_div[3] = 4; /* avoid possible /0 */ ++ data->fan_min[3] = 0; ++ ++ data->pll = maxi_read_value(client, MAXI_REG_PLL); ++ data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN); ++ data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX); ++ ++ for (i = 0; i < 4; i++) { ++ data->vid[i] = ++ maxi_read_value(client, MAXI_REG_VID(i)); ++ data->vid_min[i] = ++ maxi_read_value(client, MAXI_REG_VID_MIN(i)); ++ data->vid_max[i] = ++ maxi_read_value(client, MAXI_REG_VID_MAX(i)); ++ } ++ switch (data->type) { ++ case cristal: ++ data->vid[3] = 0; /* no voltage cache L2 */ ++ data->vid_min[3] = 0; ++ data->vid_max[3] = 0; ++ break; ++ ++ case cognac: ++ break; ++ ++ case ashaki: ++ data->vid[1] = 0; /* no voltage CPU 2 */ ++ data->vid_min[1] = 0; ++ data->vid_max[1] = 0; ++ data->vid[3] = 0; /* no voltage cache L2 */ ++ data->vid_min[3] = 0; ++ data->vid_max[3] = 0; ++ break; ++ ++ default: ++ printk("maxilife: Unknown MaxiLife chip\n"); ++ } ++ data->vid[4] = 0; /* only used by MaxliLife'99 */ ++ data->vid_min[4] = 0; ++ data->vid_max[4] = 0; ++ ++ data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) + ++ (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++void maxi99_update_client(struct i2c_client *client, ++ enum sensor_type sensor, int which) ++{ ++ static unsigned long last_updated[6][6]; /* sensor, which */ ++ struct maxi_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ /*maxi_write_token_loop(client, MAXI_TOK_LCD_LINE3, 13, "Linux 2.2.13"); */ ++ ++ if ((jiffies - last_updated[sensor][which] > 2 * HZ) || ++ (jiffies < last_updated[sensor][which] ++ || !last_updated[sensor][which])) { ++ ++ int tmp, i; ++ ++ switch (sensor) { ++ case fan: ++ for (i = 0; i < 4; i++) { ++ if (i == which) { ++ tmp = ++ maxi_read_token(client, ++ MAXI_TOK_FAN ++ (i)); ++ data->fan[i] = ++ maxi_read_token(client, ++ MAXI_TOK_FAN ++ (i)); ++ data->fan_speed[i] = ++ maxi_read_token(client, ++ MAXI_TOK_MAX ++ (MAXI_TOK_FAN ++ (i))); ++ data->fan_div[i] = 1; ++ data->fan_min[i] = 0; ++ } ++ } ++ break; ++ ++ case temp: ++ for (i = 0; i < 6; i++) { ++ if (i == which) { ++ data->temp[i] = ++ maxi_read_token(client, ++ MAXI_TOK_TEMP ++ (i)); ++ data->temp_max[i] = ++ maxi_read_token(client, ++ MAXI_TOK_MAX ++ (MAXI_TOK_TEMP ++ (i))); ++ data->temp_hyst[i] = ++ data->temp_max[i] - 5; ++ } ++ } ++ break; ++ ++ case vid: ++ for (i = 0; i < 5; i++) { ++ if (i == which) { ++ data->vid[i] = ++ maxi_read_token(client, ++ MAXI_TOK_VID ++ (i)); ++ data->vid_min[i] = ++ maxi_read_token(client, ++ MAXI_TOK_MIN ++ (MAXI_TOK_VID ++ (i))); ++ data->vid_max[i] = ++ maxi_read_token(client, ++ MAXI_TOK_MAX ++ (MAXI_TOK_VID ++ (i))); ++ } ++ } ++ break; ++ ++ case pll: ++ data->pll = 0; ++ data->pll_min = 0; ++ data->pll_max = 0; ++ break; ++ ++ case alarm: ++ data->alarms = ++ (maxi_read_token(client, MAXI_TOK_ALARM_EVENT) ++ << 8); ++ if (data->alarms) ++ data->alarms += ++ data->alarms == ++ (1 << 8) ? maxi_read_token(client, ++ MAXI_TOK_ALARM_FAN) ++ : data->alarms == ++ (2 << 8) ? maxi_read_token(client, ++ MAXI_TOK_ALARM_VID) ++ : data->alarms == ++ (4 << 8) ? maxi_read_token(client, ++ MAXI_TOK_ALARM_TEMP) ++ : data->alarms == ++ (8 << 8) ? maxi_read_token(client, ++ MAXI_TOK_ALARM_FAN) ++ : 0; ++ break; ++ ++ default: ++ printk("maxilife: Unknown sensor type\n"); ++ } ++ ++ last_updated[sensor][which] = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the data ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void maxi_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ if (data->type == nba) { ++ maxi99_fan(client, operation, ctl_name, nrels_mag, ++ results); ++ return; ++ } ++ ++ nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1]); ++ results[1] = data->fan_div[nr - 1]; ++ results[2] = FAN_FROM_REG(data->fan[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++#ifndef NOWRITE ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0]); ++ maxi_write_value(client, MAXI_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++#endif ++ } ++} ++ ++void maxi99_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi99_update_client(client, fan, nr - 1); ++ results[0] = FAN99_FROM_REG(data->fan_min[nr - 1]); /* min rpm */ ++ results[1] = data->fan_div[nr - 1]; /* divisor */ ++ results[2] = FAN99_FROM_REG(data->fan[nr - 1]); /* rpm */ ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++#ifndef NOWRITE ++ /* still to do */ ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0]); ++ maxi_write_value(client, MAXI_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++#endif ++ } ++} ++ ++void maxi_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ if (data->type == nba) { ++ maxi99_temp(client, operation, ctl_name, nrels_mag, ++ results); ++ return; ++ } ++ ++ nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_max[nr - 1]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr - 1]); ++ results[2] = TEMP_FROM_REG(data->temp[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* temperature range can not be changed */ ++ } ++} ++ ++void maxi99_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi99_update_client(client, temp, nr - 1); ++ results[0] = TEMP99_FROM_REG(data->temp_max[nr - 1]); ++ results[1] = TEMP99_FROM_REG(data->temp_hyst[nr - 1]); ++ results[2] = TEMP99_FROM_REG(data->temp[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* temperature range can not be changed */ ++ } ++} ++ ++void maxi_pll(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ if (data->type == nba) ++ maxi99_update_client(client, pll, 0); ++ else ++ maxi_update_client(client); ++ results[0] = PLL_FROM_REG(data->pll_min); ++ results[1] = PLL_FROM_REG(data->pll_max); ++ results[2] = PLL_FROM_REG(data->pll); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++#ifndef NOWRITE ++ if (*nrels_mag >= 1) { ++ data->pll_min = PLL_TO_REG(results[0]); ++ maxi_write_value(client, MAXI_REG_PLL_MIN, ++ data->pll_min); ++ } ++ if (*nrels_mag >= 2) { ++ data->pll_max = PLL_TO_REG(results[1]); ++ maxi_write_value(client, MAXI_REG_PLL_MAX, ++ data->pll_max); ++ } ++#endif ++ } ++} ++ ++void maxi_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ if (data->type == nba) { ++ maxi99_vid(client, operation, ctl_name, nrels_mag, ++ results); ++ return; ++ } ++ ++ nr = ctl_name - MAXI_SYSCTL_VID1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 4; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi_update_client(client); ++ results[0] = VID_FROM_REG(data->vid_min[nr - 1]); ++ results[1] = VID_FROM_REG(data->vid_max[nr - 1]); ++ results[2] = VID_FROM_REG(data->vid[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++#ifndef NOWRITE ++ if (*nrels_mag >= 1) { ++ data->vid_min[nr - 1] = VID_TO_REG(results[0]); ++ maxi_write_value(client, MAXI_REG_VID_MIN(nr), ++ data->vid_min[nr - 1]); ++ } ++ if (*nrels_mag >= 2) { ++ data->vid_max[nr - 1] = VID_TO_REG(results[1]); ++ maxi_write_value(client, MAXI_REG_VID_MAX(nr), ++ data->vid_max[nr - 1]); ++ } ++#endif ++ } ++} ++ ++void maxi99_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ int nr = ctl_name - MAXI_SYSCTL_VID1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 4; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ maxi99_update_client(client, vid, nr - 1); ++ results[0] = VID99_FROM_REG(nr, data->vid_min[nr - 1]); ++ results[1] = VID99_FROM_REG(nr, data->vid_max[nr - 1]); ++ results[2] = VID99_FROM_REG(nr, data->vid[nr - 1]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++#ifndef NOWRITE ++ /* still to do */ ++ if (*nrels_mag >= 1) { ++ data->vid_min[nr - 1] = VID_TO_REG(results[0]); ++ maxi_write_value(client, MAXI_REG_VID_MIN(nr), ++ data->vid_min[nr - 1]); ++ } ++ if (*nrels_mag >= 2) { ++ data->vid_max[nr - 1] = VID_TO_REG(results[1]); ++ maxi_write_value(client, MAXI_REG_VID_MAX(nr), ++ data->vid_max[nr - 1]); ++ } ++#endif ++ } ++} ++ ++void maxi_lcd(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ /* Allows writing and reading from LCD display */ ++ ++ struct maxi_data *data = client->data; ++ int nr; ++ ++ if (data->type != nba) ++ return; ++ ++ nr = ctl_name - MAXI_SYSCTL_LCD1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = *((long *) &data->lcd[nr - 1][0]); ++ results[1] = *((long *) &data->lcd[nr - 1][4]); ++ results[2] = *((long *) &data->lcd[nr - 1][8]); ++ results[3] = *((long *) &data->lcd[nr - 1][12]); ++ *nrels_mag = 4; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* ++ Writing a string to line 3 of the LCD can be done like: ++ echo -n "Linux MaxiLife" | od -A n -l > \ ++ /proc/sys/dev/sensors/maxilife-nba-i2c-0-14/lcd3 ++ */ ++ if (*nrels_mag >= 1) ++ *((long *) &data->lcd[nr - 1][0]) = results[0]; ++ if (*nrels_mag >= 2) ++ *((long *) &data->lcd[nr - 1][4]) = results[1]; ++ if (*nrels_mag >= 3) ++ *((long *) &data->lcd[nr - 1][8]) = results[2]; ++ if (*nrels_mag >= 4) ++ *((long *) &data->lcd[nr - 1][12]) = results[3]; ++ maxi_write_token_loop(client, MAXI_TOK_LCD(nr - 1), ++ strlen(data->lcd[nr - 1]) + 1, ++ data->lcd[nr - 1]); ++#if 0 ++ if (*nrels_mag >= 1) ++ printk("nr=%d, result[0] = %.4s\n", nr, ++ (char *) &results[0]); ++ if (*nrels_mag >= 2) ++ printk("nr=%d, result[1] = %.4s\n", nr, ++ (char *) &results[1]); ++ if (*nrels_mag >= 3) ++ printk("nr=%d, result[2] = %.4s\n", nr, ++ (char *) &results[2]); ++ if (*nrels_mag >= 4) ++ printk("nr=%d, result[3] = %.4s\n", nr, ++ (char *) &results[3]); ++#endif ++ } ++ ++} ++ ++void maxi_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct maxi_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ if (data->type == nba) ++ maxi99_update_client(client, alarm, 0); ++ else ++ maxi_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++static int __init sm_maxilife_init(void) ++{ ++ printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str, ++ LM_VERSION, LM_DATE); ++ return i2c_add_driver(&maxi_driver); ++} ++ ++static void __exit sm_maxilife_exit(void) ++{ ++ i2c_del_driver(&maxi_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Fons Rademakers "); ++MODULE_DESCRIPTION("HP MaxiLife driver"); ++MODULE_PARM(maxi_version, "i"); ++MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version"); ++ ++module_init(sm_maxilife_init); ++module_exit(sm_maxilife_exit); +--- linux-old/drivers/sensors/mtp008.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/mtp008.c Mon Dec 13 20:18:51 2004 +@@ -0,0 +1,1103 @@ ++/* ++ mtp008.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (C) 2001, 2004 Kris Van Hees ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = {SENSORS_I2C_END}; ++static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END}; ++static unsigned int normal_isa[] = {SENSORS_ISA_END}; ++static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(mtp008); ++ ++/* The MTP008 registers */ ++/* in0 .. in6 */ ++#define MTP008_REG_IN(nr) (0x20 + (nr)) ++#define MTP008_REG_IN_MAX(nr) (0x2b + (nr) * 2) ++#define MTP008_REG_IN_MIN(nr) (0x2c + (nr) * 2) ++ ++/* temp1 */ ++#define MTP008_REG_TEMP 0x27 ++#define MTP008_REG_TEMP_MAX 0x39 ++#define MTP008_REG_TEMP_MIN 0x3a ++ ++/* fan1 .. fan3 */ ++#define MTP008_REG_FAN(nr) (0x27 + (nr)) ++#define MTP008_REG_FAN_MIN(nr) (0x3a + (nr)) ++ ++#define MTP008_REG_CONFIG 0x40 ++#define MTP008_REG_INT_STAT1 0x41 ++#define MTP008_REG_INT_STAT2 0x42 ++ ++#define MTP008_REG_SMI_MASK1 0x43 ++#define MTP008_REG_SMI_MASK2 0x44 ++ ++#define MTP008_REG_NMI_MASK1 0x45 ++#define MTP008_REG_NMI_MASK2 0x46 ++ ++#define MTP008_REG_VID_FANDIV 0x47 ++ ++#define MTP008_REG_I2C_ADDR 0x48 ++ ++#define MTP008_REG_RESET_VID4 0x49 ++ ++#define MTP008_REG_OVT_PROP 0x50 ++ ++#define MTP008_REG_BEEP_CTRL1 0x51 ++#define MTP008_REG_BEEP_CTRL2 0x52 ++ ++/* pwm1 .. pwm3 nr range 1-3 */ ++#define MTP008_REG_PWM_CTRL(nr) (0x52 + (nr)) ++ ++#define MTP008_REG_PIN_CTRL1 0x56 ++#define MTP008_REG_PIN_CTRL2 0x57 ++ ++#define MTP008_REG_CHIPID 0x58 ++ ++/* ++ * Pin control register configuration constants. ++ */ ++#define MTP008_CFG_VT1_PII 0x08 ++#define MTP008_CFG_VT2_AIN 0x00 ++#define MTP008_CFG_VT2_VT 0x03 ++#define MTP008_CFG_VT2_PII 0x04 ++#define MTP008_CFG_VT2_MASK 0x06 ++#define MTP008_CFG_VT3_VT 0x01 ++ ++/* sensor pin types */ ++#define VOLTAGE 1 ++#define THERMISTOR 2 ++#define PIIDIODE 3 ++ ++/* ++ * Conversion routines and macros. Limit checking is only done on ++ * the TO_REG variants. ++ * ++ * Note that IN values are expressed as 100 times the actual voltage to avoid ++ * having to use floating point values. As such, IN values are between 0 and ++ * 409 (0V to 4.096V). ++ */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++/* ++ * The fan cotation count (as stored in the register) is calculated using the ++ * following formula: ++ * count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div) ++ * and the rpm is therefore: ++ * rpm = 1350000 / (count * div) ++ */ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ ++ return SENSORS_LIMIT( ++ (1350000 + rpm * div / 2) / (rpm * div), ++ 1, 254 ++ ); ++} ++ ++#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 \ ++ : (val) == 255 ? 0 \ ++ : 1350000 / \ ++ ((val) * (div)) \ ++ ) ++ ++/* ++ * Temperatures are stored as two's complement values of the Celsius value. It ++ * actually uses 10 times the Celsius value to avoid using floating point ++ * values. ++ */ ++#define TEMP_TO_REG(val) ( \ ++ (val) < 0 \ ++ ? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \ ++ : SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \ ++ ) ++#define TEMP_FROM_REG(val) ( \ ++ ( \ ++ (val) > 0x80 ? (val) - 0x100 \ ++ : (val) \ ++ ) * 10 \ ++ ) ++ ++/* ++ * VCORE voltage: ++ * 0x00 to 0x0f = 2.05 to 1.30 (0.05 per unit) ++ * 0x10 to 0x1e = 3.50 to 2.10 (0.10 per unit) ++ * 0x1f = No CPU ++ */ ++#define VID_FROM_REG(val) ((val) == 0x1f \ ++ ? 0 \ ++ : (val) < 0x10 ? 205 - (val) * 5 \ ++ : 510 - (val) * 10) ++ ++/* ++ * Fan divider. ++ */ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val) == 8 ? 3 \ ++ : (val) == 4 ? 2 \ ++ : (val) == 2 ? 1 \ ++ : 0) ++ ++/* ++ * Alarms (interrupt status). ++ */ ++#define ALARMS_FROM_REG(val) (val) ++ ++/* ++ * Beep controls. ++ */ ++#define BEEPS_FROM_REG(val) (val) ++#define BEEPS_TO_REG(val) (val) ++ ++/* ++ * PWM control. nr range 1 to 3 ++ */ ++#define PWM_FROM_REG(val) (val) ++#define PWM_TO_REG(val) (val) ++#define PWMENABLE_FROM_REG(nr, val) (((val) >> ((nr) + 3)) & 1) ++ ++/* ++ * For each registered MTP008, we need to keep some data in memory. The ++ * structure itself is dynamically allocated, at the same time when a new ++ * mtp008 client is allocated. ++ */ ++struct mtp008_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u8 temp; /* Register value */ ++ u8 temp_max; /* Register value */ ++ u8 temp_min; /* Register value */ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u8 vid; /* Register encoding */ ++ u8 fan_div[3]; /* Register encoding */ ++ u16 alarms; /* Register encoding */ ++ u16 beeps; /* Register encoding */ ++ u8 pwm[4]; /* Register value */ ++ u8 sens[3]; /* 1 = Analog input, ++ 2 = Thermistor, ++ 3 = PII/Celeron diode */ ++ u8 pwmenable; /* Register 0x57 value */ ++}; ++ ++static int mtp008_attach_adapter(struct i2c_adapter *adapter); ++static int mtp008_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int mtp008_detach_client(struct i2c_client *client); ++ ++static int mtp008_read_value(struct i2c_client *client, u8 register); ++static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value); ++static void mtp008_update_client(struct i2c_client *client); ++static void mtp008_init_client(struct i2c_client *client); ++ ++static void mtp008_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_beep(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_sens(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void mtp008_getsensortype(struct mtp008_data *data, u8 inp); ++ ++static int mtp008_id = 0; ++ ++static struct i2c_driver mtp008_driver = ++{ ++ .owner = THIS_MODULE, ++ .name = "MTP008 sensor driver", ++ .id = I2C_DRIVERID_MTP008, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = mtp008_attach_adapter, ++ .detach_client = mtp008_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define MTP008_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define MTP008_SYSCTL_IN1 1001 ++#define MTP008_SYSCTL_IN2 1002 ++#define MTP008_SYSCTL_IN3 1003 ++#define MTP008_SYSCTL_IN4 1004 ++#define MTP008_SYSCTL_IN5 1005 ++#define MTP008_SYSCTL_IN6 1006 ++#define MTP008_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define MTP008_SYSCTL_FAN2 1102 ++#define MTP008_SYSCTL_FAN3 1103 ++#define MTP008_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define MTP008_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ ++#define MTP008_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ ++#define MTP008_SYSCTL_VID 1300 /* Volts * 100 */ ++#define MTP008_SYSCTL_PWM1 1401 ++#define MTP008_SYSCTL_PWM2 1402 ++#define MTP008_SYSCTL_PWM3 1403 ++#define MTP008_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */ ++#define MTP008_SYSCTL_SENS2 1502 ++#define MTP008_SYSCTL_SENS3 1503 ++#define MTP008_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define MTP008_SYSCTL_ALARMS 2001 /* bitvector */ ++#define MTP008_SYSCTL_BEEP 2002 /* bitvector */ ++ ++#define MTP008_ALARM_IN0 0x0001 ++#define MTP008_ALARM_IN1 0x0002 ++#define MTP008_ALARM_IN2 0x0004 ++#define MTP008_ALARM_IN3 0x0008 ++#define MTP008_ALARM_IN4 0x0100 ++#define MTP008_ALARM_IN5 0x0200 ++#define MTP008_ALARM_IN6 0x0400 ++#define MTP008_ALARM_FAN1 0x0040 ++#define MTP008_ALARM_FAN2 0x0080 ++#define MTP008_ALARM_FAN3 0x0800 ++#define MTP008_ALARM_TEMP1 0x0010 ++#define MTP008_ALARM_TEMP2 0x0100 ++#define MTP008_ALARM_TEMP3 0x0200 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* The /proc/sys entries */ ++/* These files are created for each detected chip. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++ ++static ctl_table mtp008_dir_table_template[] = ++{ ++ {MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, ++ {MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, ++ {MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, ++ {MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, ++ {MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp}, ++ {MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, ++ {MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, ++ {MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid}, ++ {MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div}, ++ {MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms}, ++ {MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep}, ++ {MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, ++ {MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, ++ {MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, ++ {MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, ++ {MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, ++ {MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, ++ {0} ++}; ++ ++/* This function is called when: ++ * mtp008_driver is inserted (when this module is loaded), for each available ++ * adapter when a new adapter is inserted (and mtp008_driver is still present) ++ */ ++static int mtp008_attach_adapter(struct i2c_adapter *adapter) ++{ ++ struct i2c_client_address_data mtp008_addr_data; ++ ++ mtp008_addr_data.normal_i2c = addr_data.normal_i2c; ++ mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range; ++ mtp008_addr_data.probe = addr_data.probe; ++ mtp008_addr_data.probe_range = addr_data.probe_range; ++ mtp008_addr_data.ignore = addr_data.ignore; ++ mtp008_addr_data.ignore_range = addr_data.ignore_range; ++ mtp008_addr_data.force = addr_data.forces->force; ++ ++ return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect); ++} ++ ++int mtp008_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ const char *type_name = ""; ++ const char *client_name = ""; ++ int is_isa, err, sysid; ++ struct i2c_client *new_client; ++ struct mtp008_data *data; ++ ++ err = 0; ++ ++ is_isa = i2c_is_isa_adapter(adapter); ++ if (is_isa || ++ !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* ++ * We presume we have a valid client. We now create the client ++ * structure, even though we cannot fill it completely yet. But it ++ * allows us to use mtp008_(read|write)_value(). ++ */ ++ if (!(data = kmalloc(sizeof(struct mtp008_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &mtp008_driver; ++ new_client->flags = 0; ++ ++ /* ++ * Remaining detection. ++ */ ++ if (kind < 0) { ++ if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac) ++ goto ERROR1; ++ } ++ /* ++ * Fill in the remaining client fields and put it into the global list. ++ */ ++ type_name = "mtp008"; ++ client_name = "MTP008 chip"; ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = mtp008_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* ++ * Tell the I2C layer that a new client has arrived. ++ */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* ++ * Register a new directory entry with the sensors module. ++ */ ++ if ((sysid = i2c_register_entry(new_client, type_name, ++ mtp008_dir_table_template)) < 0) { ++ err = sysid; ++ goto ERROR2; ++ } ++ data->sysctl_id = sysid; ++ ++ /* ++ * Initialize the MTP008 chip. ++ */ ++ mtp008_init_client(new_client); ++ ++ return 0; ++ ++ /* ++ * Error handling. Bad programming practise but very code efficient. ++ */ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ ++ ERROR0: ++ return err; ++} ++ ++static int mtp008_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry( ++ ((struct mtp008_data *) (client->data))->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk("mtp008.o: Deregistration failed, " ++ "client not detached.\n"); ++ return err; ++ } ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++static int mtp008_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg) & 0xff; ++} ++ ++static int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Called when we have found a new MTP008. It should set limits, etc. */ ++static void mtp008_init_client(struct i2c_client *client) ++{ ++ u8 save1, save2; ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ /* ++ * Initialize the Myson MTP008 hardware monitoring chip. ++ * Save the pin settings that the BIOS hopefully set. ++ */ ++ save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); ++ save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); ++ mtp008_write_value(client, MTP008_REG_CONFIG, ++ (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80); ++ mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1); ++ mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2); ++ ++ mtp008_getsensortype(data, save2); ++ ++ ++ /* ++ * Start monitoring. ++ */ ++ mtp008_write_value( ++ client, MTP008_REG_CONFIG, ++ (mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01 ++ ); ++} ++ ++static void mtp008_update_client(struct i2c_client *client) ++{ ++ int i; ++ u8 inp; ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++#ifdef DEBUG ++ printk("Starting MTP008 update\n"); ++#endif ++ ++ /* ++ * Read in the analog inputs. We're reading AIN4 and AIN5 as ++ * regular analog inputs, even though they may have been ++ * configured as temperature readings instead. Interpretation ++ * of these values is done elsewhere. ++ */ ++ for (i = 0; i < 7; i++) { ++ data->in[i] = ++ mtp008_read_value(client, MTP008_REG_IN(i)); ++ data->in_max[i] = ++ mtp008_read_value(client, MTP008_REG_IN_MAX(i)); ++ data->in_min[i] = ++ mtp008_read_value(client, MTP008_REG_IN_MIN(i)); ++ } ++ ++ /* ++ * Read the temperature sensor. ++ */ ++ data->temp = mtp008_read_value(client, MTP008_REG_TEMP); ++ data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX); ++ data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN); ++ ++ /* ++ * Read the first 2 fan dividers and the VID setting. Read the ++ * third fan divider from a different register. ++ */ ++ inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV); ++ data->vid = inp & 0x0f; ++ data->vid |= (mtp008_read_value(client, ++ MTP008_REG_RESET_VID4) & 0x01) << 4; ++ ++ data->fan_div[0] = (inp >> 4) & 0x03; ++ data->fan_div[1] = inp >> 6; ++ data->fan_div[2] = ++ mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6; ++ ++ /* ++ * Read the interrupt status registers. ++ */ ++ data->alarms = ++ (mtp008_read_value(client, ++ MTP008_REG_INT_STAT1) & 0xdf) | ++ (mtp008_read_value(client, ++ MTP008_REG_INT_STAT2) & 0x0f) << 8; ++ ++ /* ++ * Read the beep control registers. ++ */ ++ data->beeps = ++ (mtp008_read_value(client, ++ MTP008_REG_BEEP_CTRL1) & 0xdf) | ++ (mtp008_read_value(client, ++ MTP008_REG_BEEP_CTRL2) & 0x8f) << 8; ++ ++ /* ++ * Read the sensor configuration. ++ */ ++ inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); ++ mtp008_getsensortype(data, inp); ++ data->pwmenable = inp; ++ ++ /* ++ * Read the PWM registers if enabled. ++ */ ++ for (i = 1; i <= 3; i++) ++ { ++ if(PWMENABLE_FROM_REG(i, inp)) ++ data->pwm[i-1] = mtp008_read_value(client, ++ MTP008_REG_PWM_CTRL(i)); ++ else ++ data->pwm[i-1] = 255; ++ } ++ ++ /* ++ * Read the fan sensors. Skip 3 if PWM1 enabled. ++ */ ++ for (i = 1; i <= 3; i++) { ++ if(i == 3 && PWMENABLE_FROM_REG(1, inp)) { ++ data->fan[2] = 0; ++ data->fan_min[2] = 0; ++ } else { ++ data->fan[i-1] = mtp008_read_value(client, ++ MTP008_REG_FAN(i)); ++ data->fan_min[i-1] = mtp008_read_value(client, ++ MTP008_REG_FAN_MIN(i)); ++ } ++ } ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ up(&data->update_lock); ++} ++ ++static void mtp008_getsensortype(struct mtp008_data *data, u8 inp) ++{ ++ inp &= 0x0f; ++ data->sens[0] = (inp >> 3) + 2; /* 2 or 3 */ ++ data->sens[1] = ((inp >> 1) & 0x03) + 1; /* 1, 2 or 3 */ ++ data->sens[2] = (inp & 0x01) + 1; /* 1 or 2 */ ++} ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++void mtp008_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int nr; ++ struct mtp008_data *data; ++ ++ nr = ctl_name - MTP008_SYSCTL_IN0; ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 2; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ } else { ++ results[0] = 0; ++ results[1] = 0; ++ results[2] = 0; ++ } ++ ++ *nrels_mag = 3; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ mtp008_write_value(client, MTP008_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ mtp008_write_value(client, MTP008_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++ } ++} ++ ++void mtp008_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int nr; ++ struct mtp008_data *data; ++ ++ nr = ctl_name - MTP008_SYSCTL_FAN1; ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = FAN_FROM_REG(data->fan_min[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ results[1] = FAN_FROM_REG(data->fan[nr], ++ DIV_FROM_REG(data->fan_div[nr])); ++ ++ *nrels_mag = 2; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr] = ++ FAN_TO_REG(results[0], ++ DIV_FROM_REG(data->fan_div[nr])); ++ mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1), ++ data->fan_min[nr]); ++ } ++ } ++} ++ ++void mtp008_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 1; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = TEMP_FROM_REG(data->temp_max); ++ results[1] = TEMP_FROM_REG(data->temp_min); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 1) { ++ data->temp_max = TEMP_TO_REG(results[0]); ++ mtp008_write_value(client, MTP008_REG_TEMP_MAX, ++ data->temp_max); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_min = TEMP_TO_REG(results[1]); ++ mtp008_write_value(client, MTP008_REG_TEMP_MIN, ++ data->temp_min); ++ } ++ } ++} ++ ++void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int nr; ++ struct mtp008_data *data; ++ ++ nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1; /* AIN4 or AIN5 */ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 1; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ if(data->sens[nr - 3] != VOLTAGE) { ++ results[0] = TEMP_FROM_REG(data->in_max[nr]); ++ results[1] = TEMP_FROM_REG(data->in_min[nr]); ++ results[2] = TEMP_FROM_REG(data->in[nr]); ++ } else { ++ results[0] = 0; ++ results[1] = 0; ++ results[2] = 0; ++ } ++ *nrels_mag = 3; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if(data->sens[nr - 3] != VOLTAGE) { ++ if (*nrels_mag >= 1) { ++ data->in_max[nr] = TEMP_TO_REG(results[0]); ++ mtp008_write_value(client, ++ MTP008_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_min[nr] = TEMP_TO_REG(results[1]); ++ mtp008_write_value(client, ++ MTP008_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ } ++ } ++} ++ ++void mtp008_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 2; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = VID_FROM_REG(data->vid); ++ ++ *nrels_mag = 1; ++ } ++} ++ ++void mtp008_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct mtp008_data *data; ++ u8 val; ++ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ results[2] = DIV_FROM_REG(data->fan_div[2]); ++ ++ *nrels_mag = 3; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 3) { ++ data->fan_div[2] = DIV_TO_REG(results[2]); ++ val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); ++ val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6; ++ mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val); ++ } ++ if (*nrels_mag >= 1) { ++ val = mtp008_read_value(client, MTP008_REG_VID_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ val = (val & 0x3f) | ++ (data->fan_div[1] & 0x03) << 6; ++ } ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4; ++ mtp008_write_value(client, MTP008_REG_VID_FANDIV, val); ++ } ++ } ++} ++ ++void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ ++ *nrels_mag = 1; ++ } ++} ++ ++void mtp008_beep(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct mtp008_data *data; ++ ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = BEEPS_FROM_REG(data->beeps); ++ ++ *nrels_mag = 1; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 1) { ++ data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f; ++ ++ mtp008_write_value(client, MTP008_REG_BEEP_CTRL1, ++ data->beeps & 0xff); ++ mtp008_write_value(client, MTP008_REG_BEEP_CTRL2, ++ data->beeps >> 8); ++ } ++ } ++} ++ ++void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ int nr; ++ struct mtp008_data *data; ++ ++ nr = ctl_name - MTP008_SYSCTL_PWM1; ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ mtp008_update_client(client); ++ ++ results[0] = PWM_FROM_REG(data->pwm[nr]); ++ results[1] = PWMENABLE_FROM_REG(nr + 1, data->pwmenable); ++ *nrels_mag = 2; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 1) { ++ if (*nrels_mag >= 2) { ++ if(results[1]) ++ data->pwmenable |= 0x10 << nr; ++ else ++ data->pwmenable &= ~(0x10 << nr); ++ mtp008_write_value(client, MTP008_REG_PIN_CTRL2, ++ data->pwmenable); ++ } ++ data->pwm[nr] = PWM_TO_REG(results[0]); ++ mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr), ++ data->pwm[nr]); ++ } ++ } ++} ++ ++void mtp008_sens(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ const char *opts = ""; ++ int nr; ++ u8 tmp; ++ struct mtp008_data *data; ++ ++ nr = 1 + ctl_name - MTP008_SYSCTL_SENS1; ++ data = client->data; ++ ++ switch (operation) { ++ case SENSORS_PROC_REAL_INFO: ++ *nrels_mag = 0; ++ ++ break; ++ case SENSORS_PROC_REAL_READ: ++ results[0] = data->sens[nr - 1]; ++ ++ *nrels_mag = 1; ++ ++ break; ++ case SENSORS_PROC_REAL_WRITE: ++ if (*nrels_mag >= 1) { ++ tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); ++ ++ switch (nr) { ++ case 1: /* VT or PII */ ++ opts = "2 or 3"; ++ ++ switch (results[0]) { ++ case THERMISTOR: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp & ~MTP008_CFG_VT1_PII); ++ data->sens[0] = 2; ++ return; ++ case PIIDIODE: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp | MTP008_CFG_VT1_PII); ++ data->sens[0] = 3; ++ return; ++ } ++ ++ break; ++ case 2: /* AIN, VT or PII */ ++ tmp &= ~MTP008_CFG_VT2_MASK; ++ opts = "1, 2 or 3"; ++ ++ switch (results[0]) { ++ case VOLTAGE: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp | MTP008_CFG_VT2_AIN); ++ data->sens[1] = 1; ++ return; ++ case THERMISTOR: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp | MTP008_CFG_VT2_VT); ++ data->sens[1] = 2; ++ return; ++ case PIIDIODE: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp | MTP008_CFG_VT2_PII); ++ data->sens[1] = 3; ++ return; ++ } ++ ++ break; ++ case 3: /* AIN or VT */ ++ opts = "1 or 2"; ++ ++ switch (results[0]) { ++ case VOLTAGE: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp & ~MTP008_CFG_VT3_VT); ++ data->sens[2] = 1; ++ return; ++ case THERMISTOR: ++ mtp008_write_value( ++ client, MTP008_REG_PIN_CTRL2, ++ tmp | MTP008_CFG_VT3_VT); ++ data->sens[2] = 2; ++ return; ++ } ++ ++ break; ++ } ++ ++ printk("mtp008.o: Invalid sensor type %ld " ++ "for sensor %d; must be %s.\n", ++ results[0], nr, opts); ++ } ++ } ++} ++ ++static int __init sm_mtp008_init(void) ++{ ++ printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&mtp008_driver); ++} ++ ++static void __exit sm_mtp008_exit(void) ++{ ++ i2c_del_driver(&mtp008_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "and Kris Van Hees "); ++MODULE_DESCRIPTION("MTP008 driver"); ++ ++module_init(sm_mtp008_init); ++module_exit(sm_mtp008_exit); +--- linux-old/drivers/sensors/pc87360.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/pc87360.c Mon Dec 13 20:18:51 2004 +@@ -0,0 +1,1357 @@ ++/* ++ * pc87360.c - Part of lm_sensors, Linux kernel modules ++ * for hardware monitoring ++ * Copyright (C) 2004 Jean Delvare ++ * ++ * Copied from smsc47m1.c: ++ * Copyright (C) 2002 Mark D. Studebaker ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Supports the following chips: ++ * ++ * Chip #vin #fan #pwm #temp devid ++ * PC87360 - 2 2 - 0xE1 ++ * PC87363 - 2 2 - 0xE8 ++ * PC87364 - 3 3 - 0xE4 ++ * PC87365 11 3 3 2 0xE5 ++ * PC87366 11 3 3 3-4 0xE9 ++ * ++ * This driver assumes that no more than one chip is present, and the ++ * standard Super-I/O address is used (0x2E/0x2F). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++static struct i2c_force_data forces[] = {{NULL}}; ++static u8 devid; ++static unsigned int extra_isa[] = { 0x0000, 0x0000, 0x0000 }; ++static u8 confreg[4]; ++ ++enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 }; ++static struct i2c_address_data addr_data = { ++ .normal_i2c = normal_i2c, ++ .normal_i2c_range = normal_i2c_range, ++ .normal_isa = normal_isa, ++ .normal_isa_range = normal_isa_range, ++ .probe = normal_i2c, /* cheat */ ++ .probe_range = normal_i2c_range, /* cheat */ ++ .ignore = normal_i2c, /* cheat */ ++ .ignore_range = normal_i2c_range, /* cheat */ ++ .forces = forces, ++}; ++ ++static int init = 1; ++MODULE_PARM(init, "i"); ++MODULE_PARM_DESC(init, ++ "Chip initialization level:\n" ++ " 0: None\n" ++ "*1: Forcibly enable internal voltage and temperature channels, except in9\n" ++ " 2: Forcibly enable all voltage and temperature channels, except in9\n" ++ " 3: Forcibly enable all voltage and temperature channels, including in9"); ++ ++/* ++ * Super-I/O registers and operations ++ */ ++ ++#define REG 0x2e /* The register to read/write */ ++#define VAL 0x2f /* The value to read/write */ ++ ++#define DEV 0x07 /* Register: Logical device select */ ++#define DEVID 0x20 /* Register: Device ID */ ++#define ACT 0x30 /* Register: Device activation */ ++#define BASE 0x60 /* Register: Base address */ ++ ++#define FSCM 0x09 /* Logical device: fans */ ++#define VLM 0x0d /* Logical device: voltages */ ++#define TMS 0x0e /* Logical device: temperatures */ ++static const u8 logdev[3] = { FSCM, VLM, TMS }; ++ ++#define LD_FAN 0 ++#define LD_IN 1 ++#define LD_TEMP 2 ++ ++static inline void superio_outb(int reg, int val) ++{ ++ outb(reg, REG); ++ outb(val, VAL); ++} ++ ++static inline int superio_inb(int reg) ++{ ++ outb(reg, REG); ++ return inb(VAL); ++} ++ ++static inline void superio_exit(void) ++{ ++ outb(0x02, REG); ++ outb(0x02, VAL); ++} ++ ++/* ++ * Logical devices ++ */ ++ ++#define PC87360_EXTENT 0x10 ++#define PC87365_REG_BANK 0x09 ++#define NO_BANK 0xff ++ ++/* ++ * Fan registers and conversions ++ */ ++ ++/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */ ++#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr)) ++#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr)) ++#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr)) ++#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) ++#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) ++ ++#define FAN_FROM_REG(val,div) ((val)==0?0: \ ++ 480000/((val)*(div))) ++#define FAN_TO_REG(val,div) ((val)<=100?0: \ ++ 480000/((val)*(div))) ++#define FAN_DIV_FROM_REG(val) (1 << ((val >> 5) & 0x03)) ++#define FAN_DIV_TO_REG(val) ((val)==8?0x60:(val)==4?0x40: \ ++ (val)==1?0x00:0x20) ++#define FAN_STATUS_FROM_REG(val) ((val) & 0x07) ++ ++#define FAN_CONFIG_MONITOR(val,nr) (((val) >> (2 + nr * 3)) & 1) ++#define FAN_CONFIG_CONTROL(val,nr) (((val) >> (3 + nr * 3)) & 1) ++#define FAN_CONFIG_INVERT(val,nr) (((val) >> (4 + nr * 3)) & 1) ++ ++#define PWM_FROM_REG(val,inv) ((inv) ? 255 - (val) : (val)) ++static inline u8 PWM_TO_REG(int val, int inv) ++{ ++ if (inv) ++ val = 255 - val; ++ if (val < 0) ++ return 0; ++ if (val > 255) ++ return 255; ++ return val; ++} ++ ++/* ++ * Voltage registers and conversions ++ */ ++ ++#define PC87365_REG_IN_CONVRATE 0x07 ++#define PC87365_REG_IN_CONFIG 0x08 ++#define PC87365_REG_IN 0x0B ++#define PC87365_REG_IN_MIN 0x0D ++#define PC87365_REG_IN_MAX 0x0C ++#define PC87365_REG_IN_STATUS 0x0A ++#define PC87365_REG_IN_ALARMS1 0x00 ++#define PC87365_REG_IN_ALARMS2 0x01 ++#define PC87365_REG_VID 0x06 ++ ++#define IN_FROM_REG(val,ref) (((val) * (ref) + 128) / 256) ++#define IN_TO_REG(val,ref) ((val)<0 ? 0 : \ ++ (val)*256>=(ref)*255 ? 255: \ ++ ((val) * 256 + (ref) / 2) / (ref)) ++ ++/* ++ * Temperature registers and conversions ++ */ ++ ++#define PC87365_REG_TEMP_CONFIG 0x08 ++#define PC87365_REG_TEMP 0x0B ++#define PC87365_REG_TEMP_MIN 0x0D ++#define PC87365_REG_TEMP_MAX 0x0C ++#define PC87365_REG_TEMP_CRIT 0x0E ++#define PC87365_REG_TEMP_STATUS 0x0A ++#define PC87365_REG_TEMP_ALARMS 0x00 ++ ++#define TEMP_FROM_REG(val) ((val)&0x80 ? (val) - 0x100 : (val)) ++#define TEMP_TO_REG(val) ((val)<-55 ? 201 : (val)>127 ? 0x7F : \ ++ (val)<0 ? (val) + 0x100 : (val)) ++ ++struct pc87360_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ int address[3]; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 fannr, innr, tempnr; ++ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u8 fan_status[3]; /* Register value */ ++ u8 pwm[3]; /* Register value */ ++ u16 fan_conf; /* Configuration register values, combined */ ++ ++ u16 in_vref; /* 10mV/bit */ ++ u8 in[14]; /* Register value */ ++ u8 in_min[14]; /* Register value */ ++ u8 in_max[14]; /* Register value */ ++ u8 in_crit[3]; /* Register value */ ++ u8 in_status[14]; /* Register value */ ++ u16 in_alarms; /* Register values, combined, masked */ ++ u8 vid_conf; /* Configuration register value */ ++ u8 vrm; ++ u8 vid; /* Register value */ ++ ++ u8 temp[3]; /* Register value */ ++ u8 temp_min[3]; /* Register value */ ++ u8 temp_max[3]; /* Register value */ ++ u8 temp_crit[3]; /* Register value */ ++ u8 temp_status[3]; /* Register value */ ++ u8 temp_alarms; /* Register value, masked */ ++}; ++ ++ ++static int pc87360_attach_adapter(struct i2c_adapter *adapter); ++static int pc87360_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int pc87360_detach_client(struct i2c_client *client); ++ ++static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, ++ u8 reg); ++static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, ++ u8 reg, u8 value); ++static void pc87360_init_client(struct i2c_client *client, int use_thermistors); ++static void pc87360_update_client(struct i2c_client *client); ++static int pc87360_find(u8 *devid, int *address); ++ ++ ++void pc87365_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++ ++static void pc87360_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pc87360_fan_status(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pc87360_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pc87360_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++void pc87365_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++void pc87365_in_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++void pc87365_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++void pc87365_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++ ++void pc87365_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++void pc87365_temp_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results); ++ ++static int pc87360_id = 0; ++ ++static struct i2c_driver pc87360_driver = { ++ .owner = THIS_MODULE, ++ .name = "PC8736x hardware monitor", ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = pc87360_attach_adapter, ++ .detach_client = pc87360_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define PC87365_SYSCTL_ALARMS 100 /* bit field */ ++ ++#define PC87360_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define PC87360_SYSCTL_FAN2 1102 ++#define PC87360_SYSCTL_FAN3 1103 /* not for PC87360/PC87363 */ ++#define PC87360_SYSCTL_FAN_DIV 1201 /* 1, 2, 4 or 8 */ ++#define PC87360_SYSCTL_FAN1_STATUS 1301 /* bit field */ ++#define PC87360_SYSCTL_FAN2_STATUS 1302 ++#define PC87360_SYSCTL_FAN3_STATUS 1303 /* not for PC87360/PC87363 */ ++#define PC87360_SYSCTL_PWM1 1401 /* 0-255 */ ++#define PC87360_SYSCTL_PWM2 1402 ++#define PC87360_SYSCTL_PWM3 1403 /* not for PC87360/PC87363 */ ++ ++#define PC87360_STATUS_FAN_READY 0x01 ++#define PC87360_STATUS_FAN_LOW 0x02 ++#define PC87360_STATUS_FAN_OVERFLOW 0x04 ++ ++#define PC87365_SYSCTL_IN0 2100 /* mV */ ++#define PC87365_SYSCTL_IN1 2101 ++#define PC87365_SYSCTL_IN2 2102 ++#define PC87365_SYSCTL_IN3 2103 ++#define PC87365_SYSCTL_IN4 2104 ++#define PC87365_SYSCTL_IN5 2105 ++#define PC87365_SYSCTL_IN6 2106 ++#define PC87365_SYSCTL_IN7 2107 ++#define PC87365_SYSCTL_IN8 2108 ++#define PC87365_SYSCTL_IN9 2109 ++#define PC87365_SYSCTL_IN10 2110 ++#define PC87365_SYSCTL_TEMP4 2111 /* not for PC87365 */ ++#define PC87365_SYSCTL_TEMP5 2112 /* not for PC87365 */ ++#define PC87365_SYSCTL_TEMP6 2113 /* not for PC87365 */ ++#define PC87365_SYSCTL_IN0_STATUS 2300 /* bit field */ ++#define PC87365_SYSCTL_IN1_STATUS 2301 ++#define PC87365_SYSCTL_IN2_STATUS 2302 ++#define PC87365_SYSCTL_IN3_STATUS 2303 ++#define PC87365_SYSCTL_IN4_STATUS 2304 ++#define PC87365_SYSCTL_IN5_STATUS 2305 ++#define PC87365_SYSCTL_IN6_STATUS 2306 ++#define PC87365_SYSCTL_IN7_STATUS 2307 ++#define PC87365_SYSCTL_IN8_STATUS 2308 ++#define PC87365_SYSCTL_IN9_STATUS 2309 ++#define PC87365_SYSCTL_IN10_STATUS 2310 ++#define PC87365_SYSCTL_TEMP4_STATUS 2311 /* not for PC87365 */ ++#define PC87365_SYSCTL_TEMP5_STATUS 2312 /* not for PC87365 */ ++#define PC87365_SYSCTL_TEMP6_STATUS 2313 /* not for PC87365 */ ++ ++#define PC87365_SYSCTL_VID 2400 ++#define PC87365_SYSCTL_VRM 2401 ++ ++#define PC87365_STATUS_IN_MIN 0x02 ++#define PC87365_STATUS_IN_MAX 0x04 ++ ++#define PC87365_SYSCTL_TEMP1 3101 /* degrees Celcius */ ++#define PC87365_SYSCTL_TEMP2 3102 ++#define PC87365_SYSCTL_TEMP3 3103 /* not for PC87365 */ ++#define PC87365_SYSCTL_TEMP1_STATUS 3301 /* bit field */ ++#define PC87365_SYSCTL_TEMP2_STATUS 3302 ++#define PC87365_SYSCTL_TEMP3_STATUS 3303 /* not for PC87365 */ ++ ++#define PC87365_STATUS_TEMP_MIN 0x02 ++#define PC87365_STATUS_TEMP_MAX 0x04 ++#define PC87365_STATUS_TEMP_CRIT 0x08 ++#define PC87365_STATUS_TEMP_OPEN 0x40 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++static ctl_table pc87360_dir_table_template[] = { /* PC87363 and PC87364 too */ ++ {PC87360_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_div}, ++ {PC87360_SYSCTL_FAN1_STATUS, "fan1_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_FAN2_STATUS, "fan2_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_FAN3_STATUS, "fan3_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {PC87360_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {PC87360_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {0} ++}; ++ ++static ctl_table pc87365_dir_table_template[] = { /* PC87366 too */ ++ {PC87365_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_alarms}, ++ {PC87360_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan}, ++ {PC87360_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_div}, ++ {PC87360_SYSCTL_FAN1_STATUS, "fan1_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_FAN2_STATUS, "fan2_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_FAN3_STATUS, "fan3_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_fan_status}, ++ {PC87360_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {PC87360_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {PC87360_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87360_pwm}, ++ {PC87365_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN10, "in10", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_IN0_STATUS, "in0_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN1_STATUS, "in1_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN2_STATUS, "in2_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN3_STATUS, "in3_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN4_STATUS, "in4_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN5_STATUS, "in5_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN6_STATUS, "in6_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN7_STATUS, "in7_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN8_STATUS, "in8_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN9_STATUS, "in9_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_IN10_STATUS, "in10_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp}, ++ {PC87365_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp}, ++ {PC87365_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp}, ++ {PC87365_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in}, ++ {PC87365_SYSCTL_TEMP1_STATUS, "temp1_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status}, ++ {PC87365_SYSCTL_TEMP2_STATUS, "temp2_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status}, ++ {PC87365_SYSCTL_TEMP3_STATUS, "temp3_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_temp_status}, ++ {PC87365_SYSCTL_TEMP4_STATUS, "temp4_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_TEMP5_STATUS, "temp5_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_TEMP6_STATUS, "temp6_status", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_in_status}, ++ {PC87365_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_vid}, ++ {PC87365_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &pc87365_vrm}, ++ {0} ++}; ++ ++static int pc87360_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, pc87360_detect); ++} ++ ++static int pc87360_find(u8 *devid, int *address) ++{ ++ u16 val; ++ int i; ++ int nrdev; /* logical device count */ ++ ++ /* No superio_enter */ ++ ++ /* Identify device */ ++ val = superio_inb(DEVID); ++ switch (val) { ++ case 0xE1: /* PC87360 */ ++ case 0xE8: /* PC87363 */ ++ case 0xE4: /* PC87364 */ ++ nrdev = 1; ++ break; ++ case 0xE5: /* PC87365 */ ++ case 0xE9: /* PC87366 */ ++ nrdev = 3; ++ break; ++ default: ++ superio_exit(); ++ return -ENODEV; ++ } ++ /* Remember the device id */ ++ *devid = val; ++ ++ for (i = 0; i < nrdev; i++) { ++ /* select logical device */ ++ superio_outb(DEV, logdev[i]); ++ ++ val = superio_inb(ACT); ++ if (!(val & 0x01)) { ++ printk(KERN_INFO "pc87360.o: Device 0x%02x not " ++ "activated\n", logdev[i]); ++ continue; ++ } ++ ++ val = (superio_inb(BASE) << 8) ++ | superio_inb(BASE + 1); ++ if (!val) { ++ printk(KERN_INFO "pc87360.o: Base address not set for " ++ "device 0x%02x\n", logdev[i]); ++ continue; ++ } ++ ++ address[i] = val; ++ ++ if (i==0) { /* Fans */ ++ confreg[0] = superio_inb(0xF0); ++ confreg[1] = superio_inb(0xF1); ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Fan 1: mon=%d " ++ "ctrl=%d inv=%d\n", (confreg[0]>>2)&1, ++ (confreg[0]>>3)&1, (confreg[0]>>4)&1); ++ printk(KERN_DEBUG "pc87360.o: Fan 2: mon=%d " ++ "ctrl=%d inv=%d\n", (confreg[0]>>5)&1, ++ (confreg[0]>>6)&1, (confreg[0]>>7)&1); ++ printk(KERN_DEBUG "pc87360.o: Fan 3: mon=%d " ++ "ctrl=%d inv=%d\n", confreg[1]&1, ++ (confreg[1]>>1)&1, (confreg[1]>>2)&1); ++#endif ++ } else if (i==1) { /* Voltages */ ++ /* Are we using thermistors? */ ++ if (*devid == 0xE9) { /* PC87366 */ ++ /* These registers are not logical-device ++ specific, just that we won't need them if ++ we don't use the VLM device */ ++ confreg[2] = superio_inb(0x2B); ++ confreg[3] = superio_inb(0x25); ++ ++ if (confreg[2] & 0x40) { ++ printk(KERN_INFO "pc87360.o: Using " ++ "thermistors for temperature " ++ "monitoring\n"); ++ } ++ if (confreg[3] & 0xE0) { ++ printk(KERN_INFO "pc87360.o: VID " ++ "inputs routed (mode %u)\n", ++ confreg[3] >> 5); ++ } ++ } ++ } ++ } ++ ++ superio_exit(); ++ return 0; ++} ++ ++/* We don't really care about the address. ++ Read from extra_isa instead. */ ++int pc87360_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct pc87360_data *data; ++ int err = 0; ++ const char *type_name = "pc87360"; ++ const char *client_name = "PC8736x chip"; ++ ctl_table *template = pc87360_dir_table_template; ++ int use_thermistors = 0; ++ ++ if (!i2c_is_isa_adapter(adapter)) { ++ return 0; ++ } ++ ++ for (i = 0; i < 3; i++) { ++ if (extra_isa[i] ++ && check_region(extra_isa[i], PC87360_EXTENT)) { ++ printk(KERN_ERR "pc87360.o: Region 0x%x-0x%x already " ++ "in use!\n", extra_isa[i], ++ extra_isa[i]+PC87360_EXTENT-1); ++ return -ENODEV; ++ } ++ } ++ ++ if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ memset(data, 0x00, sizeof(struct pc87360_data)); ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &pc87360_driver; ++ new_client->flags = 0; ++ ++ data->fannr = 2; ++ data->innr = 0; ++ data->tempnr = 0; ++ ++ switch (devid) { ++ case 0xe8: ++ type_name = "pc87363"; ++ break; ++ case 0xe4: ++ type_name = "pc87364"; ++ data->fannr = 3; ++ break; ++ case 0xe5: ++ type_name = "pc87365"; ++ template = pc87365_dir_table_template; ++ data->fannr = extra_isa[0] ? 3 : 0; ++ data->innr = extra_isa[1] ? 11 : 0; ++ data->tempnr = extra_isa[2] ? 2 : 0; ++ break; ++ case 0xe9: ++ type_name = "pc87366"; ++ template = pc87365_dir_table_template; ++ data->fannr = extra_isa[0] ? 3 : 0; ++ data->innr = extra_isa[1] ? 14 : 0; ++ data->tempnr = extra_isa[2] ? 3 : 0; ++ break; ++ } ++ ++ /* Retrieve the fans configuration from Super-I/O space */ ++ if (data->fannr) ++ data->fan_conf = confreg[0] | (confreg[1] << 8); ++ ++ for (i = 0; i < 3; i++) { ++ if ((data->address[i] = extra_isa[i])) { ++ request_region(extra_isa[i], PC87360_EXTENT, "pc87360"); ++ } ++ } ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = pc87360_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* Use the correct reference voltage ++ Unless both the VLM and the TMS logical devices agree to ++ use an external Vref, the internal one is used. */ ++ if (data->innr) { ++ i = pc87360_read_value(data, LD_IN, NO_BANK, ++ PC87365_REG_IN_CONFIG); ++ if (data->tempnr) { ++ i &= pc87360_read_value(data, LD_TEMP, NO_BANK, ++ PC87365_REG_TEMP_CONFIG); ++ } ++ data->in_vref = (i&0x02) ? 3025 : 2966; ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Using %s reference voltage\n", ++ (i&0x02) ? "external" : "internal"); ++#endif ++ ++ data->vid_conf = confreg[3]; ++ data->vrm = 90; ++ } ++ ++ /* Fan clock dividers may be needed before any data is read */ ++ for (i = 0; i < data->fannr; i++) { ++ data->fan_status[i] = pc87360_read_value(data, LD_FAN, ++ NO_BANK, PC87360_REG_FAN_STATUS(i)); ++ } ++ ++ if (init > 0) { ++ if (devid == 0xe9 && data->address[1]) /* PC87366 */ ++ use_thermistors = confreg[2] & 0x40; ++ ++ pc87360_init_client(new_client, use_thermistors); ++ } ++ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, template)) < 0) { ++ err = i; ++ goto ERROR2; ++ } ++ data->sysctl_id = i; ++ ++ return 0; ++ ++ERROR2: ++ i2c_detach_client(new_client); ++ERROR1: ++ for (i = 0; i < 3; i++) { ++ if (data->address[i]) { ++ release_region(data->address[i], PC87360_EXTENT); ++ } ++ } ++ kfree(data); ++ return err; ++} ++ ++static int pc87360_detach_client(struct i2c_client *client) ++{ ++ struct pc87360_data *data = client->data; ++ int i, err; ++ ++ i2c_deregister_entry(data->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk(KERN_ERR "pc87360.o: Client deregistration failed, " ++ "client not detached.\n"); ++ return err; ++ } ++ ++ for (i = 0; i < 3; i++) { ++ if (data->address[i]) { ++ release_region(data->address[i], PC87360_EXTENT); ++ } ++ } ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* ldi is the logical device index ++ bank is for voltages and temperatures only */ ++static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, ++ u8 reg) ++{ ++ int res; ++ ++ down(&(data->lock)); ++ if (bank != NO_BANK) { ++ outb_p(bank, data->address[ldi] + PC87365_REG_BANK); ++ } ++ res = inb_p(data->address[ldi] + reg); ++ up(&(data->lock)); ++ return res; ++} ++ ++static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, ++ u8 reg, u8 value) ++{ ++ down(&(data->lock)); ++ if (bank != NO_BANK) { ++ outb_p(bank, data->address[ldi] + PC87365_REG_BANK); ++ } ++ outb_p(value, data->address[ldi] + reg); ++ up(&(data->lock)); ++} ++ ++static void pc87360_init_client(struct i2c_client *client, int use_thermistors) ++{ ++ struct pc87360_data *data = client->data; ++ int i, nr; ++ const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; ++ const u8 init_temp[3] = { 2, 2, 1 }; ++ u8 reg; ++ ++ if (init >= 2 && data->innr) { ++ reg = pc87360_read_value(data, LD_IN, NO_BANK, ++ PC87365_REG_IN_CONVRATE); ++ printk(KERN_INFO "pc87360.o: VLM conversion set to" ++ "1s period, 160us delay\n"); ++ pc87360_write_value(data, LD_IN, NO_BANK, ++ PC87365_REG_IN_CONVRATE, ++ (reg & 0xC0) | 0x11); ++ } ++ ++ nr = data->innr < 11 ? data->innr : 11; ++ for (i=0; i= init_in[i]) { ++ /* Forcibly enable voltage channel */ ++ reg = pc87360_read_value(data, LD_IN, i, ++ PC87365_REG_IN_STATUS); ++ if (!(reg & 0x01)) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Forcibly " ++ "enabling in%d\n", i); ++#endif ++ pc87360_write_value(data, LD_IN, i, ++ PC87365_REG_IN_STATUS, ++ (reg & 0x68) | 0x87); ++ } ++ } ++ } ++ ++ /* We can't blindly trust the Super-I/O space configuration bit, ++ most BIOS won't set it properly */ ++ for (i=11; iinnr; i++) { ++ reg = pc87360_read_value(data, LD_IN, i, ++ PC87365_REG_TEMP_STATUS); ++ use_thermistors = use_thermistors || (reg & 0x01); ++ } ++ ++ i = use_thermistors ? 2 : 0; ++ for (; itempnr; i++) { ++ if (init >= init_temp[i]) { ++ /* Forcibly enable temperature channel */ ++ reg = pc87360_read_value(data, LD_TEMP, i, ++ PC87365_REG_TEMP_STATUS); ++ if (!(reg & 0x01)) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Forcibly " ++ "enabling temp%d\n", i+1); ++#endif ++ pc87360_write_value(data, LD_TEMP, i, ++ PC87365_REG_TEMP_STATUS, ++ 0xCF); ++ } ++ } ++ } ++ ++ if (use_thermistors) { ++ for (i=11; iinnr; i++) { ++ if (init >= init_in[i]) { ++ /* The pin may already be used by thermal ++ diodes */ ++ reg = pc87360_read_value(data, LD_TEMP, (i-11)/2, ++ PC87365_REG_TEMP_STATUS); ++ if (reg & 0x01) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Skipping " ++ "temp%d, pin already in use by ", ++ "temp%d\n", i-7, (i-11)/2); ++#endif ++ continue; ++ } ++ ++ /* Forcibly enable thermistor channel */ ++ reg = pc87360_read_value(data, LD_IN, i, ++ PC87365_REG_IN_STATUS); ++ if (!(reg & 0x01)) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Forcibly " ++ "enabling temp%d\n", i-7); ++#endif ++ pc87360_write_value(data, LD_IN, i, ++ PC87365_REG_TEMP_STATUS, ++ (reg & 0x60) | 0x8F); ++ } ++ } ++ } ++ } ++ ++ if (data->innr) { ++ reg = pc87360_read_value(data, LD_IN, NO_BANK, ++ PC87365_REG_IN_CONFIG); ++ if (reg & 0x01) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Forcibly " ++ "enabling monitoring (VLM)\n"); ++#endif ++ pc87360_write_value(data, LD_IN, NO_BANK, ++ PC87365_REG_IN_CONFIG, ++ reg & 0xFE); ++ } ++ } ++ ++ if (data->tempnr) { ++ reg = pc87360_read_value(data, LD_TEMP, NO_BANK, ++ PC87365_REG_TEMP_CONFIG); ++ if (reg & 0x01) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Forcibly " ++ "enabling monitoring (TMS)\n"); ++#endif ++ pc87360_write_value(data, LD_TEMP, NO_BANK, ++ PC87365_REG_TEMP_CONFIG, ++ reg & 0xFE); ++ } ++ ++ if (init >= 2) { ++ /* Chip config as documented by National Semi. */ ++ pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); ++ /* We voluntarily omit the bank here, in case the ++ sequence itself matters. It shouldn't be a problem, ++ since nobody else is supposed to access the ++ device at that point. */ ++ pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); ++ pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); ++ pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); ++ pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); ++ } ++ } ++} ++ ++static void pc87360_autodiv(struct pc87360_data *data, int nr) ++{ ++ u8 old_min = data->fan_min[nr]; ++ ++ /* Increase clock divider if needed and possible */ ++ if ((data->fan_status[nr] & 0x04) /* overflow flag */ ++ || (data->fan[nr] >= 224)) { /* next to overflow */ ++ if ((data->fan_status[nr] & 0x60) != 0x60) { ++ data->fan_status[nr] += 0x20; ++ data->fan_min[nr] >>= 1; ++ data->fan[nr] >>= 1; ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Increasing " ++ "clock divider to %d for fan %d\n", ++ FAN_DIV_FROM_REG(data->fan_status[nr]), ++ nr+1); ++#endif ++ } ++ } else { ++ /* Decrease clock divider if possible */ ++ while (!(data->fan_min[nr] & 0x80) /* fan min "nails" divider */ ++ && data->fan[nr] < 85 /* bad accuracy */ ++ && (data->fan_status[nr] & 0x60) != 0x00) { ++ data->fan_status[nr] -= 0x20; ++ data->fan_min[nr] <<= 1; ++ data->fan[nr] <<= 1; ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Decreasing " ++ "clock divider to %d for fan %d\n", ++ FAN_DIV_FROM_REG(data->fan_status[nr]), ++ nr+1); ++#endif ++ } ++ } ++ ++ /* Write new fan min if it changed */ ++ if (old_min != data->fan_min[nr]) { ++ pc87360_write_value(data, LD_FAN, NO_BANK, ++ PC87360_REG_FAN_MIN(nr), ++ data->fan_min[nr]); ++ } ++} ++ ++static void pc87360_update_client(struct i2c_client *client) ++{ ++ struct pc87360_data *data = client->data; ++ u8 i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ * 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "pc87360.o: Data update\n"); ++#endif ++ ++ /* Fans */ ++ for (i = 0; i < data->fannr; i++) { ++ if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { ++ data->fan_status[i] = pc87360_read_value(data, ++ LD_FAN, NO_BANK, ++ PC87360_REG_FAN_STATUS(i)); ++ data->fan[i] = pc87360_read_value(data, LD_FAN, ++ NO_BANK, PC87360_REG_FAN(i)); ++ data->fan_min[i] = pc87360_read_value(data, ++ LD_FAN, NO_BANK, ++ PC87360_REG_FAN_MIN(i)); ++ /* Change clock divider if needed */ ++ pc87360_autodiv(data, i); ++ /* Clear bits and write new divider */ ++ pc87360_write_value(data, LD_FAN, NO_BANK, ++ PC87360_REG_FAN_STATUS(i), ++ data->fan_status[i]); ++ } ++ data->pwm[i] = pc87360_read_value(data, LD_FAN, ++ NO_BANK, PC87360_REG_PWM(i)); ++ } ++ ++ /* Voltages */ ++ for (i = 0; i < data->innr; i++) { ++ data->in_status[i] = pc87360_read_value(data, LD_IN, i, ++ PC87365_REG_IN_STATUS); ++ /* Clear bits */ ++ pc87360_write_value(data, LD_IN, i, ++ PC87365_REG_IN_STATUS, ++ data->in_status[i]); ++ if ((data->in_status[i] & 0x81) == 0x81) { ++ data->in[i] = pc87360_read_value(data, LD_IN, ++ i, PC87365_REG_IN); ++ } ++ if (data->in_status[i] & 0x01) { ++ data->in_min[i] = pc87360_read_value(data, ++ LD_IN, i, ++ PC87365_REG_IN_MIN); ++ data->in_max[i] = pc87360_read_value(data, ++ LD_IN, i, ++ PC87365_REG_IN_MAX); ++ if (i >= 11) ++ data->in_crit[i-11] = ++ pc87360_read_value(data, LD_IN, ++ i, PC87365_REG_TEMP_CRIT); ++ } ++ } ++ if (data->innr) { ++ data->in_alarms = pc87360_read_value(data, LD_IN, ++ NO_BANK, PC87365_REG_IN_ALARMS1) ++ | ((pc87360_read_value(data, LD_IN, ++ NO_BANK, PC87365_REG_IN_ALARMS2) ++ & 0x07) << 8); ++ data->vid = (data->vid_conf & 0xE0) ? ++ pc87360_read_value(data, LD_IN, ++ NO_BANK, PC87365_REG_VID) : 0x1F; ++ } ++ ++ /* Temperatures */ ++ for (i = 0; i < data->tempnr; i++) { ++ data->temp_status[i] = pc87360_read_value(data, ++ LD_TEMP, i, ++ PC87365_REG_TEMP_STATUS); ++ /* Clear bits */ ++ pc87360_write_value(data, LD_TEMP, i, ++ PC87365_REG_TEMP_STATUS, ++ data->temp_status[i]); ++ if ((data->temp_status[i] & 0x81) == 0x81) { ++ data->temp[i] = pc87360_read_value(data, ++ LD_TEMP, i, ++ PC87365_REG_TEMP); ++ } ++ if (data->temp_status[i] & 0x01) { ++ data->temp_min[i] = pc87360_read_value(data, ++ LD_TEMP, i, ++ PC87365_REG_TEMP_MIN); ++ data->temp_max[i] = pc87360_read_value(data, ++ LD_TEMP, i, ++ PC87365_REG_TEMP_MAX); ++ data->temp_crit[i] = pc87360_read_value(data, ++ LD_TEMP, i, ++ PC87365_REG_TEMP_CRIT); ++ } ++ } ++ if (data->tempnr) { ++ data->temp_alarms = pc87360_read_value(data, LD_TEMP, ++ NO_BANK, PC87365_REG_TEMP_ALARMS) ++ & 0x3F; ++ } ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void pc87365_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = data->in_alarms; ++ results[1] = data->temp_alarms; ++ *nrels_mag = 2; ++ } ++} ++ ++void pc87360_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87360_SYSCTL_FAN1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr], ++ FAN_DIV_FROM_REG(data->fan_status[nr])); ++ results[1] = FAN_FROM_REG(data->fan[nr], ++ FAN_DIV_FROM_REG(data->fan_status[nr])); ++ *nrels_mag = 2; ++ } ++ /* We ignore National's recommendation */ ++ else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (nr >= data->fannr) ++ return; ++ if (*nrels_mag >= 1) { ++ int fan_min = FAN_TO_REG(results[0], ++ FAN_DIV_FROM_REG(data->fan_status[nr])); ++ /* If it wouldn't fit, change clock divisor */ ++ while (fan_min > 255 ++ && (data->fan_status[nr] & 0x60) != 0x60) { ++ fan_min >>= 1; ++ data->fan[nr] >>= 1; ++ data->fan_status[nr] += 0x20; ++ } ++ data->fan_min[nr] = fan_min > 255 ? 255 : fan_min; ++ pc87360_write_value(data, LD_FAN, NO_BANK, ++ PC87360_REG_FAN_MIN(nr), ++ data->fan_min[nr]); ++ /* Write new divider, preserve alarm bits */ ++ pc87360_write_value(data, LD_FAN, NO_BANK, ++ PC87360_REG_FAN_STATUS(nr), ++ data->fan_status[nr] & 0xF9); ++ } ++ } ++} ++ ++void pc87360_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int i; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ for (i = 0; i < data->fannr; i++) { ++ results[i] = FAN_DIV_FROM_REG(data->fan_status[i]); ++ } ++ for (; i < 3; i++) { ++ results[i] = 0; ++ } ++ *nrels_mag = 3; ++ } ++} ++ ++void pc87360_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87360_SYSCTL_PWM1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr], ++ FAN_CONFIG_INVERT(data->fan_conf, nr)); ++ results[1] = FAN_CONFIG_CONTROL(data->fan_conf, nr); ++ *nrels_mag = 2; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (nr >= data->fannr) ++ return; ++ if (*nrels_mag >= 1) { ++ data->pwm[nr] = PWM_TO_REG(results[0], ++ FAN_CONFIG_INVERT(data->fan_conf, nr)); ++ pc87360_write_value(data, LD_FAN, NO_BANK, ++ PC87360_REG_PWM(nr), ++ data->pwm[nr]); ++ } ++ } ++} ++ ++void pc87360_fan_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87360_SYSCTL_FAN1_STATUS; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = FAN_STATUS_FROM_REG(data->fan_status[nr]); ++ *nrels_mag = 1; ++ } ++} ++ ++void pc87365_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87365_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr], data->in_vref); ++ results[1] = IN_FROM_REG(data->in_max[nr], data->in_vref); ++ if (nr < 11) { ++ *nrels_mag = 3; ++ } else { ++ results[2] = IN_FROM_REG(data->in_crit[nr-11], ++ data->in_vref); ++ *nrels_mag = 4; ++ } ++ results[(*nrels_mag)-1] = IN_FROM_REG(data->in[nr], ++ data->in_vref); ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0], ++ data->in_vref); ++ pc87360_write_value(data, LD_IN, nr, ++ PC87365_REG_IN_MIN, ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1], ++ data->in_vref); ++ pc87360_write_value(data, LD_IN, nr, ++ PC87365_REG_IN_MAX, ++ data->in_max[nr]); ++ } ++ if (*nrels_mag >= 3 && nr >= 11) { ++ data->in_crit[nr-11] = IN_TO_REG(results[2], ++ data->in_vref); ++ pc87360_write_value(data, LD_IN, nr, ++ PC87365_REG_TEMP_CRIT, ++ data->in_crit[nr-11]); ++ } ++ } ++} ++ ++void pc87365_in_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87365_SYSCTL_IN0_STATUS; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = data->in_status[nr]; ++ *nrels_mag = 1; ++ } ++} ++ ++void pc87365_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = vid_from_reg(data->vid & 0x1f, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void pc87365_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void pc87365_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87365_SYSCTL_TEMP1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_max[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_min[nr]); ++ results[2] = TEMP_FROM_REG(data->temp_crit[nr]); ++ results[3] = TEMP_FROM_REG(data->temp[nr]); ++ *nrels_mag = 4; ++ } ++ else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (nr >= data->tempnr) ++ return; ++ if (*nrels_mag >= 1) { ++ data->temp_max[nr] = TEMP_TO_REG(results[0]); ++ pc87360_write_value(data, LD_TEMP, nr, ++ PC87365_REG_TEMP_MAX, ++ data->temp_max[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_min[nr] = TEMP_TO_REG(results[1]); ++ pc87360_write_value(data, LD_TEMP, nr, ++ PC87365_REG_TEMP_MIN, ++ data->temp_min[nr]); ++ } ++ if (*nrels_mag >= 3) { ++ data->temp_crit[nr] = TEMP_TO_REG(results[2]); ++ pc87360_write_value(data, LD_TEMP, nr, ++ PC87365_REG_TEMP_CRIT, ++ data->temp_crit[nr]); ++ } ++ } ++} ++ ++void pc87365_temp_status(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pc87360_data *data = client->data; ++ int nr = ctl_name - PC87365_SYSCTL_TEMP1_STATUS; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pc87360_update_client(client); ++ results[0] = data->temp_status[nr]; ++ *nrels_mag = 1; ++ } ++} ++ ++ ++static int __init pc87360_init(void) ++{ ++ int i; ++ ++ printk(KERN_INFO "pc87360.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (pc87360_find(&devid, extra_isa)) { ++ printk(KERN_WARNING "pc87360.o: PC8736x not detected, " ++ "module not inserted.\n"); ++ return -ENODEV; ++ } ++ ++ /* Arbitrarily pick one of the addresses */ ++ for (i = 0; i < 3; i++) { ++ if (extra_isa[i] != 0x0000) { ++ normal_isa[0] = extra_isa[i]; ++ break; ++ } ++ } ++ ++ if (normal_isa[0] == 0x0000) { ++ printk(KERN_WARNING "pc87360.o: No active logical device, " ++ "module not inserted.\n"); ++ return -ENODEV; ++ ++ } ++ ++ return i2c_add_driver(&pc87360_driver); ++} ++ ++static void __exit pc87360_exit(void) ++{ ++ i2c_del_driver(&pc87360_driver); ++} ++ ++ ++MODULE_AUTHOR("Jean Delvare "); ++MODULE_DESCRIPTION("PC8736x hardware monitor"); ++MODULE_LICENSE("GPL"); ++ ++module_init(pc87360_init); ++module_exit(pc87360_exit); +--- linux-old/drivers/sensors/pcf8574.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/pcf8574.c Mon Dec 13 20:18:51 2004 +@@ -0,0 +1,309 @@ ++/* ++ pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2000 Frodo Looijaard , ++ Philip Edelbrock , ++ Dan Eaton ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* A few notes about the PCF8574: ++ ++* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by ++ Philips Semiconductors. It is designed to provide a byte I2C ++ interface to up to 8 separate devices. ++ ++* The PCF8574 appears as a very simple SMBus device which can be ++ read from or written to with SMBUS byte read/write accesses. ++ ++* Because of the general purpose nature of this device, it will most ++ likely be necessary to customize the /proc interface to suit the ++ specific application. ++ ++ --Dan ++ ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x20, 0x27, 0x38, 0x3f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_2(pcf8574, pcf8574a); ++ ++/* The PCF8574 registers */ ++ ++/* (No registers. [Wow! This thing is SIMPLE!] ) */ ++ ++/* Initial values */ ++#define PCF8574_INIT 255 /* All outputs on (input mode) */ ++ ++/* Each client has this additional data */ ++struct pcf8574_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ ++ u8 read, write; /* Register values */ ++}; ++ ++static int pcf8574_attach_adapter(struct i2c_adapter *adapter); ++static int pcf8574_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int pcf8574_detach_client(struct i2c_client *client); ++ ++static void pcf8574_read(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pcf8574_write(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pcf8574_update_client(struct i2c_client *client); ++static void pcf8574_init_client(struct i2c_client *client); ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver pcf8574_driver = { ++ .owner = THIS_MODULE, ++ .name = "PCF8574 sensor chip driver", ++ .id = I2C_DRIVERID_PCF8574, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = pcf8574_attach_adapter, ++ .detach_client = pcf8574_detach_client, ++}; ++ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define PCF8574_SYSCTL_READ 1000 ++#define PCF8574_SYSCTL_WRITE 1001 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected PCF8574. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table pcf8574_dir_table_template[] = { ++ {PCF8574_SYSCTL_READ, "read", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8574_read}, ++ {PCF8574_SYSCTL_WRITE, "write", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8574_write}, ++ {0} ++}; ++ ++static int pcf8574_id = 0; ++ ++static int pcf8574_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, pcf8574_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int pcf8574_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct pcf8574_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("pcf8574.o: pcf8574_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. */ ++ if (!(data = kmalloc(sizeof(struct pcf8574_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &pcf8574_driver; ++ new_client->flags = 0; ++ ++ /* Now, we would do the remaining detection. But the PCF8574 is plainly ++ impossible to detect! Stupid chip. */ ++ ++ /* Determine the chip type */ ++ if (kind <= 0) { ++ if (address >= 0x38 && address <= 0x3f) ++ kind = pcf8574a; ++ else ++ kind = pcf8574; ++ } ++ ++ if (kind == pcf8574a) { ++ type_name = "pcf8574a"; ++ client_name = "PCF8574A chip"; ++ } else { ++ type_name = "pcf8574a"; ++ client_name = "PCF8574A chip"; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = pcf8574_id++; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR1; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ pcf8574_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR2; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the PCF8574 chip */ ++ pcf8574_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int pcf8574_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct pcf8574_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk("pcf8574.o: Client deregistration failed, " ++ "client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* Called when we have found a new PCF8574. */ ++static void pcf8574_init_client(struct i2c_client *client) ++{ ++ struct pcf8574_data *data = client->data; ++ data->write = PCF8574_INIT; ++ i2c_smbus_write_byte(client, data->write); ++} ++ ++ ++static void pcf8574_update_client(struct i2c_client *client) ++{ ++ struct pcf8574_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++#ifdef DEBUG ++ printk("Starting pcf8574 update\n"); ++#endif ++ ++ data->read = i2c_smbus_read_byte(client); ++ ++ up(&data->update_lock); ++} ++ ++ ++void pcf8574_read(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct pcf8574_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pcf8574_update_client(client); ++ results[0] = data->read; ++ *nrels_mag = 1; ++ } ++} ++void pcf8574_write(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct pcf8574_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->write; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->write = results[0]; ++ i2c_smbus_write_byte(client, data->write); ++ } ++ } ++} ++ ++ ++static int __init sm_pcf8574_init(void) ++{ ++ printk("pcf8574.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&pcf8574_driver); ++} ++ ++static void __exit sm_pcf8574_exit(void) ++{ ++ i2c_del_driver(&pcf8574_driver); ++} ++ ++ ++MODULE_AUTHOR("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "Dan Eaton and " ++ "Aurelien Jarno "); ++MODULE_DESCRIPTION("PCF8574 driver"); ++ ++module_init(sm_pcf8574_init); ++module_exit(sm_pcf8574_exit); +--- linux-old/drivers/sensors/pcf8591.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/pcf8591.c Mon Dec 13 20:18:51 2004 +@@ -0,0 +1,448 @@ ++/* ++ pcf8591.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2001 Aurelien Jarno ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(pcf8591); ++ ++/* The PCF8591 control byte */ ++/* 7 6 5 4 3 2 1 0 */ ++/* | 0 |AOEF| AIP | 0 |AINC| AICH | */ ++ ++#define PCF8591_CONTROL_BYTE_AOEF 0x40 /* Analog Output Enable Flag */ ++ /* (analog output active if 1) */ ++ ++#define PCF8591_CONTROL_BYTE_AIP 0x30 /* Analog Input Programming */ ++ /* 0x00 = four single ended inputs */ ++ /* 0x10 = three differential inputs */ ++ /* 0x20 = single ended and differential mixed */ ++ /* 0x30 = two differential inputs */ ++ ++#define PCF8591_CONTROL_BYTE_AINC 0x04 /* Autoincrement Flag */ ++ /* (switch on if 1) */ ++ ++#define PCF8591_CONTROL_BYTE_AICH 0x03 /* Analog Output Enable Flag */ ++ /* 0x00 = channel 0 */ ++ /* 0x01 = channel 1 */ ++ /* 0x02 = channel 2 */ ++ /* 0x03 = channel 3 */ ++ ++ ++/* Initial values */ ++#define PCF8591_INIT_CONTROL_BYTE (PCF8591_CONTROL_BYTE_AOEF | PCF8591_CONTROL_BYTE_AINC) ++ /* DAC out enabled, four single ended inputs, autoincrement */ ++ ++#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ ++ ++ ++/* Conversions. */ ++#define REG_TO_SIGNED(reg) (reg & 0x80)?(reg - 256):(reg) ++ /* Convert signed 8 bit value to signed value */ ++ ++ ++struct pcf8591_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 control_byte; ++ u8 ch[4]; ++ u8 aout; ++}; ++ ++static int pcf8591_attach_adapter(struct i2c_adapter *adapter); ++static int pcf8591_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int pcf8591_detach_client(struct i2c_client *client); ++ ++static void pcf8591_update_client(struct i2c_client *client); ++static void pcf8591_init_client(struct i2c_client *client); ++ ++static void pcf8591_ain_conf(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pcf8591_ain(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pcf8591_aout_enable(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void pcf8591_aout(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver pcf8591_driver = { ++ .owner = THIS_MODULE, ++ .name = "PCF8591 sensor chip driver", ++ .id = I2C_DRIVERID_PCF8591, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = pcf8591_attach_adapter, ++ .detach_client = pcf8591_detach_client, ++}; ++ ++static int pcf8591_id = 0; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define PCF8591_SYSCTL_AIN_CONF 1000 /* Analog input configuration */ ++#define PCF8591_SYSCTL_CH0 1001 /* Input channel 1 */ ++#define PCF8591_SYSCTL_CH1 1002 /* Input channel 2 */ ++#define PCF8591_SYSCTL_CH2 1003 /* Input channel 3 */ ++#define PCF8591_SYSCTL_CH3 1004 /* Input channel 4 */ ++#define PCF8591_SYSCTL_AOUT_ENABLE 1005 /* Analog output enable flag */ ++#define PCF8591_SYSCTL_AOUT 1006 /* Analog output */ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected PCF8591. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table pcf8591_dir_table_template[] = { ++ {PCF8591_SYSCTL_AIN_CONF, "ain_conf", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_ain_conf}, ++ {PCF8591_SYSCTL_CH0, "ch0", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_ain}, ++ {PCF8591_SYSCTL_CH1, "ch1", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_ain}, ++ {PCF8591_SYSCTL_CH2, "ch2", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_ain}, ++ {PCF8591_SYSCTL_CH3, "ch3", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_ain}, ++ {PCF8591_SYSCTL_AOUT_ENABLE, "aout_enable", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_aout_enable}, ++ {PCF8591_SYSCTL_AOUT, "aout", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &pcf8591_aout}, ++ {0} ++}; ++ ++ ++/* This function is called when: ++ * pcf8591_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and pcf8591_driver is still present) */ ++static int pcf8591_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, pcf8591_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int pcf8591_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct pcf8591_data *data; ++ int err = 0; ++ ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ (KERN_ERR "pcf8591.o: pcf8591_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE ++ | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. */ ++ if (!(data = kmalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &pcf8591_driver; ++ new_client->flags = 0; ++ ++ /* Now, we would do the remaining detection. But the PCF8591 is plainly ++ impossible to detect! Stupid chip. */ ++ ++ /* Determine the chip type - only one kind supported! */ ++ if (kind <= 0) ++ kind = pcf8591; ++ ++ type_name = "pcf8591"; ++ client_name = "PCF8591 chip"; ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = pcf8591_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ pcf8591_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the PCF8591 chip */ ++ pcf8591_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int pcf8591_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct pcf8591_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ (KERN_ERR "pcf8591.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* Called when we have found a new PCF8591. */ ++static void pcf8591_init_client(struct i2c_client *client) ++{ ++ struct pcf8591_data *data = client->data; ++ data->control_byte = PCF8591_INIT_CONTROL_BYTE; ++ data->aout = PCF8591_INIT_AOUT; ++ ++ i2c_smbus_write_byte_data(client, data->control_byte, data->aout); ++} ++ ++static void pcf8591_update_client(struct i2c_client *client) ++{ ++ struct pcf8591_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG "Starting pcf8591 update\n"); ++#endif ++ ++ i2c_smbus_write_byte(client, data->control_byte); ++ i2c_smbus_read_byte(client); /* The first byte transmitted contains the */ ++ /* conversion code of the previous read cycled */ ++ /* FLUSH IT ! */ ++ ++ ++ /* Number of byte to read to signed depend on the analog input mode */ ++ data->ch[0] = i2c_smbus_read_byte(client); ++ data->ch[1] = i2c_smbus_read_byte(client); ++ /* In all case, read at least two values */ ++ ++ if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) != 0x30) ++ data->ch[2] = i2c_smbus_read_byte(client); ++ /* Read the third value if not in "two differential inputs" mode */ ++ ++ if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0x00) ++ data->ch[3] = i2c_smbus_read_byte(client); ++ /* Read the fourth value only in "four single ended inputs" mode */ ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. */ ++void pcf8591_ain_conf(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pcf8591_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = (data->control_byte & PCF8591_CONTROL_BYTE_AIP) >> 4; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (results[0] >= 0 && results[0] <= 3) ++ { ++ data->control_byte &= ~PCF8591_CONTROL_BYTE_AIP; ++ data->control_byte |= (results[0] << 4); ++ i2c_smbus_write_byte(client, data->control_byte); ++ data->valid = 0; ++ } ++ } ++ } ++} ++ ++void pcf8591_ain(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pcf8591_data *data = client->data; ++ int nr = ctl_name - PCF8591_SYSCTL_CH0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ pcf8591_update_client(client); ++ ++ /* Number of data to show and conversion to signed depend on */ ++ /* the analog input mode */ ++ ++ switch(nr) { ++ case 0: ++ if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0) ++ | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2)) ++ results[0] = data->ch[0]; /* single ended */ ++ else ++ results[0] = REG_TO_SIGNED(data->ch[0]);/* differential */ ++ break; ++ case 1: ++ if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0) ++ | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2)) ++ results[0] = data->ch[1]; /* single ended */ ++ else ++ results[0] = REG_TO_SIGNED(data->ch[1]);/* differential */ ++ break; ++ case 2: ++ if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 3) ++ results[0] = 0; /* channel not used */ ++ else if ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0) ++ results[0] = data->ch[2]; /* single ended */ ++ else ++ results[0] = REG_TO_SIGNED(data->ch[2]);/* differential */ ++ break; ++ case 3: ++ if (((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 0) ++ | ((data->control_byte & PCF8591_CONTROL_BYTE_AIP) == 2)) ++ results[0] = data->ch[3]; /* single ended */ ++ else ++ results[0] = 0; /* channel not used */ ++ break; ++ } ++ *nrels_mag = 1; ++ } ++} ++ ++void pcf8591_aout_enable(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pcf8591_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = !(!(data->control_byte & PCF8591_CONTROL_BYTE_AOEF)); ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (results[0]) ++ data->control_byte |= PCF8591_CONTROL_BYTE_AOEF; ++ else ++ data->control_byte &= ~PCF8591_CONTROL_BYTE_AOEF; ++ ++ i2c_smbus_write_byte(client, data->control_byte); ++ } ++ } ++} ++ ++void pcf8591_aout(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct pcf8591_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->aout; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (results[0] >= 0 && results[0] <= 255) /* ignore values outside DAC range */ ++ { ++ data->aout = results[0]; ++ i2c_smbus_write_byte_data(client, data->control_byte, data->aout); ++ } ++ } ++ } ++} ++ ++static int __init sm_pcf8591_init(void) ++{ ++ printk(KERN_INFO "pcf8591.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&pcf8591_driver); ++} ++ ++static void __exit sm_pcf8591_exit(void) ++{ ++ i2c_del_driver(&pcf8591_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Aurelien Jarno "); ++MODULE_DESCRIPTION("PCF8591 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_pcf8591_init); ++module_exit(sm_pcf8591_exit); +--- linux-old/drivers/sensors/sis5595.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/sis5595.c Mon Dec 13 20:18:52 2004 +@@ -0,0 +1,735 @@ ++/* ++ sis5595.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ Copyright (c) 1998 - 2001 Frodo Looijaard , ++ Kyösti Mälkki , and ++ Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports following revisions: ++ Version PCI ID PCI Revision ++ 1 1039/0008 AF or less ++ 2 1039/0008 B0 or greater ++ ++ Note: these chips contain a 0008 device which is incompatible with the ++ 5595. We recognize these by the presence of the listed ++ "blacklist" PCI ID and refuse to load. ++ ++ NOT SUPPORTED PCI ID BLACKLIST PCI ID ++ 540 0008 0540 ++ 550 0008 0550 ++ 5513 0008 5511 ++ 5581 0008 5597 ++ 5582 0008 5597 ++ 5597 0008 5597 ++ 5598 0008 5597/5598 ++ 630 0008 0630 ++ 645 0008 0645 ++ 730 0008 0730 ++ 735 0008 0735 ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the device at the given address. */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++ ++/* Addresses to scan. ++ Note that we can't determine the ISA address until we have initialized ++ our module */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(sis5595); ++ ++static int blacklist[] = { ++ PCI_DEVICE_ID_SI_540, ++ PCI_DEVICE_ID_SI_550, ++ PCI_DEVICE_ID_SI_630, ++ PCI_DEVICE_ID_SI_730, ++ PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but ++ that ID shows up in other chips so we ++ use the 5511 ID for recognition */ ++ PCI_DEVICE_ID_SI_5597, ++ PCI_DEVICE_ID_SI_5598, ++ 0x645, ++ 0x735, ++ 0 }; ++/* ++ SiS southbridge has a LM78-like chip integrated on the same IC. ++ This driver is a customized copy of lm78.c ++*/ ++ ++/* Many SIS5595 constants specified below */ ++ ++/* Length of ISA address segment */ ++#define SIS5595_EXTENT 8 ++/* PCI Config Registers */ ++#define SIS5595_REVISION_REG 0x08 ++#define SIS5595_BASE_REG 0x68 ++#define SIS5595_PIN_REG 0x7A ++#define SIS5595_ENABLE_REG 0x7B ++ ++/* Where are the ISA address/data registers relative to the base address */ ++#define SIS5595_ADDR_REG_OFFSET 5 ++#define SIS5595_DATA_REG_OFFSET 6 ++ ++/* The SIS5595 registers */ ++#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) ++#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) ++#define SIS5595_REG_IN(nr) (0x20 + (nr)) ++ ++#define SIS5595_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define SIS5595_REG_FAN(nr) (0x27 + (nr)) ++ ++/* On the first version of the chip, the temp registers are separate. ++ On the second version, ++ TEMP pin is shared with IN4, configured in PCI register 0x7A. ++ The registers are the same as well. ++ OVER and HYST are really MAX and MIN. */ ++ ++#define REV2MIN 0xb0 ++#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ ++ SIS5595_REG_IN(4) : 0x27 ++#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ ++ SIS5595_REG_IN_MAX(4) : 0x39 ++#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ ++ SIS5595_REG_IN_MIN(4) : 0x3a ++ ++#define SIS5595_REG_CONFIG 0x40 ++#define SIS5595_REG_ALARM1 0x41 ++#define SIS5595_REG_ALARM2 0x42 ++#define SIS5595_REG_FANDIV 0x47 ++ ++/* Conversions. Limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) ++ ++/* Version 1 datasheet temp=.83*reg + 52.12 */ ++#define TEMP_FROM_REG(val) (((((val)>=0x80?(val)-0x100:(val))*83)+5212)/10) ++/* inverse 1.20*val - 62.77 */ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?\ ++ ((((val)*12)-6327)/100):\ ++ ((((val)*12)-6227)/100)),0,255)) ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++ ++/* For the SIS5595, we need to keep some data in memory. That ++ data is pointed to by sis5595_list[NR]->data. The structure itself is ++ dynamically allocated, at the time when the new sis5595 client is ++ allocated. */ ++struct sis5595_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ char maxins; /* == 3 if temp enabled, otherwise == 4 */ ++ u8 revision; /* Reg. value */ ++ ++ u8 in[5]; /* Register value */ ++ u8 in_max[5]; /* Register value */ ++ u8 in_min[5]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 temp; /* Register value */ ++ u8 temp_over; /* Register value - really max */ ++ u8 temp_hyst; /* Register value - really min */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u16 alarms; /* Register encoding, combined */ ++}; ++ ++static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ ++ ++static int sis5595_attach_adapter(struct i2c_adapter *adapter); ++static int sis5595_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int sis5595_detach_client(struct i2c_client *client); ++ ++static int sis5595_read_value(struct i2c_client *client, u8 register); ++static int sis5595_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void sis5595_update_client(struct i2c_client *client); ++static void sis5595_init_client(struct i2c_client *client); ++static int sis5595_find_sis(int *address); ++ ++ ++static void sis5595_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void sis5595_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void sis5595_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void sis5595_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void sis5595_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int sis5595_id = 0; ++ ++/* The driver. I choose to use type i2c_driver, as at is identical to both ++ smbus_driver and isa_driver, and clients could be of either kind */ ++static struct i2c_driver sis5595_driver = { ++ .owner = THIS_MODULE, ++ .name = "SiS 5595", ++ .id = I2C_DRIVERID_SIS5595, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = sis5595_attach_adapter, ++ .detach_client = sis5595_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define SIS5595_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define SIS5595_SYSCTL_IN1 1001 ++#define SIS5595_SYSCTL_IN2 1002 ++#define SIS5595_SYSCTL_IN3 1003 ++#define SIS5595_SYSCTL_IN4 1004 ++#define SIS5595_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define SIS5595_SYSCTL_FAN2 1102 ++#define SIS5595_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ ++#define SIS5595_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define SIS5595_SYSCTL_ALARMS 2001 /* bitvector */ ++ ++#define SIS5595_ALARM_IN0 0x01 ++#define SIS5595_ALARM_IN1 0x02 ++#define SIS5595_ALARM_IN2 0x04 ++#define SIS5595_ALARM_IN3 0x08 ++#define SIS5595_ALARM_BTI 0x20 ++#define SIS5595_ALARM_FAN1 0x40 ++#define SIS5595_ALARM_FAN2 0x80 ++#define SIS5595_ALARM_IN4 0x8000 ++#define SIS5595_ALARM_TEMP 0x8000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected SIS5595. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table sis5595_dir_table_template[] = { ++ {SIS5595_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_in}, ++ {SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_in}, ++ {SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_in}, ++ {SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_in}, ++ {SIS5595_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_in}, ++ {SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_fan}, ++ {SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_fan}, ++ {SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_temp}, ++ {SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_fan_div}, ++ {SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &sis5595_alarms}, ++ {0} ++}; ++ ++/* This is called when the module is loaded */ ++static int sis5595_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, sis5595_detect); ++} ++ ++/* Locate SiS bridge and correct base address for SIS5595 */ ++static int sis5595_find_sis(int *address) ++{ ++ u16 val; ++ int *i; ++ ++ if (!pci_present()) ++ return -ENODEV; ++ ++ if (!(s_bridge = ++ pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, ++ NULL))) ++ return -ENODEV; ++ ++ /* Look for imposters */ ++ for(i = blacklist; *i != 0; i++) { ++ if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { ++ printk("sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); ++ return -ENODEV; ++ } ++ } ++ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val)) ++ return -ENODEV; ++ ++ *address = val & ~(SIS5595_EXTENT - 1); ++ if (*address == 0 && force_addr == 0) { ++ printk("sis5595.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ if (force_addr) ++ *address = force_addr; /* so detect will get called */ ++ ++ return 0; ++} ++ ++int sis5595_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct sis5595_data *data; ++ int err = 0; ++ const char *type_name = "sis5595"; ++ const char *client_name = "SIS5595 chip"; ++ char val; ++ u16 a; ++ ++ /* Make sure we are probing the ISA bus!! */ ++ if (!i2c_is_isa_adapter(adapter)) { ++ printk ++ ("sis5595.o: sis5595_detect called for an I2C bus adapter?!?\n"); ++ return 0; ++ } ++ ++ if(force_addr) ++ address = force_addr & ~(SIS5595_EXTENT - 1); ++ if (check_region(address, SIS5595_EXTENT)) { ++ printk("sis5595.o: region 0x%x already in use!\n", address); ++ return -ENODEV; ++ } ++ if(force_addr) { ++ printk("sis5595.o: forcing ISA address 0x%04X\n", address); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) ++ return -ENODEV; ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) ++ return -ENODEV; ++ if ((a & ~(SIS5595_EXTENT - 1)) != address) { ++ /* doesn't work for some chips? */ ++ printk("sis5595.o: force address failed\n"); ++ return -ENODEV; ++ } ++ } ++ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) ++ return -ENODEV; ++ if((val & 0x80) == 0) { ++ printk("sis5595.o: enabling sensors\n"); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, ++ val | 0x80)) ++ return -ENODEV; ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) ++ return -ENODEV; ++ if((val & 0x80) == 0) { /* doesn't work for some chips! */ ++ printk("sis5595.o: sensors enable failed - not supported?\n"); ++ return -ENODEV; ++ } ++ } ++ ++ if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &sis5595_driver; ++ new_client->flags = 0; ++ ++ /* Reserve the ISA region */ ++ request_region(address, SIS5595_EXTENT, type_name); ++ ++ /* Check revision and pin registers to determine whether 3 or 4 voltages */ ++ pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); ++ if(data->revision < REV2MIN) { ++ data->maxins = 3; ++ } else { ++ pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); ++ if(val & 0x80) ++ /* 3 voltages, 1 temp */ ++ data->maxins = 3; ++ else ++ /* 4 voltages, no temps */ ++ data->maxins = 4; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = sis5595_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ sis5595_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the SIS5595 chip */ ++ sis5595_init_client(new_client); ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, SIS5595_EXTENT); ++ kfree(data); ++ return err; ++} ++ ++static int sis5595_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct sis5595_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("sis5595.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, SIS5595_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* ISA access must be locked explicitly. ++ There are some ugly typecasts here, but the good news is - they should ++ nowhere else be necessary! */ ++static int sis5595_read_value(struct i2c_client *client, u8 reg) ++{ ++ int res; ++ ++ down(&(((struct sis5595_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); ++ res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); ++ up(&(((struct sis5595_data *) (client->data))->lock)); ++ return res; ++} ++ ++static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ down(&(((struct sis5595_data *) (client->data))->lock)); ++ outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); ++ outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); ++ up(&(((struct sis5595_data *) (client->data))->lock)); ++ return 0; ++} ++ ++/* Called when we have found a new SIS5595. */ ++static void sis5595_init_client(struct i2c_client *client) ++{ ++ u8 reg; ++ ++ /* Start monitoring */ ++ reg = i2c_smbus_read_byte_data(client, SIS5595_REG_CONFIG); ++ sis5595_write_value(client, SIS5595_REG_CONFIG, (reg|0x01)&0x7F); ++} ++ ++static void sis5595_update_client(struct i2c_client *client) ++{ ++ struct sis5595_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++ for (i = 0; i <= data->maxins; i++) { ++ data->in[i] = ++ sis5595_read_value(client, SIS5595_REG_IN(i)); ++ data->in_min[i] = ++ sis5595_read_value(client, ++ SIS5595_REG_IN_MIN(i)); ++ data->in_max[i] = ++ sis5595_read_value(client, ++ SIS5595_REG_IN_MAX(i)); ++ } ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = ++ sis5595_read_value(client, SIS5595_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ sis5595_read_value(client, ++ SIS5595_REG_FAN_MIN(i)); ++ } ++ if(data->maxins == 3) { ++ data->temp = ++ sis5595_read_value(client, SIS5595_REG_TEMP); ++ data->temp_over = ++ sis5595_read_value(client, SIS5595_REG_TEMP_OVER); ++ data->temp_hyst = ++ sis5595_read_value(client, SIS5595_REG_TEMP_HYST); ++ } ++ i = sis5595_read_value(client, SIS5595_REG_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = ++ sis5595_read_value(client, SIS5595_REG_ALARM1) | ++ (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++ ++/* Return 0 for in4 and disallow writes if pin used for temp */ ++void sis5595_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct sis5595_data *data = client->data; ++ int nr = ctl_name - SIS5595_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ if(nr <= 3 || data->maxins == 4) { ++ sis5595_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ } else { ++ results[0] = 0; ++ results[1] = 0; ++ results[2] = 0; ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(nr <= 3 || data->maxins == 4) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ sis5595_write_value(client, ++ SIS5595_REG_IN_MIN(nr), data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ sis5595_write_value(client, ++ SIS5595_REG_IN_MAX(nr), data->in_max[nr]); ++ } ++ } ++ } ++} ++ ++void sis5595_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct sis5595_data *data = client->data; ++ int nr = ctl_name - SIS5595_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ sis5595_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr-1])); ++ sis5595_write_value(client, ++ SIS5595_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++/* Return 0 for temp and disallow writes if pin used for in4 */ ++void sis5595_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct sis5595_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ if(data->maxins == 3) { ++ sis5595_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ } else { ++ results[0] = 0; ++ results[1] = 0; ++ results[2] = 0; ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if(data->maxins == 3) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ sis5595_write_value(client, ++ SIS5595_REG_TEMP_OVER, data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ sis5595_write_value(client, ++ SIS5595_REG_TEMP_HYST, data->temp_hyst); ++ } ++ } ++ } ++} ++ ++void sis5595_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct sis5595_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ sis5595_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void sis5595_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct sis5595_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ sis5595_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = sis5595_read_value(client, SIS5595_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ sis5595_write_value(client, SIS5595_REG_FANDIV, old); ++ } ++ } ++} ++ ++static int __init sm_sis5595_init(void) ++{ ++ int addr; ++ ++ printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (sis5595_find_sis(&addr)) { ++ printk("sis5595.o: SIS5595 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ ++ return i2c_add_driver(&sis5595_driver); ++} ++ ++static void __exit sm_sis5595_exit(void) ++{ ++ i2c_del_driver(&sis5595_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Kyösti Mälkki "); ++MODULE_DESCRIPTION("SiS 5595 Sensor device"); ++ ++module_init(sm_sis5595_init); ++module_exit(sm_sis5595_exit); +--- linux-old/drivers/sensors/smsc47m1.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/smsc47m1.c Mon Dec 13 20:18:52 2004 +@@ -0,0 +1,515 @@ ++/* ++ smsc47m1.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ Copyright (c) 2002 Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++SENSORS_INSMOD_1(smsc47m1); ++ ++/* modified from kernel/include/traps.c */ ++#define REG 0x2e /* The register to read/write */ ++#define DEV 0x07 /* Register: Logical device select */ ++#define VAL 0x2f /* The value to read/write */ ++#define PME 0x0a /* The device with the fan registers in it */ ++#define DEVID 0x20 /* Register: Device ID */ ++ ++static inline void ++superio_outb(int reg, int val) ++{ ++ outb(reg, REG); ++ outb(val, VAL); ++} ++ ++static inline int ++superio_inb(int reg) ++{ ++ outb(reg, REG); ++ return inb(VAL); ++} ++ ++static inline void ++superio_select(void) ++{ ++ outb(DEV, REG); ++ outb(PME, VAL); ++} ++ ++static inline void ++superio_enter(void) ++{ ++ outb(0x55, REG); ++} ++ ++static inline void ++superio_exit(void) ++{ ++ outb(0xAA, REG); ++} ++ ++/* ++ * SMSC LPC47M10x (device id 0x59), LPC47M14x (device id 0x5F) and ++ * LPC47B27x (device id 0x51) have fan control. ++ * The 47M15x and 47M192 chips "with hardware monitoring block" ++ * can do much more besides (device id 0x60). ++ */ ++#define SMSC_DEVID_MATCH(id) ((id) == 0x51 || (id) == 0x59 || (id) == 0x5F) ++ ++#define SMSC_ACT_REG 0x30 ++#define SMSC_BASE_REG 0x60 ++ ++#define SMSC_EXTENT 0x80 ++ ++#define SMSC47M1_REG_ALARM1 0x04 ++#define SMSC47M1_REG_TPIN2 0x33 ++#define SMSC47M1_REG_TPIN1 0x34 ++#define SMSC47M1_REG_PPIN(nr) (0x37 - (nr)) ++#define SMSC47M1_REG_PWM(nr) (0x55 + (nr)) ++#define SMSC47M1_REG_FANDIV 0x58 ++#define SMSC47M1_REG_FAN(nr) (0x58 + (nr)) ++#define SMSC47M1_REG_FAN_MIN(nr) (0x5a + (nr)) ++ ++static inline u8 MIN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 0; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT(192 - ((983040 + rpm * div / 2) / (rpm * div)), ++ 0, 191); ++} ++ ++#define MIN_FROM_REG(val,div) ((val)>=192?0: \ ++ 983040/((192-(val))*(div))) ++#define FAN_FROM_REG(val,div,preload) ((val)==0?-1:(val)==255?0: \ ++ 983040/(((val)-preload)*(div))) ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++/* reg is 6 middle bits; /proc is 8 bits */ ++#define PWM_FROM_REG(val) (((val) << 1) & 0xfc) ++#define PWM_TO_REG(val) (((SENSORS_LIMIT((val), 0, 255)) >> 1) & 0x7e) ++ ++struct smsc47m1_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u8 alarms; /* Register encoding */ ++ u8 pwm[2]; /* Register value (bit 7 is enable) */ ++}; ++ ++ ++static int smsc47m1_attach_adapter(struct i2c_adapter *adapter); ++static int smsc47m1_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int smsc47m1_detach_client(struct i2c_client *client); ++ ++static int smsc47m1_read_value(struct i2c_client *client, u8 register); ++static int smsc47m1_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void smsc47m1_update_client(struct i2c_client *client); ++static void smsc47m1_init_client(struct i2c_client *client); ++static int smsc47m1_find(int *address); ++ ++ ++static void smsc47m1_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void smsc47m1_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void smsc47m1_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void smsc47m1_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int smsc47m1_id = 0; ++ ++static struct i2c_driver smsc47m1_driver = { ++ .owner = THIS_MODULE, ++ .name = "SMSC 47M1xx fan monitor", ++ .id = I2C_DRIVERID_SMSC47M1, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = smsc47m1_attach_adapter, ++ .detach_client = smsc47m1_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define SMSC47M1_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define SMSC47M1_SYSCTL_FAN2 1102 ++#define SMSC47M1_SYSCTL_PWM1 1401 ++#define SMSC47M1_SYSCTL_PWM2 1402 ++#define SMSC47M1_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define SMSC47M1_SYSCTL_ALARMS 2004 /* bitvector */ ++ ++#define SMSC47M1_ALARM_FAN1 0x0001 ++#define SMSC47M1_ALARM_FAN2 0x0002 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++static ctl_table smsc47m1_dir_table_template[] = { ++ {SMSC47M1_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_fan}, ++ {SMSC47M1_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_fan}, ++ {SMSC47M1_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_fan_div}, ++ {SMSC47M1_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_alarms}, ++ {SMSC47M1_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_pwm}, ++ {SMSC47M1_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &smsc47m1_pwm}, ++ {0} ++}; ++ ++static int smsc47m1_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, smsc47m1_detect); ++} ++ ++static int smsc47m1_find(int *address) ++{ ++ u16 val; ++ ++ superio_enter(); ++ val= superio_inb(DEVID); ++ if (!SMSC_DEVID_MATCH(val)) { ++ superio_exit(); ++ return -ENODEV; ++ } ++ ++ superio_select(); ++ val = (superio_inb(SMSC_BASE_REG) << 8) | ++ superio_inb(SMSC_BASE_REG + 1); ++ *address = val & ~(SMSC_EXTENT - 1); ++ if (*address == 0 && force_addr == 0) { ++ printk("smsc47m1.o: base address not set - use force_addr=0xaddr\n"); ++ superio_exit(); ++ return -ENODEV; ++ } ++ if (force_addr) ++ *address = force_addr; /* so detect will get called */ ++ ++ superio_exit(); ++ return 0; ++} ++ ++int smsc47m1_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct smsc47m1_data *data; ++ int err = 0; ++ const char *type_name = "smsc47m1"; ++ const char *client_name = "47M1xx chip"; ++ ++ if (!i2c_is_isa_adapter(adapter)) { ++ return 0; ++ } ++ ++ if(force_addr) ++ address = force_addr & ~(SMSC_EXTENT - 1); ++ if (check_region(address, SMSC_EXTENT)) { ++ printk("smsc47m1.o: region 0x%x already in use!\n", address); ++ return -ENODEV; ++ } ++ if(force_addr) { ++ printk("smsc47m1.o: forcing ISA address 0x%04X\n", address); ++ superio_enter(); ++ superio_select(); ++ superio_outb(SMSC_BASE_REG, address >> 8); ++ superio_outb(SMSC_BASE_REG+1, address & 0xff); ++ superio_exit(); ++ } ++ ++ if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &smsc47m1_driver; ++ new_client->flags = 0; ++ ++ request_region(address, SMSC_EXTENT, "smsc47m1-fans"); ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = smsc47m1_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ smsc47m1_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ smsc47m1_init_client(new_client); ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, SMSC_EXTENT); ++ kfree(data); ++ return err; ++} ++ ++static int smsc47m1_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct smsc47m1_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("smsc47m1.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, SMSC_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static int smsc47m1_read_value(struct i2c_client *client, u8 reg) ++{ ++ int res; ++ ++ down(&(((struct smsc47m1_data *) (client->data))->lock)); ++ res = inb_p(client->addr + reg); ++ up(&(((struct smsc47m1_data *) (client->data))->lock)); ++ return res; ++} ++ ++static int smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ down(&(((struct smsc47m1_data *) (client->data))->lock)); ++ outb_p(value, client->addr + reg); ++ up(&(((struct smsc47m1_data *) (client->data))->lock)); ++ return 0; ++} ++ ++static void smsc47m1_init_client(struct i2c_client *client) ++{ ++ /* configure pins for tach function */ ++ smsc47m1_write_value(client, SMSC47M1_REG_TPIN1, 0x05); ++ smsc47m1_write_value(client, SMSC47M1_REG_TPIN2, 0x05); ++} ++ ++static void smsc47m1_update_client(struct i2c_client *client) ++{ ++ struct smsc47m1_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = ++ smsc47m1_read_value(client, SMSC47M1_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ smsc47m1_read_value(client, SMSC47M1_REG_FAN_MIN(i)); ++ data->pwm[i - 1] = ++ smsc47m1_read_value(client, SMSC47M1_REG_PWM(i)); ++ } ++ ++ i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = ++ smsc47m1_read_value(client, SMSC47M1_REG_ALARM1) >> 6; ++ if(data->alarms) ++ smsc47m1_write_value(client, SMSC47M1_REG_ALARM1, 0xc0); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void smsc47m1_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct smsc47m1_data *data = client->data; ++ int nr = ctl_name - SMSC47M1_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ smsc47m1_update_client(client); ++ results[0] = MIN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1]), ++ data->fan_min[nr - 1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = MIN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr-1])); ++ smsc47m1_write_value(client, SMSC47M1_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void smsc47m1_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct smsc47m1_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ smsc47m1_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++void smsc47m1_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct smsc47m1_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ smsc47m1_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, old); ++ } ++ } ++} ++ ++void smsc47m1_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct smsc47m1_data *data = client->data; ++ int nr = 1 + ctl_name - SMSC47M1_SYSCTL_PWM1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ smsc47m1_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr - 1]); ++ results[1] = data->pwm[nr - 1] & 0x01; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->pwm[nr - 1] &= 0x81; ++ data->pwm[nr - 1] |= PWM_TO_REG(results[0]); ++ if (*nrels_mag >= 2) { ++ if(results[1] && (data->pwm[nr-1] & 0x01)) { ++ /* enable PWM */ ++/* hope BIOS did it already ++ smsc47m1_write_value(client, ++ SMSC47M1_REG_PPIN(nr), 0x04); ++*/ ++ data->pwm[nr - 1] &= 0xfe; ++ } else if((!results[1]) && (!(data->pwm[nr-1] & 0x01))) { ++ /* disable PWM */ ++ data->pwm[nr - 1] |= 0x01; ++ } ++ } ++ smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), ++ data->pwm[nr - 1]); ++ } ++ } ++} ++ ++static int __init sm_smsc47m1_init(void) ++{ ++ int addr; ++ ++ printk("smsc47m1.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (smsc47m1_find(&addr)) { ++ printk("smsc47m1.o: SMSC 47M1xx not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ ++ return i2c_add_driver(&smsc47m1_driver); ++} ++ ++static void __exit sm_smsc47m1_exit(void) ++{ ++ i2c_del_driver(&smsc47m1_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Mark D. Studebaker "); ++MODULE_DESCRIPTION("SMSC 47M1xx Fan sensors"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_smsc47m1_init); ++module_exit(sm_smsc47m1_exit); +--- linux-old/drivers/sensors/thmc50.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/thmc50.c Mon Dec 13 20:18:52 2004 +@@ -0,0 +1,496 @@ ++/* ++ thmc50.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999 Frodo Looijaard and ++ Philip Edelbrock ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#define DEBUG 1 ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++MODULE_LICENSE("GPL"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x2D, 0x2E, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(thmc50); ++ ++/* Many THMC50 constants specified below */ ++ ++/* The THMC50 registers */ ++#define THMC50_REG_TEMP 0x27 ++#define THMC50_REG_CONF 0x40 ++#define THMC50_REG_TEMP_HYST 0x3A ++#define THMC50_REG_TEMP_OS 0x39 ++ ++#define THMC50_REG_TEMP_TRIP 0x13 ++#define THMC50_REG_TEMP_REMOTE_TRIP 0x14 ++#define THMC50_REG_TEMP_DEFAULT_TRIP 0x17 ++#define THMC50_REG_TEMP_REMOTE_DEFAULT_TRIP 0x18 ++#define THMC50_REG_ANALOG_OUT 0x19 ++#define THMC50_REG_REMOTE_TEMP 0x26 ++#define THMC50_REG_REMOTE_TEMP_HYST 0x38 ++#define THMC50_REG_REMOTE_TEMP_OS 0x37 ++ ++#define THMC50_REG_INTER 0x41 ++#define THMC50_REG_INTER_MIRROR 0x4C ++#define THMC50_REG_INTER_MASK 0x43 ++ ++#define THMC50_REG_COMPANY_ID 0x3E ++#define THMC50_REG_DIE_CODE 0x3F ++ ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define TEMP_FROM_REG(val) ((val>127)?val - 0x0100:val) ++#define TEMP_TO_REG(val) ((val<0)?0x0100+val:val) ++ ++/* Each client has this additional data */ ++struct thmc50_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u16 temp, temp_os, temp_hyst, ++ remote_temp, remote_temp_os, remote_temp_hyst, ++ inter, inter_mask, die_code, analog_out; /* Register values */ ++}; ++ ++static int thmc50_attach_adapter(struct i2c_adapter *adapter); ++static int thmc50_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void thmc50_init_client(struct i2c_client *client); ++static int thmc50_detach_client(struct i2c_client *client); ++ ++static int thmc50_read_value(struct i2c_client *client, u8 reg); ++static int thmc50_write_value(struct i2c_client *client, u8 reg, ++ u16 value); ++static void thmc50_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void thmc50_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void thmc50_inter(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void thmc50_inter_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void thmc50_die_code(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void thmc50_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void thmc50_update_client(struct i2c_client *client); ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver thmc50_driver = { ++ .owner = THIS_MODULE, ++ .name = "THMC50 sensor chip driver", ++ .id = I2C_DRIVERID_THMC50, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = thmc50_attach_adapter, ++ .detach_client = thmc50_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define THMC50_SYSCTL_TEMP 1200 /* Degrees Celcius */ ++#define THMC50_SYSCTL_REMOTE_TEMP 1201 /* Degrees Celcius */ ++#define THMC50_SYSCTL_INTER 1202 ++#define THMC50_SYSCTL_INTER_MASK 1203 ++#define THMC50_SYSCTL_DIE_CODE 1204 ++#define THMC50_SYSCTL_ANALOG_OUT 1205 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected THMC50. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table thmc50_dir_table_template[] = { ++ {THMC50_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_temp}, ++ {THMC50_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_remote_temp}, ++ {THMC50_SYSCTL_INTER, "inter", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_inter}, ++ {THMC50_SYSCTL_INTER_MASK, "inter_mask", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_inter_mask}, ++ {THMC50_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_die_code}, ++ {THMC50_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &thmc50_analog_out}, ++ {0} ++}; ++ ++ ++static int thmc50_id = 0; ++ ++static int thmc50_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, thmc50_detect); ++} ++ ++/* This function is called by i2c_detect */ ++int thmc50_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int company, i; ++ struct i2c_client *new_client; ++ struct thmc50_data *data; ++ int err = 0; ++ const char *type_name, *client_name; ++ ++#ifdef DEBUG ++ printk("thmc50.o: Probing for THMC50 at 0x%2X on bus %d\n", ++ address, adapter->id); ++#endif ++ ++ /* Make sure we aren't probing the ISA bus!! This is just a safety check ++ at this moment; i2c_detect really won't call us. */ ++#ifdef DEBUG ++ if (i2c_is_isa_adapter(adapter)) { ++ printk ++ ("thmc50.o: thmc50_detect called for an ISA bus adapter?!?\n"); ++ return 0; ++ } ++#endif ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto ERROR0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access thmc50_{read,write}_value. */ ++ if (!(data = kmalloc(sizeof(struct thmc50_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &thmc50_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ company = ++ i2c_smbus_read_byte_data(new_client, THMC50_REG_COMPANY_ID); ++ ++ if (company != 0x49) { ++#ifdef DEBUG ++ printk ++ ("thmc50.o: Detect of THMC50 failed (reg 3E: 0x%X)\n", ++ company); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Determine the chip type - only one kind supported! */ ++ kind = thmc50; ++ ++ if (kind == thmc50) { ++ type_name = "thmc50"; ++ client_name = "THMC50 chip"; ++ } else { ++#ifdef DEBUG ++ printk("thmc50.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ goto ERROR1; ++ } ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = thmc50_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ thmc50_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ thmc50_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int thmc50_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct thmc50_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("thmc50.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* All registers are word-sized, except for the configuration register. ++ THMC50 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int thmc50_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* All registers are word-sized, except for the configuration register. ++ THMC50 uses a high-byte first convention, which is exactly opposite to ++ the usual practice. */ ++static int thmc50_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void thmc50_init_client(struct i2c_client *client) ++{ ++ thmc50_write_value(client, THMC50_REG_CONF, 1); ++} ++ ++static void thmc50_update_client(struct i2c_client *client) ++{ ++ struct thmc50_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting thmc50 update\n"); ++#endif ++ ++ data->temp = thmc50_read_value(client, THMC50_REG_TEMP); ++ data->temp_os = ++ thmc50_read_value(client, THMC50_REG_TEMP_OS); ++ data->temp_hyst = ++ thmc50_read_value(client, THMC50_REG_TEMP_HYST); ++ data->remote_temp = ++ thmc50_read_value(client, THMC50_REG_REMOTE_TEMP); ++ data->remote_temp_os = ++ thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_OS); ++ data->remote_temp_hyst = ++ thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_HYST); ++ data->inter = thmc50_read_value(client, THMC50_REG_INTER); ++ data->inter_mask = ++ thmc50_read_value(client, THMC50_REG_INTER_MASK); ++ data->die_code = ++ thmc50_read_value(client, THMC50_REG_DIE_CODE); ++ data->analog_out = ++ thmc50_read_value(client, THMC50_REG_ANALOG_OUT); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void thmc50_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_os); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_os = TEMP_TO_REG(results[0]); ++ thmc50_write_value(client, THMC50_REG_TEMP_OS, ++ data->temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ thmc50_write_value(client, THMC50_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++ ++void thmc50_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = TEMP_FROM_REG(data->remote_temp_os); ++ results[1] = TEMP_FROM_REG(data->remote_temp_hyst); ++ results[2] = TEMP_FROM_REG(data->remote_temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->remote_temp_os = TEMP_TO_REG(results[0]); ++ thmc50_write_value(client, ++ THMC50_REG_REMOTE_TEMP_OS, ++ data->remote_temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ data->remote_temp_hyst = TEMP_TO_REG(results[1]); ++ thmc50_write_value(client, ++ THMC50_REG_REMOTE_TEMP_HYST, ++ data->remote_temp_hyst); ++ } ++ } ++} ++ ++ ++void thmc50_inter(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = data->inter; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk("thmc50.o: No writes to Interrupt register!\n"); ++ } ++} ++ ++ ++void thmc50_inter_mask(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = data->inter_mask; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->inter_mask = results[0]; ++ thmc50_write_value(client, THMC50_REG_INTER_MASK, ++ data->inter_mask); ++ } ++ } ++} ++ ++ ++void thmc50_die_code(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = data->die_code; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ printk("thmc50.o: No writes to Die-Code register!\n"); ++ } ++} ++ ++ ++void thmc50_analog_out(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct thmc50_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ thmc50_update_client(client); ++ results[0] = data->analog_out; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->analog_out = results[0]; ++ thmc50_write_value(client, THMC50_REG_ANALOG_OUT, ++ data->analog_out); ++ } ++ } ++} ++ ++ ++ ++ ++static int __init sm_thmc50_init(void) ++{ ++ printk("thmc50.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ return i2c_add_driver(&thmc50_driver); ++} ++ ++static void __exit sm_thmc50_exit(void) ++{ ++ i2c_del_driver(&thmc50_driver); ++} ++ ++ ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("THMC50 driver"); ++ ++module_init(sm_thmc50_init); ++module_exit(sm_thmc50_exit); +--- linux-old/drivers/sensors/via686a.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/via686a.c Mon Dec 13 20:18:52 2004 +@@ -0,0 +1,849 @@ ++/* ++ via686a.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ Copyright (c) 1998 - 2002 Frodo Looijaard , ++ Kyösti Mälkki , ++ Mark Studebaker , ++ and Bob Dougherty ++ (Some conversion-factor data were contributed by Jonathan Teh Soon Yew ++ and Alex van Kaam .) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports the Via VT82C686A, VT82C686B south bridges. ++ Reports all as a 686A. ++ See doc/chips/via686a for details. ++ Warning - only supports a single device. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++ ++/* If force_addr is set to anything different from 0, we forcibly enable ++ the device at the given address. */ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++ ++/* Addresses to scan. ++ Note that we can't determine the ISA address until we have initialized ++ our module */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(via686a); ++ ++/* ++ The Via 686a southbridge has a LM78-like chip integrated on the same IC. ++ This driver is a customized copy of lm78.c ++*/ ++ ++/* Many VIA686A constants specified below */ ++ ++/* Length of ISA address segment */ ++#define VIA686A_EXTENT 0x80 ++#define VIA686A_BASE_REG 0x70 ++#define VIA686A_ENABLE_REG 0x74 ++ ++/* The VIA686A registers */ ++/* ins numbered 0-4 */ ++#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) ++#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) ++#define VIA686A_REG_IN(nr) (0x22 + (nr)) ++ ++/* fans numbered 1-2 */ ++#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define VIA686A_REG_FAN(nr) (0x28 + (nr)) ++ ++// the following values are as speced by VIA: ++static const u8 regtemp[] = { 0x20, 0x21, 0x1f }; ++static const u8 regover[] = { 0x39, 0x3d, 0x1d }; ++static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e }; ++ ++/* temps numbered 1-3 */ ++#define VIA686A_REG_TEMP(nr) (regtemp[(nr) - 1]) ++#define VIA686A_REG_TEMP_OVER(nr) (regover[(nr) - 1]) ++#define VIA686A_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) ++#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6 ++#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6 ++ ++#define VIA686A_REG_ALARM1 0x41 ++#define VIA686A_REG_ALARM2 0x42 ++#define VIA686A_REG_FANDIV 0x47 ++#define VIA686A_REG_CONFIG 0x40 ++// The following register sets temp interrupt mode (bits 1-0 for temp1, ++// 3-2 for temp2, 5-4 for temp3). Modes are: ++// 00 interrupt stays as long as value is out-of-range ++// 01 interrupt is cleared once register is read (default) ++// 10 comparator mode- like 00, but ignores hysteresis ++// 11 same as 00 ++#define VIA686A_REG_TEMP_MODE 0x4b ++// We'll just assume that you want to set all 3 simultaneously: ++#define VIA686A_TEMP_MODE_MASK 0x3F ++#define VIA686A_TEMP_MODE_CONTINUOUS (0x00) ++ ++/* Conversions. Limit checking is only done on the TO_REG ++ variants. */ ++ ++/********* VOLTAGE CONVERSIONS (Bob Dougherty) ********/ ++// From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): ++// voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp ++// voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V ++// voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V ++// voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V ++// voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V ++// in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; ++// That is: ++// volts = (25*regVal+133)*factor ++// regVal = (volts/factor-133)/25 ++// (These conversions were contributed by Jonathan Teh Soon Yew ++// ) ++static inline u8 IN_TO_REG(long val, int inNum) ++{ ++ /* To avoid floating point, we multiply constants by 10 (100 for +12V). ++ Rounding is done (120500 is actually 133000 - 12500). ++ Remember that val is expressed in 0.01V/bit, which is why we divide ++ by an additional 1000 (10000 for +12V): 100 for val and 10 (100) ++ for the constants. */ ++ if (inNum <= 1) ++ return (u8) ++ SENSORS_LIMIT((val * 21024 - 120500) / 25000, 0, 255); ++ else if (inNum == 2) ++ return (u8) ++ SENSORS_LIMIT((val * 15737 - 120500) / 25000, 0, 255); ++ else if (inNum == 3) ++ return (u8) ++ SENSORS_LIMIT((val * 10108 - 120500) / 25000, 0, 255); ++ else ++ return (u8) ++ SENSORS_LIMIT((val * 41714 - 1205000) / 250000, 0, 255); ++} ++ ++static inline long IN_FROM_REG(u8 val, int inNum) ++{ ++ /* To avoid floating point, we multiply constants by 10 (100 for +12V). ++ We also multiply them by 100 because we want 0.01V/bit for the ++ output value. Rounding is done. */ ++ if (inNum <= 1) ++ return (long) ((25000 * val + 133000 + 21024 / 2) / 21024); ++ else if (inNum == 2) ++ return (long) ((25000 * val + 133000 + 15737 / 2) / 15737); ++ else if (inNum == 3) ++ return (long) ((25000 * val + 133000 + 10108 / 2) / 10108); ++ else ++ return (long) ((250000 * val + 1330000 + 41714 / 2) / 41714); ++} ++ ++/********* FAN RPM CONVERSIONS ********/ ++// Higher register values = slower fans (the fan's strobe gates a counter). ++// But this chip saturates back at 0, not at 255 like all the other chips. ++// So, 0 means 0 RPM ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 0; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) ++ ++/******** TEMP CONVERSIONS (Bob Dougherty) *********/ ++// linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) ++// if(temp<169) ++// return double(temp)*0.427-32.08; ++// else if(temp>=169 && temp<=202) ++// return double(temp)*0.582-58.16; ++// else ++// return double(temp)*0.924-127.33; ++// ++// A fifth-order polynomial fits the unofficial data (provided by Alex van ++// Kaam ) a bit better. It also give more reasonable ++// numbers on my machine (ie. they agree with what my BIOS tells me). ++// Here's the fifth-order fit to the 8-bit data: ++// temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - ++// 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. ++// ++// (2000-10-25- RFD: thanks to Uwe Andersen for ++// finding my typos in this formula!) ++// ++// Alas, none of the elegant function-fit solutions will work because we ++// aren't allowed to use floating point in the kernel and doing it with ++// integers doesn't rpovide enough precision. So we'll do boring old ++// look-up table stuff. The unofficial data (see below) have effectively ++// 7-bit resolution (they are rounded to the nearest degree). I'm assuming ++// that the transfer function of the device is monotonic and smooth, so a ++// smooth function fit to the data will allow us to get better precision. ++// I used the 5th-order poly fit described above and solved for ++// VIA register values 0-255. I *10 before rounding, so we get tenth-degree ++// precision. (I could have done all 1024 values for our 10-bit readings, ++// but the function is very linear in the useful range (0-80 deg C), so ++// we'll just use linear interpolation for 10-bit readings.) So, tempLUT ++// is the temp at via register values 0-255: ++static const long tempLUT[] = ++ { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, ++ -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, ++ -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, ++ -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, ++ -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, ++ -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, ++ -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, ++ 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, ++ 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, ++ 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, ++ 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, ++ 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, ++ 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, ++ 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, ++ 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, ++ 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, ++ 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, ++ 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, ++ 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, ++ 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, ++ 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, ++ 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 ++}; ++ ++/* the original LUT values from Alex van Kaam ++ (for via register values 12-240): ++{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, ++-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, ++-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, ++-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, ++12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, ++22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, ++33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, ++45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, ++61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, ++85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; ++*/ ++ ++// Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed ++// an extra term for a good fit to these inverse data!) and then ++// solving for each temp value from -50 to 110 (the useable range for ++// this chip). Here's the fit: ++// viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 ++// - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) ++// Note that n=161: ++static const u8 viaLUT[] = ++ { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, ++ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, ++ 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, ++ 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, ++ 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, ++ 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, ++ 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, ++ 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, ++ 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, ++ 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, ++ 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, ++ 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, ++ 239, 240 ++}; ++ ++/* Converting temps to (8-bit) hyst and over registers ++ No interpolation here. ++ The +50 is because the temps start at -50 */ ++static inline u8 TEMP_TO_REG(long val) ++{ ++ return viaLUT[val <= -500 ? 0 : val >= 1100 ? 160 : ++ (val < 0 ? val - 5 : val + 5) / 10 + 50]; ++} ++ ++/* for 8-bit temperature hyst and over registers */ ++#define TEMP_FROM_REG(val) (tempLUT[(val)]) ++ ++/* for 10-bit temperature readings */ ++// You might _think_ this is too long to inline, but's it's really only ++// called once... ++static inline long TEMP_FROM_REG10(u16 val) ++{ ++ // the temp values are already *10, so we don't need to do that. ++ long temp; ++ u16 eightBits = val >> 2; ++ u16 twoBits = val & 3; ++ ++ /* no interpolation for these */ ++ if (twoBits == 0 || eightBits == 255) ++ return (long) tempLUT[eightBits]; ++ ++ /* do some linear interpolation */ ++ temp = (4 - twoBits) * tempLUT[eightBits] ++ + twoBits * tempLUT[eightBits + 1]; ++ /* achieve rounding */ ++ return (temp < 0 ? temp - 2 : temp + 2) / 4; ++} ++ ++#define ALARMS_FROM_REG(val) (val) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++ ++/* For the VIA686A, we need to keep some data in memory. ++ The structure is dynamically allocated, at the same time when a new ++ via686a client is allocated. */ ++struct via686a_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[5]; /* Register value */ ++ u8 in_max[5]; /* Register value */ ++ u8 in_min[5]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u16 temp[3]; /* Register value 10 bit */ ++ u8 temp_over[3]; /* Register value */ ++ u8 temp_hyst[3]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u16 alarms; /* Register encoding, combined */ ++}; ++ ++static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ ++ ++static int via686a_attach_adapter(struct i2c_adapter *adapter); ++static int via686a_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int via686a_detach_client(struct i2c_client *client); ++ ++static int via686a_read_value(struct i2c_client *client, u8 register); ++static void via686a_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void via686a_update_client(struct i2c_client *client); ++static void via686a_init_client(struct i2c_client *client); ++ ++ ++static void via686a_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void via686a_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void via686a_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void via686a_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void via686a_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int via686a_id = 0; ++ ++/* The driver. I choose to use type i2c_driver, as at is identical to both ++ smbus_driver and isa_driver, and clients could be of either kind */ ++static struct i2c_driver via686a_driver = { ++ .owner = THIS_MODULE, ++ .name = "VIA 686A", ++ .id = I2C_DRIVERID_VIA686A, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = via686a_attach_adapter, ++ .detach_client = via686a_detach_client, ++}; ++ ++ ++ ++/* The /proc/sys entries */ ++ ++/* -- SENSORS SYSCTL START -- */ ++#define VIA686A_SYSCTL_IN0 1000 ++#define VIA686A_SYSCTL_IN1 1001 ++#define VIA686A_SYSCTL_IN2 1002 ++#define VIA686A_SYSCTL_IN3 1003 ++#define VIA686A_SYSCTL_IN4 1004 ++#define VIA686A_SYSCTL_FAN1 1101 ++#define VIA686A_SYSCTL_FAN2 1102 ++#define VIA686A_SYSCTL_TEMP 1200 ++#define VIA686A_SYSCTL_TEMP2 1201 ++#define VIA686A_SYSCTL_TEMP3 1202 ++#define VIA686A_SYSCTL_FAN_DIV 2000 ++#define VIA686A_SYSCTL_ALARMS 2001 ++ ++#define VIA686A_ALARM_IN0 0x01 ++#define VIA686A_ALARM_IN1 0x02 ++#define VIA686A_ALARM_IN2 0x04 ++#define VIA686A_ALARM_IN3 0x08 ++#define VIA686A_ALARM_TEMP 0x10 ++#define VIA686A_ALARM_FAN1 0x40 ++#define VIA686A_ALARM_FAN2 0x80 ++#define VIA686A_ALARM_IN4 0x100 ++#define VIA686A_ALARM_TEMP2 0x800 ++#define VIA686A_ALARM_CHAS 0x1000 ++#define VIA686A_ALARM_TEMP3 0x8000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected VIA686A. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table via686a_dir_table_template[] = { ++ {VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_in}, ++ {VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_in}, ++ {VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_in}, ++ {VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_in}, ++ {VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_in}, ++ {VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_fan}, ++ {VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_fan}, ++ {VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &via686a_temp}, ++ {VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, ++ {VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, ++ {VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div}, ++ {VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms}, ++ {0} ++}; ++ ++static inline int via686a_read_value(struct i2c_client *client, u8 reg) ++{ ++ return (inb_p(client->addr + reg)); ++} ++ ++static inline void via686a_write_value(struct i2c_client *client, u8 reg, ++ u8 value) ++{ ++ outb_p(value, client->addr + reg); ++} ++ ++/* This is called when the module is loaded */ ++static int via686a_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, via686a_detect); ++} ++ ++int via686a_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct via686a_data *data; ++ int err = 0; ++ const char *type_name = "via686a"; ++ u16 val; ++ ++ /* Make sure we are probing the ISA bus!! */ ++ if (!i2c_is_isa_adapter(adapter)) { ++ printk ++ ("via686a.o: via686a_detect called for an I2C bus adapter?!?\n"); ++ return 0; ++ } ++ ++ /* 8231 requires multiple of 256, we enforce that on 686 as well */ ++ if(force_addr) ++ address = force_addr & 0xFF00; ++ if (check_region(address, VIA686A_EXTENT)) { ++ printk("via686a.o: region 0x%x already in use!\n", ++ address); ++ return -ENODEV; ++ } ++ ++ if(force_addr) { ++ printk("via686a.o: forcing ISA address 0x%04X\n", address); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) ++ return -ENODEV; ++ } ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) ++ return -ENODEV; ++ if (!(val & 0x0001)) { ++ printk("via686a.o: enabling sensors\n"); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, ++ val | 0x0001)) ++ return -ENODEV; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &via686a_driver; ++ new_client->flags = 0; ++ ++ /* Reserve the ISA region */ ++ request_region(address, VIA686A_EXTENT, "via686a-sensors"); ++ ++ /* Fill in the remaining client fields and put into the global list */ ++ strcpy(new_client->name, "Via 686A Integrated Sensors"); ++ ++ new_client->id = via686a_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ via686a_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the VIA686A chip */ ++ via686a_init_client(new_client); ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, VIA686A_EXTENT); ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int via686a_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct via686a_data *) ++ (client->data))->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("via686a.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, VIA686A_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++/* Called when we have found a new VIA686A. */ ++static void via686a_init_client(struct i2c_client *client) ++{ ++ u8 reg; ++ ++ /* Start monitoring */ ++ reg = via686a_read_value(client, VIA686A_REG_CONFIG); ++ via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F); ++ ++ /* Configure temp interrupt mode for continuous-interrupt operation */ ++ via686a_write_value(client, VIA686A_REG_TEMP_MODE, ++ via686a_read_value(client, VIA686A_REG_TEMP_MODE) & ++ !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); ++} ++ ++static void via686a_update_client(struct i2c_client *client) ++{ ++ struct via686a_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if (time_after(jiffies - data->last_updated, HZ + HZ / 2) || ++ time_before(jiffies, data->last_updated) || !data->valid) { ++ ++ for (i = 0; i <= 4; i++) { ++ data->in[i] = ++ via686a_read_value(client, VIA686A_REG_IN(i)); ++ data->in_min[i] = via686a_read_value(client, ++ VIA686A_REG_IN_MIN ++ (i)); ++ data->in_max[i] = ++ via686a_read_value(client, VIA686A_REG_IN_MAX(i)); ++ } ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = ++ via686a_read_value(client, VIA686A_REG_FAN(i)); ++ data->fan_min[i - 1] = via686a_read_value(client, ++ VIA686A_REG_FAN_MIN(i)); ++ } ++ for (i = 1; i <= 3; i++) { ++ data->temp[i - 1] = via686a_read_value(client, ++ VIA686A_REG_TEMP(i)) << 2; ++ data->temp_over[i - 1] = ++ via686a_read_value(client, ++ VIA686A_REG_TEMP_OVER(i)); ++ data->temp_hyst[i - 1] = ++ via686a_read_value(client, ++ VIA686A_REG_TEMP_HYST(i)); ++ } ++ /* add in lower 2 bits ++ temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 ++ temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 ++ temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 ++ */ ++ data->temp[0] |= (via686a_read_value(client, ++ VIA686A_REG_TEMP_LOW1) ++ & 0xc0) >> 6; ++ data->temp[1] |= ++ (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & ++ 0x30) >> 4; ++ data->temp[2] |= ++ (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & ++ 0xc0) >> 6; ++ ++ i = via686a_read_value(client, VIA686A_REG_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = ++ via686a_read_value(client, ++ VIA686A_REG_ALARM1) | ++ (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++static void via686a_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct via686a_data *data = client->data; ++ int nr = ctl_name - VIA686A_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ via686a_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr], nr); ++ results[1] = IN_FROM_REG(data->in_max[nr], nr); ++ results[2] = IN_FROM_REG(data->in[nr], nr); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0], nr); ++ via686a_write_value(client, VIA686A_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1], nr); ++ via686a_write_value(client, VIA686A_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void via686a_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct via686a_data *data = client->data; ++ int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ via686a_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div ++ [nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = FAN_TO_REG(results[0], ++ DIV_FROM_REG(data-> ++ fan_div[nr -1])); ++ via686a_write_value(client, ++ VIA686A_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++void via686a_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct via686a_data *data = client->data; ++ int nr = ctl_name - VIA686A_SYSCTL_TEMP; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ via686a_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = TEMP_FROM_REG10(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over[nr] = TEMP_TO_REG(results[0]); ++ via686a_write_value(client, ++ VIA686A_REG_TEMP_OVER(nr + 1), ++ data->temp_over[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = TEMP_TO_REG(results[1]); ++ via686a_write_value(client, ++ VIA686A_REG_TEMP_HYST(nr + 1), ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct via686a_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ via686a_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void via686a_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct via686a_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ via686a_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = via686a_read_value(client, VIA686A_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ via686a_write_value(client, VIA686A_REG_FANDIV, ++ old); ++ } ++ } ++} ++ ++ ++static struct pci_device_id via686a_pci_ids[] __devinitdata = { ++ {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ { 0, } ++}; ++ ++static int __devinit via686a_pci_probe(struct pci_dev *dev, ++ const struct pci_device_id *id) ++{ ++ u16 val; ++ int addr = 0; ++ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(dev, VIA686A_BASE_REG, &val)) ++ return -ENODEV; ++ ++ addr = val & ~(VIA686A_EXTENT - 1); ++ if (addr == 0 && force_addr == 0) { ++ printk("via686a.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ if (force_addr) ++ addr = force_addr; /* so detect will get called */ ++ ++ if (!addr) { ++ printk("via686a.o: No Via 686A sensors found.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ s_bridge = dev; ++ return i2c_add_driver(&via686a_driver); ++} ++ ++static void __devexit via686a_pci_remove(struct pci_dev *dev) ++{ ++ i2c_del_driver(&via686a_driver); ++} ++ ++static struct pci_driver via686a_pci_driver = { ++ .name = "via686a", ++ .id_table = via686a_pci_ids, ++ .probe = via686a_pci_probe, ++ .remove = __devexit_p(via686a_pci_remove), ++}; ++ ++static int __init sm_via686a_init(void) ++{ ++ printk("via686a.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return pci_module_init(&via686a_pci_driver); ++} ++ ++static void __exit sm_via686a_exit(void) ++{ ++ pci_unregister_driver(&via686a_pci_driver); ++} ++ ++MODULE_AUTHOR("Kyösti Mälkki , " ++ "Mark Studebaker " ++ "and Bob Dougherty "); ++MODULE_DESCRIPTION("VIA 686A Sensor device"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_via686a_init); ++module_exit(sm_via686a_exit); +--- linux-old/drivers/sensors/vt1211.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/vt1211.c Mon Dec 13 20:18:53 2004 +@@ -0,0 +1,823 @@ ++/* ++ vt1211.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ Copyright (c) 2002 Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports VIA VT1211 Super I/O sensors via ISA (LPC) accesses only. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++SENSORS_INSMOD_1(vt1211); ++ ++/* modified from kernel/include/traps.c */ ++#define REG 0x2e /* The register to read/write */ ++#define DEV 0x07 /* Register: Logical device select */ ++#define VAL 0x2f /* The value to read/write */ ++#define PME 0x0b /* The device with the hardware monitor */ ++#define DEVID 0x20 /* Register: Device ID */ ++ ++static inline void ++superio_outb(int reg, int val) ++{ ++ outb(reg, REG); ++ outb(val, VAL); ++} ++ ++static inline int ++superio_inb(int reg) ++{ ++ outb(reg, REG); ++ return inb(VAL); ++} ++ ++static inline void ++superio_select(void) ++{ ++ outb(DEV, REG); ++ outb(PME, VAL); ++} ++ ++static inline void ++superio_enter(void) ++{ ++ outb(0x87, REG); ++ outb(0x87, REG); ++} ++ ++static inline void ++superio_exit(void) ++{ ++ outb(0xAA, REG); ++} ++ ++#define VT1211_DEVID 0x3c ++#define VT1211_ACT_REG 0x30 ++#define VT1211_BASE_REG 0x60 ++ ++#define VT1211_EXTENT 0x80 ++ ++/* pwm numbered 1-2 */ ++#define VT1211_REG_PWM(nr) (0x5f + (nr)) ++#define VT1211_REG_PWM_CTL 0x51 ++ ++/* The VT1211 registers */ ++/* We define the sensors as follows. Somewhat convoluted to minimize ++ changes from via686a. ++ Sensor Voltage Mode Temp Mode ++ -------- ------------ --------- ++ Reading 1 temp3 ++ Reading 3 temp1 not in vt1211 ++ UCH1/Reading2 in0 temp2 ++ UCH2 in1 temp4 ++ UCH3 in2 temp5 ++ UCH4 in3 temp6 ++ UCH5 in4 temp7 ++ 3.3V in5 ++ -12V in6 not in vt1211 ++*/ ++ ++/* ins numbered 0-6 */ ++#define VT1211_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2)) ++#define VT1211_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2)) ++#define VT1211_REG_IN(nr) (0x21 + (nr)) ++ ++/* fans numbered 1-2 */ ++#define VT1211_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define VT1211_REG_FAN(nr) (0x28 + (nr)) ++ ++static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 }; ++static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 }; ++static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 }; ++ ++/* temps numbered 1-7 */ ++#define VT1211_REG_TEMP(nr) (regtemp[(nr) - 1]) ++#define VT1211_REG_TEMP_OVER(nr) (regover[(nr) - 1]) ++#define VT1211_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) ++#define VT1211_REG_TEMP_LOW3 0x4b /* bits 7-6 */ ++#define VT1211_REG_TEMP_LOW2 0x49 /* bits 5-4 */ ++#define VT1211_REG_TEMP_LOW47 0x4d ++ ++#define VT1211_REG_CONFIG 0x40 ++#define VT1211_REG_ALARM1 0x41 ++#define VT1211_REG_ALARM2 0x42 ++#define VT1211_REG_VID 0x45 ++#define VT1211_REG_FANDIV 0x47 ++#define VT1211_REG_UCH_CONFIG 0x4a ++#define VT1211_REG_TEMP1_CONFIG 0x4b ++#define VT1211_REG_TEMP2_CONFIG 0x4c ++ ++/* temps 1-7; voltages 0-6 */ ++#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \ ++ (i) == 3 ? 1 : \ ++ (i) == 2 ? ((ch_config) >> 1) & 0x01 : \ ++ ((ch_config) >> ((i)-1)) & 0x01) ++#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01)) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++#define PWM_FROM_REG(val) (val) ++#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255) ++ ++#define TEMP_FROM_REG(val) ((val)*10) ++#define TEMP_FROM_REG10(val) (((val)*10)/4) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),0,255)) ++#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val) ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255)) ++ ++ ++/********* FAN RPM CONVERSIONS ********/ ++/* But this chip saturates back at 0, not at 255 like all the other chips. ++ So, 0 means 0 RPM */ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 0; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1310720 + rpm * div / 2) / (rpm * div), 1, 255); ++} ++ ++#define MIN_TO_REG(a,b) FAN_TO_REG(a,b) ++#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div))) ++ ++struct vt1211_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u16 temp[7]; /* Register value 10 bit */ ++ u8 temp_over[7]; /* Register value */ ++ u8 temp_hyst[7]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u16 alarms; /* Register encoding */ ++ u8 pwm[2]; /* Register value */ ++ u8 pwm_ctl; /* Register value */ ++ u8 vid; /* Register encoding */ ++ u8 vrm; ++ u8 uch_config; ++}; ++ ++static int vt1211_attach_adapter(struct i2c_adapter *adapter); ++static int vt1211_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int vt1211_detach_client(struct i2c_client *client); ++ ++static inline int vt_rdval(struct i2c_client *client, u8 register); ++static inline void vt1211_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void vt1211_update_client(struct i2c_client *client); ++static void vt1211_init_client(struct i2c_client *client); ++static int vt1211_find(int *address); ++ ++ ++static void vt1211_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_uch(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt1211_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int vt1211_id = 0; ++ ++static struct i2c_driver vt1211_driver = { ++ .owner = THIS_MODULE, ++ .name = "VT1211 sensors driver", ++ .id = I2C_DRIVERID_VT1211, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = vt1211_attach_adapter, ++ .detach_client = vt1211_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define VT1211_SYSCTL_IN0 1000 ++#define VT1211_SYSCTL_IN1 1001 ++#define VT1211_SYSCTL_IN2 1002 ++#define VT1211_SYSCTL_IN3 1003 ++#define VT1211_SYSCTL_IN4 1004 ++#define VT1211_SYSCTL_IN5 1005 ++#define VT1211_SYSCTL_IN6 1006 ++#define VT1211_SYSCTL_FAN1 1101 ++#define VT1211_SYSCTL_FAN2 1102 ++#define VT1211_SYSCTL_TEMP 1200 ++#define VT1211_SYSCTL_TEMP2 1201 ++#define VT1211_SYSCTL_TEMP3 1202 ++#define VT1211_SYSCTL_TEMP4 1203 ++#define VT1211_SYSCTL_TEMP5 1204 ++#define VT1211_SYSCTL_TEMP6 1205 ++#define VT1211_SYSCTL_TEMP7 1206 ++#define VT1211_SYSCTL_VID 1300 ++#define VT1211_SYSCTL_PWM1 1401 ++#define VT1211_SYSCTL_PWM2 1402 ++#define VT1211_SYSCTL_VRM 1600 ++#define VT1211_SYSCTL_UCH 1700 ++#define VT1211_SYSCTL_FAN_DIV 2000 ++#define VT1211_SYSCTL_ALARMS 2001 ++ ++#define VT1211_ALARM_IN1 0x01 ++#define VT1211_ALARM_IN2 0x02 ++#define VT1211_ALARM_IN5 0x04 ++#define VT1211_ALARM_IN3 0x08 ++#define VT1211_ALARM_TEMP 0x10 ++#define VT1211_ALARM_FAN1 0x40 ++#define VT1211_ALARM_FAN2 0x80 ++#define VT1211_ALARM_IN4 0x100 ++#define VT1211_ALARM_IN6 0x200 ++#define VT1211_ALARM_TEMP2 0x800 ++#define VT1211_ALARM_CHAS 0x1000 ++#define VT1211_ALARM_TEMP3 0x8000 ++/* duplicates */ ++#define VT1211_ALARM_IN0 VT1211_ALARM_TEMP ++#define VT1211_ALARM_TEMP4 VT1211_ALARM_IN1 ++#define VT1211_ALARM_TEMP5 VT1211_ALARM_IN2 ++#define VT1211_ALARM_TEMP6 VT1211_ALARM_IN3 ++#define VT1211_ALARM_TEMP7 VT1211_ALARM_IN4 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++static ctl_table vt1211_dir_table_template[] = { ++ {VT1211_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++/* ++ datasheet says these are reserved ++ {VT1211_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_in}, ++ {VT1211_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_temp}, ++*/ ++ {VT1211_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_TEMP7, "temp7", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt1211_temp}, ++ {VT1211_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_fan}, ++ {VT1211_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_fan}, ++ {VT1211_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_fan_div}, ++ {VT1211_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_alarms}, ++ {VT1211_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_pwm}, ++ {VT1211_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_pwm}, ++ {VT1211_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_vid}, ++ {VT1211_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_vrm}, ++ {VT1211_SYSCTL_UCH, "uch_config", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt1211_uch}, ++ {0} ++}; ++ ++static int vt1211_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, vt1211_detect); ++} ++ ++static int vt1211_find(int *address) ++{ ++ u16 val; ++ ++ superio_enter(); ++ val= superio_inb(DEVID); ++ if(VT1211_DEVID != val) { ++ superio_exit(); ++ return -ENODEV; ++ } ++ ++ superio_select(); ++ val = (superio_inb(VT1211_BASE_REG) << 8) | ++ superio_inb(VT1211_BASE_REG + 1); ++ *address = val & ~(VT1211_EXTENT - 1); ++ if (*address == 0 && force_addr == 0) { ++ printk("vt1211.o: base address not set - use force_addr=0xaddr\n"); ++ superio_exit(); ++ return -ENODEV; ++ } ++ if (force_addr) ++ *address = force_addr; /* so detect will get called */ ++ ++ superio_exit(); ++ return 0; ++} ++ ++int vt1211_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct vt1211_data *data; ++ int err = 0; ++ u8 val; ++ const char *type_name = "vt1211"; ++ const char *client_name = "VT1211 chip"; ++ ++ if (!i2c_is_isa_adapter(adapter)) { ++ return 0; ++ } ++ ++ if(force_addr) ++ address = force_addr & ~(VT1211_EXTENT - 1); ++ if (check_region(address, VT1211_EXTENT)) { ++ printk("vt1211.o: region 0x%x already in use!\n", address); ++ return -ENODEV; ++ } ++ if(force_addr) { ++ printk("vt1211.o: forcing ISA address 0x%04X\n", address); ++ superio_enter(); ++ superio_select(); ++ superio_outb(VT1211_BASE_REG, address >> 8); ++ superio_outb(VT1211_BASE_REG+1, address & 0xff); ++ superio_exit(); ++ } ++ ++ superio_enter(); ++ superio_select(); ++ if((val = 0x01 & superio_inb(VT1211_ACT_REG)) == 0) ++ superio_outb(VT1211_ACT_REG, 1); ++ superio_exit(); ++ ++ if (!(data = kmalloc(sizeof(struct vt1211_data), GFP_KERNEL))) { ++ return -ENOMEM; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &vt1211_driver; ++ new_client->flags = 0; ++ ++ request_region(address, VT1211_EXTENT, "vt1211-sensors"); ++ strcpy(new_client->name, client_name); ++ ++ new_client->id = vt1211_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ vt1211_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ vt1211_init_client(new_client); ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, VT1211_EXTENT); ++ kfree(data); ++ return err; ++} ++ ++static int vt1211_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct vt1211_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("vt1211.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, VT1211_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++static inline int vt_rdval(struct i2c_client *client, u8 reg) ++{ ++ return (inb_p(client->addr + reg)); ++} ++ ++static inline void vt1211_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ outb_p(value, client->addr + reg); ++} ++ ++static void vt1211_init_client(struct i2c_client *client) ++{ ++ struct vt1211_data *data = client->data; ++ ++ data->vrm = DEFAULT_VRM; ++ /* set "default" interrupt mode for alarms, which isn't the default */ ++ vt1211_write_value(client, VT1211_REG_TEMP1_CONFIG, 0); ++ vt1211_write_value(client, VT1211_REG_TEMP2_CONFIG, 0); ++} ++ ++static void vt1211_update_client(struct i2c_client *client) ++{ ++ struct vt1211_data *data = client->data; ++ int i, j; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ data->uch_config = vt_rdval(client, VT1211_REG_UCH_CONFIG); ++ for (i = 0; i <= 5; i++) { ++ if(ISVOLT(i, data->uch_config)) { ++ data->in[i] = vt_rdval(client, VT1211_REG_IN(i)); ++ data->in_min[i] = vt_rdval(client, ++ VT1211_REG_IN_MIN(i)); ++ data->in_max[i] = vt_rdval(client, ++ VT1211_REG_IN_MAX(i)); ++ } else { ++ data->in[i] = 0; ++ data->in_min[i] = 0; ++ data->in_max[i] = 0; ++ } ++ } ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i)); ++ data->fan_min[i - 1] = vt_rdval(client, ++ VT1211_REG_FAN_MIN(i)); ++ } ++ for (i = 2; i <= 7; i++) { ++ if(ISTEMP(i, data->uch_config)) { ++ data->temp[i - 1] = vt_rdval(client, ++ VT1211_REG_TEMP(i)) << 2; ++ switch(i) { ++ case 1: ++ /* ? */ ++ j = 0; ++ break; ++ case 2: ++ j = (vt_rdval(client, ++ VT1211_REG_TEMP_LOW2) & ++ 0x30) >> 4; ++ break; ++ case 3: ++ j = (vt_rdval(client, ++ VT1211_REG_TEMP_LOW3) & ++ 0xc0) >> 6; ++ break; ++ case 4: ++ case 5: ++ case 6: ++ case 7: ++ default: ++ j = (vt_rdval(client, ++ VT1211_REG_TEMP_LOW47) >> ++ ((i-4)*2)) & 0x03; ++ break; ++ ++ } ++ data->temp[i - 1] |= j; ++ data->temp_over[i - 1] = vt_rdval(client, ++ VT1211_REG_TEMP_OVER(i)); ++ data->temp_hyst[i - 1] = vt_rdval(client, ++ VT1211_REG_TEMP_HYST(i)); ++ } else { ++ data->temp[i - 1] = 0; ++ data->temp_over[i - 1] = 0; ++ data->temp_hyst[i - 1] = 0; ++ } ++ } ++ ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i)); ++ data->fan_min[i - 1] = vt_rdval(client, ++ VT1211_REG_FAN_MIN(i)); ++ data->pwm[i - 1] = vt_rdval(client, VT1211_REG_PWM(i)); ++ } ++ ++ data->pwm_ctl = vt_rdval(client, VT1211_REG_PWM_CTL); ++ i = vt_rdval(client, VT1211_REG_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = vt_rdval(client, VT1211_REG_ALARM1) | ++ (vt_rdval(client, VT1211_REG_ALARM2) << 8); ++ data->vid= vt_rdval(client, VT1211_REG_VID) & 0x1f; ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void vt1211_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ int nr = ctl_name - VT1211_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ vt1211_write_value(client, VT1211_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ vt1211_write_value(client, VT1211_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void vt1211_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ int nr = ctl_name - VT1211_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div ++ [nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = MIN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr-1])); ++ vt1211_write_value(client, VT1211_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void vt1211_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ int nr = ctl_name - VT1211_SYSCTL_TEMP; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = TEMP_FROM_REG10(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over[nr] = TEMP_TO_REG(results[0]); ++ vt1211_write_value(client, ++ VT1211_REG_TEMP_OVER(nr + 1), ++ data->temp_over[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = TEMP_TO_REG(results[1]); ++ vt1211_write_value(client, ++ VT1211_REG_TEMP_HYST(nr + 1), ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void vt1211_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++void vt1211_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = vt_rdval(client, VT1211_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ vt1211_write_value(client, VT1211_REG_FANDIV, old); ++ } ++ } ++} ++ ++void vt1211_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ int nr = 1 + ctl_name - VT1211_SYSCTL_PWM1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr - 1]); ++ results[1] = (data->pwm_ctl >> (3 + (4 * (nr - 1)))) & 1; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->pwm[nr - 1] = PWM_TO_REG(results[0]); ++ if (*nrels_mag >= 2) { ++ if(results[1]) { ++ data->pwm_ctl |= ++ (0x08 << (4 * (nr - 1))); ++ vt1211_write_value(client, ++ VT1211_REG_PWM_CTL, ++ data->pwm_ctl); ++ } else { ++ data->pwm_ctl &= ++ ~ (0x08 << (4 * (nr - 1))); ++ vt1211_write_value(client, ++ VT1211_REG_PWM_CTL, ++ data->pwm_ctl); ++ } ++ } ++ vt1211_write_value(client, VT1211_REG_PWM(nr), ++ data->pwm[nr - 1]); ++ } ++ } ++} ++ ++void vt1211_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt1211_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void vt1211_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void vt1211_uch(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt1211_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->uch_config & 0x7c; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->uch_config = (data->uch_config & 0x83)|(results[0] & 0x7c); ++ vt1211_write_value(client, VT1211_REG_UCH_CONFIG, ++ data->uch_config); ++ } ++ } ++} ++ ++static int __init sm_vt1211_init(void) ++{ ++ int addr; ++ ++ printk("vt1211.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (vt1211_find(&addr)) { ++ printk("vt1211.o: VT1211 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ ++ return i2c_add_driver(&vt1211_driver); ++} ++ ++static void __exit sm_vt1211_exit(void) ++{ ++ i2c_del_driver(&vt1211_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Mark D. Studebaker "); ++MODULE_DESCRIPTION("VT1211 sensors"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_vt1211_init); ++module_exit(sm_vt1211_exit); +--- linux-old/drivers/sensors/vt8231.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/vt8231.c Mon Dec 13 20:18:53 2004 +@@ -0,0 +1,794 @@ ++/* ++ vt8231.c - Part of lm_sensors, Linux kernel modules ++ for hardware monitoring ++ ++ Copyright (c) 2002 Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* Supports VIA VT8231 South Bridge embedded sensors */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++ ++ ++static int force_addr = 0; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++SENSORS_INSMOD_1(vt8231); ++ ++#define VIA686A_EXTENT 0x80 ++#define VIA686A_BASE_REG 0x70 ++#define VIA686A_ENABLE_REG 0x74 ++ ++/* pwm numbered 1-2 */ ++#define VT8231_REG_PWM(nr) (0x5f + (nr)) ++#define VT8231_REG_PWM_CTL 0x51 ++ ++/* The VT8231 registers */ ++/* We define the sensors as follows. Somewhat convoluted to minimize ++ changes from via686a. ++ Sensor Voltage Mode Temp Mode ++ -------- ------------ --------- ++ Reading 1 temp3 ++ Reading 3 temp1 not in vt8231 ++ UCH1/Reading2 in0 temp2 ++ UCH2 in1 temp4 ++ UCH3 in2 temp5 ++ UCH4 in3 temp6 ++ UCH5 in4 temp7 ++ 3.3V in5 ++ -12V in6 not in vt8231 ++*/ ++ ++/* ins numbered 0-6 */ ++#define VT8231_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2)) ++#define VT8231_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2)) ++#define VT8231_REG_IN(nr) (0x21 + (nr)) ++ ++/* fans numbered 1-2 */ ++#define VT8231_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define VT8231_REG_FAN(nr) (0x28 + (nr)) ++ ++static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 }; ++static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 }; ++static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 }; ++ ++/* temps numbered 1-7 */ ++#define VT8231_REG_TEMP(nr) (regtemp[(nr) - 1]) ++#define VT8231_REG_TEMP_OVER(nr) (regover[(nr) - 1]) ++#define VT8231_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) ++#define VT8231_REG_TEMP_LOW3 0x4b /* bits 7-6 */ ++#define VT8231_REG_TEMP_LOW2 0x49 /* bits 5-4 */ ++#define VT8231_REG_TEMP_LOW47 0x4d ++ ++#define VT8231_REG_CONFIG 0x40 ++#define VT8231_REG_ALARM1 0x41 ++#define VT8231_REG_ALARM2 0x42 ++#define VT8231_REG_VID 0x45 ++#define VT8231_REG_FANDIV 0x47 ++#define VT8231_REG_UCH_CONFIG 0x4a ++#define VT8231_REG_TEMP1_CONFIG 0x4b ++#define VT8231_REG_TEMP2_CONFIG 0x4c ++ ++/* temps 1-7; voltages 0-6 */ ++#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \ ++ (i) == 3 ? 1 : \ ++ (i) == 2 ? ((ch_config) >> 1) & 0x01 : \ ++ ((ch_config) >> ((i)-1)) & 0x01) ++#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01)) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) ++#define PWM_FROM_REG(val) (val) ++#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255) ++ ++#define TEMP_FROM_REG(val) ((val)*10) ++#define TEMP_FROM_REG10(val) (((val)*10)/4) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),0,255)) ++#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val) ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255)) ++ ++ ++/********* FAN RPM CONVERSIONS ********/ ++/* But this chip saturates back at 0, not at 255 like all the other chips. ++ So, 0 means 0 RPM */ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 0; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1310720 + rpm * div / 2) / (rpm * div), 1, 255); ++} ++ ++#define MIN_TO_REG(a,b) FAN_TO_REG(a,b) ++#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div))) ++ ++struct vt8231_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 in[7]; /* Register value */ ++ u8 in_max[7]; /* Register value */ ++ u8 in_min[7]; /* Register value */ ++ u16 temp[7]; /* Register value 10 bit */ ++ u8 temp_over[7]; /* Register value */ ++ u8 temp_hyst[7]; /* Register value */ ++ u8 fan[2]; /* Register value */ ++ u8 fan_min[2]; /* Register value */ ++ u8 fan_div[2]; /* Register encoding, shifted right */ ++ u16 alarms; /* Register encoding */ ++ u8 pwm[2]; /* Register value */ ++ u8 pwm_ctl; /* Register value */ ++ u8 vid; /* Register encoding */ ++ u8 vrm; ++ u8 uch_config; ++}; ++ ++static int vt8231_attach_adapter(struct i2c_adapter *adapter); ++static int vt8231_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int vt8231_detach_client(struct i2c_client *client); ++ ++static inline int vt_rdval(struct i2c_client *client, u8 register); ++static inline void vt8231_write_value(struct i2c_client *client, u8 register, ++ u8 value); ++static void vt8231_update_client(struct i2c_client *client); ++static void vt8231_init_client(struct i2c_client *client); ++static int vt8231_find(int *address); ++ ++ ++static void vt8231_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_uch(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void vt8231_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int vt8231_id = 0; ++ ++static struct i2c_driver vt8231_driver = { ++ .owner = THIS_MODULE, ++ .name = "VT8231 sensors driver", ++ .id = I2C_DRIVERID_VT8231, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = vt8231_attach_adapter, ++ .detach_client = vt8231_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++#define VT8231_SYSCTL_IN0 1000 ++#define VT8231_SYSCTL_IN1 1001 ++#define VT8231_SYSCTL_IN2 1002 ++#define VT8231_SYSCTL_IN3 1003 ++#define VT8231_SYSCTL_IN4 1004 ++#define VT8231_SYSCTL_IN5 1005 ++#define VT8231_SYSCTL_IN6 1006 ++#define VT8231_SYSCTL_FAN1 1101 ++#define VT8231_SYSCTL_FAN2 1102 ++#define VT8231_SYSCTL_TEMP 1200 ++#define VT8231_SYSCTL_TEMP2 1201 ++#define VT8231_SYSCTL_TEMP3 1202 ++#define VT8231_SYSCTL_TEMP4 1203 ++#define VT8231_SYSCTL_TEMP5 1204 ++#define VT8231_SYSCTL_TEMP6 1205 ++#define VT8231_SYSCTL_TEMP7 1206 ++#define VT8231_SYSCTL_VID 1300 ++#define VT8231_SYSCTL_PWM1 1401 ++#define VT8231_SYSCTL_PWM2 1402 ++#define VT8231_SYSCTL_VRM 1600 ++#define VT8231_SYSCTL_UCH 1700 ++#define VT8231_SYSCTL_FAN_DIV 2000 ++#define VT8231_SYSCTL_ALARMS 2001 ++ ++#define VT8231_ALARM_IN1 0x01 ++#define VT8231_ALARM_IN2 0x02 ++#define VT8231_ALARM_IN5 0x04 ++#define VT8231_ALARM_IN3 0x08 ++#define VT8231_ALARM_TEMP 0x10 ++#define VT8231_ALARM_FAN1 0x40 ++#define VT8231_ALARM_FAN2 0x80 ++#define VT8231_ALARM_IN4 0x100 ++#define VT8231_ALARM_IN6 0x200 ++#define VT8231_ALARM_TEMP2 0x800 ++#define VT8231_ALARM_CHAS 0x1000 ++#define VT8231_ALARM_TEMP3 0x8000 ++/* duplicates */ ++#define VT8231_ALARM_IN0 VT8231_ALARM_TEMP ++#define VT8231_ALARM_TEMP4 VT8231_ALARM_IN1 ++#define VT8231_ALARM_TEMP5 VT8231_ALARM_IN2 ++#define VT8231_ALARM_TEMP6 VT8231_ALARM_IN3 ++#define VT8231_ALARM_TEMP7 VT8231_ALARM_IN4 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++static ctl_table vt8231_dir_table_template[] = { ++ {VT8231_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++/* ++ not in 8231 ++ {VT8231_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_in}, ++ {VT8231_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_temp}, ++*/ ++ {VT8231_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_TEMP7, "temp7", NULL, 0, 0644, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &vt8231_temp}, ++ {VT8231_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_fan}, ++ {VT8231_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_fan}, ++ {VT8231_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_fan_div}, ++ {VT8231_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_alarms}, ++ {VT8231_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_pwm}, ++ {VT8231_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_pwm}, ++ {VT8231_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_vid}, ++ {VT8231_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_vrm}, ++ {VT8231_SYSCTL_UCH, "uch_config", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &vt8231_uch}, ++ {0} ++}; ++ ++static struct pci_dev *s_bridge; ++ ++static int vt8231_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, vt8231_detect); ++} ++ ++/* Locate chip and get correct base address */ ++static int vt8231_find(int *address) ++{ ++ u16 val; ++ ++ if (!pci_present()) ++ return -ENODEV; ++ ++ if (!(s_bridge = pci_find_device(PCI_VENDOR_ID_VIA, ++ 0x8235, NULL))) ++ return -ENODEV; ++ ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(s_bridge, VIA686A_BASE_REG, &val)) ++ return -ENODEV; ++ *address = val & ~(VIA686A_EXTENT - 1); ++ if (*address == 0 && force_addr == 0) { ++ printk("vt8231.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); ++ return -ENODEV; ++ } ++ if (force_addr) ++ *address = force_addr; /* so detect will get called */ ++ ++ return 0; ++} ++ ++int vt8231_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct vt8231_data *data; ++ int err = 0; ++ const char *type_name = "vt8231"; ++ u16 val; ++ ++ if (!i2c_is_isa_adapter(adapter)) { ++ return 0; ++ } ++ ++ /* 8231 requires multiple of 256 */ ++ if(force_addr) ++ address = force_addr & 0xFF00; ++ if (check_region(address, VIA686A_EXTENT)) { ++ printk("vt8231.o: region 0x%x already in use!\n", ++ address); ++ return -ENODEV; ++ } ++ ++ if(force_addr) { ++ printk("vt8231.o: forcing ISA address 0x%04X\n", address); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) ++ return -ENODEV; ++ } ++ if (PCIBIOS_SUCCESSFUL != ++ pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) ++ return -ENODEV; ++ if (!(val & 0x0001)) { ++ printk("vt8231.o: enabling sensors\n"); ++ if (PCIBIOS_SUCCESSFUL != ++ pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, ++ val | 0x0001)) ++ return -ENODEV; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct vt8231_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &vt8231_driver; ++ new_client->flags = 0; ++ ++ /* Reserve the ISA region */ ++ request_region(address, VIA686A_EXTENT, "vt8231-sensors"); ++ ++ /* Fill in the remaining client fields and put into the global list */ ++ strcpy(new_client->name, "Via 8231 Integrated Sensors"); ++ ++ new_client->id = vt8231_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry((struct i2c_client *) new_client, ++ type_name, ++ vt8231_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR4; ++ } ++ data->sysctl_id = i; ++ ++ vt8231_init_client(new_client); ++ return 0; ++ ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, VIA686A_EXTENT); ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int vt8231_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct vt8231_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("vt8231.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, VIA686A_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++static inline int vt_rdval(struct i2c_client *client, u8 reg) ++{ ++ return (inb_p(client->addr + reg)); ++} ++ ++static inline void vt8231_write_value(struct i2c_client *client, u8 reg, u8 value) ++{ ++ outb_p(value, client->addr + reg); ++} ++ ++static void vt8231_init_client(struct i2c_client *client) ++{ ++ struct vt8231_data *data = client->data; ++ ++ data->vrm = DEFAULT_VRM; ++ /* set "default" interrupt mode for alarms, which isn't the default */ ++ vt8231_write_value(client, VT8231_REG_TEMP1_CONFIG, 0); ++ vt8231_write_value(client, VT8231_REG_TEMP2_CONFIG, 0); ++} ++ ++static void vt8231_update_client(struct i2c_client *client) ++{ ++ struct vt8231_data *data = client->data; ++ int i, j; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ data->uch_config = vt_rdval(client, VT8231_REG_UCH_CONFIG); ++ for (i = 0; i <= 5; i++) { ++ if(ISVOLT(i, data->uch_config)) { ++ data->in[i] = vt_rdval(client, VT8231_REG_IN(i)); ++ data->in_min[i] = vt_rdval(client, ++ VT8231_REG_IN_MIN(i)); ++ data->in_max[i] = vt_rdval(client, ++ VT8231_REG_IN_MAX(i)); ++ } else { ++ data->in[i] = 0; ++ data->in_min[i] = 0; ++ data->in_max[i] = 0; ++ } ++ } ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = vt_rdval(client, VT8231_REG_FAN(i)); ++ data->fan_min[i - 1] = vt_rdval(client, ++ VT8231_REG_FAN_MIN(i)); ++ } ++ for (i = 2; i <= 7; i++) { ++ if(ISTEMP(i, data->uch_config)) { ++ data->temp[i - 1] = vt_rdval(client, ++ VT8231_REG_TEMP(i)) << 2; ++ switch(i) { ++ case 1: ++ /* ? */ ++ j = 0; ++ break; ++ case 2: ++ j = (vt_rdval(client, ++ VT8231_REG_TEMP_LOW2) & ++ 0x30) >> 4; ++ break; ++ case 3: ++ j = (vt_rdval(client, ++ VT8231_REG_TEMP_LOW3) & ++ 0xc0) >> 6; ++ break; ++ case 4: ++ case 5: ++ case 6: ++ case 7: ++ default: ++ j = (vt_rdval(client, ++ VT8231_REG_TEMP_LOW47) >> ++ ((i-4)*2)) & 0x03; ++ break; ++ ++ } ++ data->temp[i - 1] |= j; ++ data->temp_over[i - 1] = vt_rdval(client, ++ VT8231_REG_TEMP_OVER(i)); ++ data->temp_hyst[i - 1] = vt_rdval(client, ++ VT8231_REG_TEMP_HYST(i)); ++ } else { ++ data->temp[i - 1] = 0; ++ data->temp_over[i - 1] = 0; ++ data->temp_hyst[i - 1] = 0; ++ } ++ } ++ ++ for (i = 1; i <= 2; i++) { ++ data->fan[i - 1] = vt_rdval(client, VT8231_REG_FAN(i)); ++ data->fan_min[i - 1] = vt_rdval(client, ++ VT8231_REG_FAN_MIN(i)); ++ data->pwm[i - 1] = vt_rdval(client, VT8231_REG_PWM(i)); ++ } ++ ++ data->pwm_ctl = vt_rdval(client, VT8231_REG_PWM_CTL); ++ i = vt_rdval(client, VT8231_REG_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = i >> 6; ++ data->alarms = vt_rdval(client, VT8231_REG_ALARM1) | ++ (vt_rdval(client, VT8231_REG_ALARM2) << 8); ++ data->vid= vt_rdval(client, VT8231_REG_VID) & 0x1f; ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++void vt8231_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ int nr = ctl_name - VT8231_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ vt8231_write_value(client, VT8231_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ vt8231_write_value(client, VT8231_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void vt8231_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ int nr = ctl_name - VT8231_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div ++ [nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = MIN_TO_REG(results[0], ++ DIV_FROM_REG ++ (data-> ++ fan_div[nr-1])); ++ vt8231_write_value(client, VT8231_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++ ++void vt8231_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ int nr = ctl_name - VT8231_SYSCTL_TEMP; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over[nr]); ++ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); ++ results[2] = TEMP_FROM_REG10(data->temp[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over[nr] = TEMP_TO_REG(results[0]); ++ vt8231_write_value(client, ++ VT8231_REG_TEMP_OVER(nr + 1), ++ data->temp_over[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst[nr] = TEMP_TO_REG(results[1]); ++ vt8231_write_value(client, ++ VT8231_REG_TEMP_HYST(nr + 1), ++ data->temp_hyst[nr]); ++ } ++ } ++} ++ ++void vt8231_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++void vt8231_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ int old; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = vt_rdval(client, VT8231_REG_FANDIV); ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = DIV_TO_REG(results[1]); ++ old = (old & 0x3f) | (data->fan_div[1] << 6); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = DIV_TO_REG(results[0]); ++ old = (old & 0xcf) | (data->fan_div[0] << 4); ++ vt8231_write_value(client, VT8231_REG_FANDIV, old); ++ } ++ } ++} ++ ++void vt8231_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ int nr = 1 + ctl_name - VT8231_SYSCTL_PWM1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr - 1]); ++ results[1] = (data->pwm_ctl >> (3 + (4 * (nr - 1)))) & 1; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->pwm[nr - 1] = PWM_TO_REG(results[0]); ++ if (*nrels_mag >= 2) { ++ if(results[1]) { ++ data->pwm_ctl |= ++ (0x08 << (4 * (nr - 1))); ++ vt8231_write_value(client, ++ VT8231_REG_PWM_CTL, ++ data->pwm_ctl); ++ } else { ++ data->pwm_ctl &= ++ ~ (0x08 << (4 * (nr - 1))); ++ vt8231_write_value(client, ++ VT8231_REG_PWM_CTL, ++ data->pwm_ctl); ++ } ++ } ++ vt8231_write_value(client, VT8231_REG_PWM(nr), ++ data->pwm[nr - 1]); ++ } ++ } ++} ++ ++void vt8231_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ vt8231_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void vt8231_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void vt8231_uch(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct vt8231_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->uch_config & 0x7c; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->uch_config = (data->uch_config & 0x83)|(results[0] & 0x7c); ++ vt8231_write_value(client, VT8231_REG_UCH_CONFIG, ++ data->uch_config); ++ } ++ } ++} ++ ++static int __init sm_vt8231_init(void) ++{ ++ int addr; ++ ++ printk("vt8231.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ ++ if (vt8231_find(&addr)) { ++ printk("vt8231.o: VT8231 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ ++ return i2c_add_driver(&vt8231_driver);} ++ ++static void __exit sm_vt8231_exit(void) ++{ ++ i2c_del_driver(&vt8231_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Mark D. Studebaker "); ++MODULE_DESCRIPTION("VT8231 sensors"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_vt8231_init); ++module_exit(sm_vt8231_exit); +--- linux-old/drivers/sensors/w83627hf.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/w83627hf.c Mon Dec 13 20:18:53 2004 +@@ -0,0 +1,1422 @@ ++/* ++ w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998 - 2003 Frodo Looijaard , ++ Philip Edelbrock , ++ and Mark Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports following chips: ++ ++ Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA ++ w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC) ++ w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) ++ w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC) ++ w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) ++ ++ For other winbond chips, and for i2c support in the above chips, ++ use w83781d.c. ++ ++ Note: automatic ("cruise") fan control for 697, 637 & 627thf not ++ supported yet. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++#include "lm75.h" ++ ++static int force_addr; ++MODULE_PARM(force_addr, "i"); ++MODULE_PARM_DESC(force_addr, ++ "Initialize the base address of the sensors"); ++static int force_i2c = 0x1f; ++MODULE_PARM(force_i2c, "i"); ++MODULE_PARM_DESC(force_i2c, ++ "Initialize the i2c address of the sensors"); ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf); ++ ++static int init = 1; ++MODULE_PARM(init, "i"); ++MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); ++ ++/* modified from kernel/include/traps.c */ ++#define REG 0x2e /* The register to read/write */ ++#define DEV 0x07 /* Register: Logical device select */ ++#define VAL 0x2f /* The value to read/write */ ++ ++/* logical device numbers for superio_select (below) */ ++#define W83627HF_LD_FDC 0x00 ++#define W83627HF_LD_PRT 0x01 ++#define W83627HF_LD_UART1 0x02 ++#define W83627HF_LD_UART2 0x03 ++#define W83627HF_LD_KBC 0x05 ++#define W83627HF_LD_CIR 0x06 /* w83627hf only */ ++#define W83627HF_LD_GAME 0x07 ++#define W83627HF_LD_MIDI 0x07 ++#define W83627HF_LD_GPIO1 0x07 ++#define W83627HF_LD_GPIO5 0x07 /* w83627thf only */ ++#define W83627HF_LD_GPIO2 0x08 ++#define W83627HF_LD_GPIO3 0x09 ++#define W83627HF_LD_GPIO4 0x09 /* w83627thf only */ ++#define W83627HF_LD_ACPI 0x0a ++#define W83627HF_LD_HWM 0x0b ++ ++#define DEVID 0x20 /* Register: Device ID */ ++ ++#define W83627THF_GPIO5_IOSR 0xf3 /* w83627thf only */ ++#define W83627THF_GPIO5_DR 0xf4 /* w83627thf only */ ++#define W83627THF_GPIO5_INVR 0xf5 /* w83627thf only */ ++ ++static inline void ++superio_outb(int reg, int val) ++{ ++ outb(reg, REG); ++ outb(val, VAL); ++} ++ ++static inline int ++superio_inb(int reg) ++{ ++ outb(reg, REG); ++ return inb(VAL); ++} ++ ++static inline void ++superio_select(int ld) ++{ ++ outb(DEV, REG); ++ outb(ld, VAL); ++} ++ ++static inline void ++superio_enter(void) ++{ ++ outb(0x87, REG); ++ outb(0x87, REG); ++} ++ ++static inline void ++superio_exit(void) ++{ ++ outb(0xAA, REG); ++} ++ ++#define W627_DEVID 0x52 ++#define W627THF_DEVID 0x82 ++#define W697_DEVID 0x60 ++#define W637_DEVID 0x70 ++#define WINB_ACT_REG 0x30 ++#define WINB_BASE_REG 0x60 ++/* Constants specified below */ ++ ++/* Length of ISA address segment */ ++#define WINB_EXTENT 8 ++ ++/* Where are the ISA address/data registers relative to the base address */ ++#define W83781D_ADDR_REG_OFFSET 5 ++#define W83781D_DATA_REG_OFFSET 6 ++ ++/* The W83781D registers */ ++/* The W83782D registers for nr=7,8 are in bank 5 */ ++#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ ++ (0x554 + (((nr) - 7) * 2))) ++#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ ++ (0x555 + (((nr) - 7) * 2))) ++#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ ++ (0x550 + (nr) - 7)) ++ ++#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) ++#define W83781D_REG_FAN(nr) (0x27 + (nr)) ++ ++#define W83781D_REG_TEMP2 0x0150 ++#define W83781D_REG_TEMP3 0x0250 ++#define W83781D_REG_TEMP2_HYST 0x153 ++#define W83781D_REG_TEMP3_HYST 0x253 ++#define W83781D_REG_TEMP2_CONFIG 0x152 ++#define W83781D_REG_TEMP3_CONFIG 0x252 ++#define W83781D_REG_TEMP2_OVER 0x155 ++#define W83781D_REG_TEMP3_OVER 0x255 ++ ++#define W83781D_REG_TEMP 0x27 ++#define W83781D_REG_TEMP_OVER 0x39 ++#define W83781D_REG_TEMP_HYST 0x3A ++#define W83781D_REG_BANK 0x4E ++ ++#define W83781D_REG_CONFIG 0x40 ++#define W83781D_REG_ALARM1 0x41 ++#define W83781D_REG_ALARM2 0x42 ++#define W83781D_REG_ALARM3 0x450 ++ ++#define W83781D_REG_IRQ 0x4C ++#define W83781D_REG_BEEP_CONFIG 0x4D ++#define W83781D_REG_BEEP_INTS1 0x56 ++#define W83781D_REG_BEEP_INTS2 0x57 ++#define W83781D_REG_BEEP_INTS3 0x453 ++ ++#define W83781D_REG_VID_FANDIV 0x47 ++ ++#define W83781D_REG_CHIPID 0x49 ++#define W83781D_REG_WCHIPID 0x58 ++#define W83781D_REG_CHIPMAN 0x4F ++#define W83781D_REG_PIN 0x4B ++ ++#define W83781D_REG_VBAT 0x5D ++ ++#define W83627HF_REG_PWM1 0x5A ++#define W83627HF_REG_PWM2 0x5B ++#define W83627HF_REG_PWMCLK12 0x5C ++ ++#define W83627THF_REG_PWM1 0x01 /* 697HF and 637HF too */ ++#define W83627THF_REG_PWM2 0x03 /* 697HF and 637HF too */ ++#define W83627THF_REG_PWM3 0x11 /* 637HF too */ ++ ++#define W83627THF_REG_VRM_OVT_CFG 0x18 /* 637HF too */ ++ ++static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 }; ++static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2, ++ W83627THF_REG_PWM3 }; ++#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \ ++ regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1]) ++ ++#define W83781D_REG_I2C_ADDR 0x48 ++#define W83781D_REG_I2C_SUBADDR 0x4A ++ ++/* Sensor selection */ ++#define W83781D_REG_SCFG1 0x5D ++static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; ++#define W83781D_REG_SCFG2 0x59 ++static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; ++#define W83781D_DEFAULT_BETA 3435 ++ ++/* Conversions. Limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++#define IN_TO_REG_VRM9(val) \ ++ (SENSORS_LIMIT((((val) * 1000 - 70000 + 244) / 488), 0, 255)) ++#define IN_FROM_REG_VRM9(reg) (((reg) * 488 + 70000 + 500) / 1000) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define TEMP_MIN (-1280) ++#define TEMP_MAX ( 1270) ++ ++/* TEMP: 1/10 degrees C (-128C to +127C) ++ REG: 1C/bit, two's complement */ ++static u8 TEMP_TO_REG(int temp) ++{ ++ int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX); ++ ntemp += (ntemp<0 ? -5 : 5); ++ return (u8)(ntemp / 10); ++} ++ ++static int TEMP_FROM_REG(u8 reg) ++{ ++ return (s8)reg * 10; ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) ++ ++#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) ++#define BEEPS_TO_REG(val) ((val) & 0xffffff) ++ ++#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) ++#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++static inline u8 DIV_TO_REG(long val) ++{ ++ int i; ++ val = SENSORS_LIMIT(val, 1, 128) >> 1; ++ for (i = 0; i < 6; i++) { ++ if (val == 0) ++ break; ++ val >>= 1; ++ } ++ return ((u8) i); ++} ++ ++/* For each registered chip, we need to keep some data in memory. That ++ data is pointed to by w83627hf_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new client is allocated. */ ++struct w83627hf_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ struct i2c_client *lm75; /* for secondary I2C addresses */ ++ /* pointer to array of 2 subclients */ ++ ++ u8 in[9]; /* Register value */ ++ u8 in_max[9]; /* Register value */ ++ u8 in_min[9]; /* Register value */ ++ u8 fan[3]; /* Register value */ ++ u8 fan_min[3]; /* Register value */ ++ u8 temp; ++ u8 temp_over; /* Register value */ ++ u8 temp_hyst; /* Register value */ ++ u16 temp_add[2]; /* Register value */ ++ u16 temp_add_over[2]; /* Register value */ ++ u16 temp_add_hyst[2]; /* Register value */ ++ u8 fan_div[3]; /* Register encoding, shifted right */ ++ u8 vid; /* Register encoding, combined */ ++ u32 alarms; /* Register encoding, combined */ ++ u32 beeps; /* Register encoding, combined */ ++ u8 beep_enable; /* Boolean */ ++ u8 pwm[3]; /* Register value */ ++ u8 pwmenable[3]; /* bool */ ++ u16 sens[3]; /* 782D/783S only. ++ 1 = pentium diode; 2 = 3904 diode; ++ 3000-5000 = thermistor beta. ++ Default = 3435. ++ Other Betas unimplemented */ ++ u8 vrm; ++ u8 vrm_ovt; /* Register value, 627thf & 637hf only */ ++}; ++ ++ ++static int w83627hf_attach_adapter(struct i2c_adapter *adapter); ++static int w83627hf_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int w83627hf_detach_client(struct i2c_client *client); ++ ++static int w83627hf_read_value(struct i2c_client *client, u16 register); ++static int w83627hf_write_value(struct i2c_client *client, u16 register, ++ u16 value); ++static void w83627hf_update_client(struct i2c_client *client); ++static void w83627hf_init_client(struct i2c_client *client); ++ ++ ++static void w83627hf_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_beep(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83627hf_sens(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++ ++static int w83627hf_id = 0; ++ ++static struct i2c_driver w83627hf_driver = { ++ .owner = THIS_MODULE, ++ .name = "W83627HF sensor driver", ++ .id = I2C_DRIVERID_W83627HF, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = w83627hf_attach_adapter, ++ .detach_client = w83627hf_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++/* WARNING these are copied from w83781d.c and have not been renamed. ++ Note that the 627hf and 697hf are supported by both drivers. ++ Do not make incompatible changes here or we will have errors ++ in the generated file ../include/sensors.h !!! ++*/ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define W83781D_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define W83781D_SYSCTL_IN1 1001 ++#define W83781D_SYSCTL_IN2 1002 ++#define W83781D_SYSCTL_IN3 1003 ++#define W83781D_SYSCTL_IN4 1004 ++#define W83781D_SYSCTL_IN5 1005 ++#define W83781D_SYSCTL_IN6 1006 ++#define W83781D_SYSCTL_IN7 1007 ++#define W83781D_SYSCTL_IN8 1008 ++#define W83781D_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define W83781D_SYSCTL_FAN2 1102 ++#define W83781D_SYSCTL_FAN3 1103 ++#define W83781D_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_VID 1300 /* Volts * 1000 */ ++#define W83781D_SYSCTL_VRM 1301 ++#define W83781D_SYSCTL_PWM1 1401 ++#define W83781D_SYSCTL_PWM2 1402 ++#define W83781D_SYSCTL_PWM3 1403 ++#define W83781D_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */ ++#define W83781D_SYSCTL_SENS2 1502 ++#define W83781D_SYSCTL_SENS3 1503 ++#define W83781D_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define W83781D_SYSCTL_ALARMS 2001 /* bitvector */ ++#define W83781D_SYSCTL_BEEP 2002 /* bitvector */ ++ ++#define W83781D_ALARM_IN0 0x0001 ++#define W83781D_ALARM_IN1 0x0002 ++#define W83781D_ALARM_IN2 0x0004 ++#define W83781D_ALARM_IN3 0x0008 ++#define W83781D_ALARM_IN4 0x0100 ++#define W83781D_ALARM_IN5 0x0200 ++#define W83781D_ALARM_IN6 0x0400 ++#define W83782D_ALARM_IN7 0x10000 ++#define W83782D_ALARM_IN8 0x20000 ++#define W83781D_ALARM_FAN1 0x0040 ++#define W83781D_ALARM_FAN2 0x0080 ++#define W83781D_ALARM_FAN3 0x0800 ++#define W83781D_ALARM_TEMP1 0x0010 ++#define W83781D_ALARM_TEMP23 0x0020 /* 781D only */ ++#define W83781D_ALARM_TEMP2 0x0020 /* 782D/783S */ ++#define W83781D_ALARM_TEMP3 0x2000 /* 782D only */ ++#define W83781D_ALARM_CHAS 0x1000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected chip. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++ ++/* without pwm3-4 */ ++static ctl_table w83627hf_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {0} ++}; ++ ++/* similar to w83782d but no fan3, no vid */ ++static ctl_table w83697hf_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ /* no in1 to maintain compatibility with 781d and 782d. */ ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp_add}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {0} ++}; ++ ++/* no in5 and in6 */ ++/* We use this one for W83637HF too */ ++static ctl_table w83627thf_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83627hf_sens}, ++ {0} ++}; ++ ++ ++/* This function is called when: ++ * w83627hf_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and w83627hf_driver is still present) */ ++static int w83627hf_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, w83627hf_detect); ++} ++ ++static int w83627hf_find(int *address) ++{ ++ u16 val; ++ ++ superio_enter(); ++ val= superio_inb(DEVID); ++ if(val != W627_DEVID && val !=W627THF_DEVID && val != W697_DEVID && val != W637_DEVID) { ++ superio_exit(); ++ return -ENODEV; ++ } ++ ++ superio_select(W83627HF_LD_HWM); ++ val = (superio_inb(WINB_BASE_REG) << 8) | ++ superio_inb(WINB_BASE_REG + 1); ++ *address = val & ~(WINB_EXTENT - 1); ++ if (*address == 0 && force_addr == 0) { ++ printk("w83627hf.o: base address not set - use force_addr=0xaddr\n"); ++ superio_exit(); ++ return -ENODEV; ++ } ++ if (force_addr) ++ *address = force_addr; /* so detect will get called */ ++ ++ superio_exit(); ++ return 0; ++} ++ ++int w83627hf_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, val; ++ struct i2c_client *new_client; ++ struct w83627hf_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ if (!i2c_is_isa_adapter(adapter)) ++ return 0; ++ ++ if(force_addr) ++ address = force_addr & ~(WINB_EXTENT - 1); ++ if (check_region(address, WINB_EXTENT)) { ++ printk("w83627hf.o: region 0x%x already in use!\n", address); ++ return -ENODEV; ++ } ++ if(force_addr) { ++ printk("w83627hf.o: forcing ISA address 0x%04X\n", address); ++ superio_enter(); ++ superio_select(W83627HF_LD_HWM); ++ superio_outb(WINB_BASE_REG, address >> 8); ++ superio_outb(WINB_BASE_REG+1, address & 0xff); ++ superio_exit(); ++ } ++ ++ superio_enter(); ++ val= superio_inb(DEVID); ++ if(val == W627_DEVID) ++ kind = w83627hf; ++ else if(val == W697_DEVID) ++ kind = w83697hf; ++ else if(val == W627THF_DEVID) ++ kind = w83627thf; ++ else if(val == W637_DEVID) ++ kind = w83637hf; ++ ++ superio_select(W83627HF_LD_HWM); ++ if((val = 0x01 & superio_inb(WINB_ACT_REG)) == 0) ++ superio_outb(WINB_ACT_REG, 1); ++ superio_exit(); ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access w83627hf_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &w83627hf_driver; ++ new_client->flags = 0; ++ ++ ++ if (kind == w83627hf) { ++ type_name = "w83627hf"; ++ client_name = "W83627HF chip"; ++ } else if (kind == w83627thf) { ++ type_name = "w83627thf"; ++ client_name = "W83627THF chip"; ++ } else if (kind == w83697hf) { ++ type_name = "w83697hf"; ++ client_name = "W83697HF chip"; ++ } else if (kind == w83637hf) { ++ type_name = "w83637hf"; ++ client_name = "W83637HF chip"; ++ } else { ++ goto ERROR1; ++ } ++ ++ request_region(address, WINB_EXTENT, type_name); ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ new_client->id = w83627hf_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ data->lm75 = NULL; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ (kind == w83697hf) ? ++ w83697hf_dir_table_template : ++ (kind == w83627hf) ? ++ w83627hf_dir_table_template : ++ /* w83627thf table also used for 637 */ ++ w83627thf_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR7; ++ } ++ data->sysctl_id = i; ++ ++ /* Initialize the chip */ ++ w83627hf_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR7: ++ i2c_detach_client(new_client); ++ ERROR3: ++ release_region(address, WINB_EXTENT); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int w83627hf_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct w83627hf_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ (KERN_ERR "w83627hf.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ release_region(client->addr, WINB_EXTENT); ++ kfree(client->data); ++ ++ return 0; ++} ++ ++ ++/* ++ ISA access must always be locked explicitly! ++ We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the W83781D access and should not be necessary. ++ There are some ugly typecasts here, but the good news is - they should ++ nowhere else be necessary! */ ++static int w83627hf_read_value(struct i2c_client *client, u16 reg) ++{ ++ int res, word_sized; ++ ++ down(&(((struct w83627hf_data *) (client->data))->lock)); ++ word_sized = (((reg & 0xff00) == 0x100) ++ || ((reg & 0xff00) == 0x200)) ++ && (((reg & 0x00ff) == 0x50) ++ || ((reg & 0x00ff) == 0x53) ++ || ((reg & 0x00ff) == 0x55)); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(reg >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); ++ res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); ++ if (word_sized) { ++ outb_p((reg & 0xff) + 1, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ res = ++ (res << 8) + inb_p(client->addr + ++ W83781D_DATA_REG_OFFSET); ++ } ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ up(&(((struct w83627hf_data *) (client->data))->lock)); ++ return res; ++} ++ ++static int w83627thf_read_gpio5(struct i2c_client *client) ++{ ++ int res, inv; ++ ++ down(&(((struct w83627hf_data *) (client->data))->lock)); ++ superio_enter(); ++ superio_select(W83627HF_LD_GPIO5); ++ res = superio_inb(W83627THF_GPIO5_DR); ++ inv = superio_inb(W83627THF_GPIO5_INVR); ++ superio_exit(); ++ up(&(((struct w83627hf_data *) (client->data))->lock)); ++ return res; ++} ++ ++static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value) ++{ ++ int word_sized; ++ ++ down(&(((struct w83627hf_data *) (client->data))->lock)); ++ word_sized = (((reg & 0xff00) == 0x100) ++ || ((reg & 0xff00) == 0x200)) ++ && (((reg & 0x00ff) == 0x53) ++ || ((reg & 0x00ff) == 0x55)); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(reg >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); ++ if (word_sized) { ++ outb_p(value >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ outb_p((reg & 0xff) + 1, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ } ++ outb_p(value & 0xff, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ up(&(((struct w83627hf_data *) (client->data))->lock)); ++ return 0; ++} ++ ++/* Called when we have found a new W83781D. It should set limits, etc. */ ++static void w83627hf_init_client(struct i2c_client *client) ++{ ++ struct w83627hf_data *data = client->data; ++ int i; ++ int type = data->type; ++ u8 tmp; ++ ++ /* Minimize conflicts with other winbond i2c-only clients... */ ++ /* disable i2c subclients... how to disable main i2c client?? */ ++ /* force i2c address to relatively uncommon address */ ++ w83627hf_write_value(client, W83781D_REG_I2C_SUBADDR, 0x89); ++ w83627hf_write_value(client, W83781D_REG_I2C_ADDR, force_i2c); ++ ++ /* Read VID only once */ ++ if (w83627hf == data->type || w83637hf == data->type) { ++ int lo = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); ++ int hi = w83627hf_read_value(client, W83781D_REG_CHIPID); ++ data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); ++ } else if (w83627thf == data->type) { ++ data->vid = w83627thf_read_gpio5(client) & 0x1f; ++ } ++ ++ /* Read VRM & OVT Config only once */ ++ if (w83627thf == data->type || w83637hf == data->type) ++ data->vrm_ovt = ++ w83627hf_read_value(client, W83627THF_REG_VRM_OVT_CFG); ++ else ++ data->vrm_ovt = 0; ++ ++ /* Choose VRM based on "VRM & OVT" register */ ++ data->vrm = (data->vrm_ovt & 0x01) ? 90 : 82; ++ ++ tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); ++ for (i = 1; i <= 3; i++) { ++ if (!(tmp & BIT_SCFG1[i - 1])) { ++ data->sens[i - 1] = W83781D_DEFAULT_BETA; ++ } else { ++ if (w83627hf_read_value ++ (client, ++ W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) ++ data->sens[i - 1] = 1; ++ else ++ data->sens[i - 1] = 2; ++ } ++ if ((type == w83697hf) && (i == 2)) ++ break; ++ } ++ ++ data->pwmenable[0] = 1; ++ data->pwmenable[1] = 1; ++ data->pwmenable[2] = 1; ++ ++ if(init) { ++ if (type == w83627hf) { ++ /* enable PWM2 control (can't hurt since PWM reg ++ should have been reset to 0xff) */ ++ w83627hf_write_value(client, W83627HF_REG_PWMCLK12, 0x19); ++ } ++ /* enable comparator mode for temp2 and temp3 so ++ alarm indication will work correctly */ ++ i = w83627hf_read_value(client, W83781D_REG_IRQ); ++ if (!(i & 0x40)) ++ w83627hf_write_value(client, W83781D_REG_IRQ, ++ i | 0x40); ++ } ++ ++ /* Start monitoring */ ++ w83627hf_write_value(client, W83781D_REG_CONFIG, ++ (w83627hf_read_value(client, ++ W83781D_REG_CONFIG) & 0xf7) ++ | 0x01); ++} ++ ++static void w83627hf_update_client(struct i2c_client *client) ++{ ++ struct w83627hf_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ for (i = 0; i <= 8; i++) { ++ /* skip missing sensors */ ++ if (((data->type == w83697hf) && (i == 1)) || ++ ((data->type == w83627thf || data->type == w83637hf) && ++ (i == 4 || i == 5))) ++ continue; ++ data->in[i] = ++ w83627hf_read_value(client, W83781D_REG_IN(i)); ++ data->in_min[i] = ++ w83627hf_read_value(client, ++ W83781D_REG_IN_MIN(i)); ++ data->in_max[i] = ++ w83627hf_read_value(client, ++ W83781D_REG_IN_MAX(i)); ++ } ++ for (i = 1; i <= 3; i++) { ++ data->fan[i - 1] = ++ w83627hf_read_value(client, W83781D_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ w83627hf_read_value(client, ++ W83781D_REG_FAN_MIN(i)); ++ } ++ for (i = 1; i <= 3; i++) { ++ u8 tmp = w83627hf_read_value(client, ++ W836X7HF_REG_PWM(data->type, i)); ++ if (data->type == w83627thf) ++ tmp &= 0xf0; /* bits 0-3 are reserved in 627THF */ ++ data->pwm[i - 1] = tmp; ++ if(i == 2 && (data->type == w83627hf || data->type == w83697hf)) ++ break; ++ } ++ ++ data->temp = w83627hf_read_value(client, W83781D_REG_TEMP); ++ data->temp_over = ++ w83627hf_read_value(client, W83781D_REG_TEMP_OVER); ++ data->temp_hyst = ++ w83627hf_read_value(client, W83781D_REG_TEMP_HYST); ++ data->temp_add[0] = ++ w83627hf_read_value(client, W83781D_REG_TEMP2); ++ data->temp_add_over[0] = ++ w83627hf_read_value(client, W83781D_REG_TEMP2_OVER); ++ data->temp_add_hyst[0] = ++ w83627hf_read_value(client, W83781D_REG_TEMP2_HYST); ++ if (data->type != w83697hf) { ++ data->temp_add[1] = ++ w83627hf_read_value(client, W83781D_REG_TEMP3); ++ data->temp_add_over[1] = ++ w83627hf_read_value(client, W83781D_REG_TEMP3_OVER); ++ data->temp_add_hyst[1] = ++ w83627hf_read_value(client, W83781D_REG_TEMP3_HYST); ++ } ++ ++ i = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = (i >> 6) & 0x03; ++ if (data->type != w83697hf) { ++ data->fan_div[2] = (w83627hf_read_value(client, ++ W83781D_REG_PIN) >> 6) & 0x03; ++ } ++ i = w83627hf_read_value(client, W83781D_REG_VBAT); ++ data->fan_div[0] |= (i >> 3) & 0x04; ++ data->fan_div[1] |= (i >> 4) & 0x04; ++ if (data->type != w83697hf) ++ data->fan_div[2] |= (i >> 5) & 0x04; ++ data->alarms = ++ w83627hf_read_value(client, W83781D_REG_ALARM1) | ++ (w83627hf_read_value(client, W83781D_REG_ALARM2) << 8) | ++ (w83627hf_read_value(client, W83781D_REG_ALARM3) << 16); ++ i = w83627hf_read_value(client, W83781D_REG_BEEP_INTS2); ++ data->beep_enable = i >> 7; ++ data->beeps = ((i & 0x7f) << 8) | ++ w83627hf_read_value(client, W83781D_REG_BEEP_INTS1) | ++ w83627hf_read_value(client, W83781D_REG_BEEP_INTS3) << 16; ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++void w83627hf_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ ++ if (nr == 0 && (data->vrm_ovt & 0x01)) { ++ /* use VRM9 calculation */ ++ results[0] = IN_FROM_REG_VRM9(data->in_min[0]); ++ results[1] = IN_FROM_REG_VRM9(data->in_max[0]); ++ results[2] = IN_FROM_REG_VRM9(data->in[0]); ++ ++ } else { ++ /* use VRM8 (standard) calculation */ ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ } ++ ++ *nrels_mag = 3; ++ ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (nr == 0 && (data->vrm_ovt & 0x01)) ++ /* use VRM9 calculation */ ++ data->in_min[0] = IN_TO_REG_VRM9(results[0]); ++ else ++ /* use VRM8 (standard) calculation */ ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ ++ w83627hf_write_value(client, W83781D_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ if (nr == 0 && (data->vrm_ovt & 0x01)) ++ /* use VRM9 calculation */ ++ data->in_min[0] = IN_TO_REG_VRM9(results[1]); ++ else ++ /* use VRM8 (standard) calculation */ ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ ++ w83627hf_write_value(client, W83781D_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void w83627hf_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = ++ FAN_TO_REG(results[0], ++ DIV_FROM_REG(data->fan_div[nr-1])); ++ w83627hf_write_value(client, ++ W83781D_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++void w83627hf_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ w83627hf_write_value(client, W83781D_REG_TEMP_OVER, ++ data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ w83627hf_write_value(client, W83781D_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void w83627hf_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_TEMP2; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = ++ LM75_TEMP_FROM_REG(data->temp_add_over[nr]); ++ results[1] = ++ LM75_TEMP_FROM_REG(data->temp_add_hyst[nr]); ++ results[2] = LM75_TEMP_FROM_REG(data->temp_add[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_add_over[nr] = ++ LM75_TEMP_TO_REG(results[0]); ++ w83627hf_write_value(client, ++ nr ? W83781D_REG_TEMP3_OVER : ++ W83781D_REG_TEMP2_OVER, ++ data->temp_add_over[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_add_hyst[nr] = ++ LM75_TEMP_TO_REG(results[1]); ++ w83627hf_write_value(client, ++ nr ? W83781D_REG_TEMP3_HYST : ++ W83781D_REG_TEMP2_HYST, ++ data->temp_add_hyst[nr]); ++ } ++ } ++} ++ ++void w83627hf_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void w83627hf_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void w83627hf_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = data->alarms; ++ *nrels_mag = 1; ++ } ++} ++ ++void w83627hf_beep(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int val; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); ++ results[1] = data->beeps; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 2) { ++ data->beeps = BEEPS_TO_REG(results[1]); ++ w83627hf_write_value(client, W83781D_REG_BEEP_INTS1, ++ data->beeps & 0xff); ++ w83627hf_write_value(client, ++ W83781D_REG_BEEP_INTS3, ++ ((data-> beeps) >> 16) & ++ 0xff); ++ val = (data->beeps >> 8) & 0x7f; ++ } else if (*nrels_mag >= 1) ++ val = ++ w83627hf_read_value(client, ++ W83781D_REG_BEEP_INTS2) & ++ 0x7f; ++ if (*nrels_mag >= 1) { ++ data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); ++ w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, ++ val | data->beep_enable << 7); ++ } ++ } ++} ++ ++/* w83697hf only has two fans */ ++void w83627hf_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int old, old2, old3 = 0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ if (data->type == w83697hf) { ++ *nrels_mag = 2; ++ } else { ++ results[2] = DIV_FROM_REG(data->fan_div[2]); ++ *nrels_mag = 3; ++ } ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); ++ /* w83627hf doesn't have extended divisor bits */ ++ old3 = ++ w83627hf_read_value(client, W83781D_REG_VBAT); ++ if (*nrels_mag >= 3 && data->type != w83697hf) { ++ data->fan_div[2] = ++ DIV_TO_REG(results[2]); ++ old2 = w83627hf_read_value(client, W83781D_REG_PIN); ++ old2 = ++ (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); ++ w83627hf_write_value(client, W83781D_REG_PIN, old2); ++ old3 = ++ (old3 & 0x7f) | ++ ((data->fan_div[2] & 0x04) << 5); ++ } ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = ++ DIV_TO_REG(results[1]); ++ old = ++ (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); ++ old3 = ++ (old3 & 0xbf) | ++ ((data->fan_div[1] & 0x04) << 4); ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = ++ DIV_TO_REG(results[0]); ++ old = ++ (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); ++ w83627hf_write_value(client, W83781D_REG_VID_FANDIV, ++ old); ++ old3 = ++ (old3 & 0xdf) | ++ ((data->fan_div[0] & 0x04) << 3); ++ w83627hf_write_value(client, ++ W83781D_REG_VBAT, ++ old3); ++ } ++ } ++} ++ ++/* we do not currently support disabling PWM with 2nd argument; ++ set first argument to 255 to disable */ ++void w83627hf_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83627hf_update_client(client); ++ results[0] = data->pwm[nr - 1]; ++ results[1] = data->pwmenable[nr - 1]; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (data->type == w83627thf) { ++ /* bits 0-3 are reserved in 627THF */ ++ data->pwm[nr - 1] = PWM_TO_REG(results[0]) & 0xf0; ++ w83627hf_write_value(client, ++ W836X7HF_REG_PWM(data->type, nr), ++ data->pwm[nr - 1] | ++ (w83627hf_read_value(client, ++ W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); ++ } else { ++ data->pwm[nr - 1] = PWM_TO_REG(results[0]); ++ w83627hf_write_value(client, ++ W836X7HF_REG_PWM(data->type, nr), ++ data->pwm[nr - 1]); ++ } ++ } ++ } ++} ++ ++void w83627hf_sens(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83627hf_data *data = client->data; ++ int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1; ++ u8 tmp; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->sens[nr - 1]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ switch (results[0]) { ++ case 1: /* PII/Celeron diode */ ++ tmp = w83627hf_read_value(client, ++ W83781D_REG_SCFG1); ++ w83627hf_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp | BIT_SCFG1[nr - ++ 1]); ++ tmp = w83627hf_read_value(client, ++ W83781D_REG_SCFG2); ++ w83627hf_write_value(client, ++ W83781D_REG_SCFG2, ++ tmp | BIT_SCFG2[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ case 2: /* 3904 */ ++ tmp = w83627hf_read_value(client, ++ W83781D_REG_SCFG1); ++ w83627hf_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp | BIT_SCFG1[nr - ++ 1]); ++ tmp = w83627hf_read_value(client, ++ W83781D_REG_SCFG2); ++ w83627hf_write_value(client, ++ W83781D_REG_SCFG2, ++ tmp & ~BIT_SCFG2[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ case W83781D_DEFAULT_BETA: /* thermistor */ ++ tmp = w83627hf_read_value(client, ++ W83781D_REG_SCFG1); ++ w83627hf_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp & ~BIT_SCFG1[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ default: ++ printk ++ (KERN_ERR "w83627hf.o: Invalid sensor type %ld; must be 1, 2, or %d\n", ++ results[0], W83781D_DEFAULT_BETA); ++ break; ++ } ++ } ++ } ++} ++ ++static int __init sm_w83627hf_init(void) ++{ ++ int addr; ++ ++ printk(KERN_INFO "w83627hf.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ if (w83627hf_find(&addr)) { ++ printk("w83627hf.o: W83627/697 not detected, module not inserted.\n"); ++ return -ENODEV; ++ } ++ normal_isa[0] = addr; ++ ++ return i2c_add_driver(&w83627hf_driver); ++} ++ ++static void __exit sm_w83627hf_exit(void) ++{ ++ i2c_del_driver(&w83627hf_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "and Mark Studebaker "); ++MODULE_DESCRIPTION("W83627HF driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_w83627hf_init); ++module_exit(sm_w83627hf_exit); +--- linux-old/drivers/sensors/w83781d.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/w83781d.c Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,1957 @@ ++/* ++ w83781d.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998 - 2003 Frodo Looijaard , ++ Philip Edelbrock , ++ and Mark Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ Supports following chips: ++ ++ Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA ++ as99127f 7 3 0 3 0x31 0x12c3 yes no ++ as99127f rev.2 (type name = as99127f) 0x31 0x5ca3 yes no ++ w83627hf 9 3 2 3 0x21 0x5ca3 yes yes(LPC) ++ w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) ++ w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes ++ w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes ++ w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no ++ w83791d 10 5 5 3 0x71 0x5ca3 yes no ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++#include ++#include "lm75.h" ++ ++/* RT Table support #defined so we can take it out if it gets bothersome */ ++#define W83781D_RT 1 ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_7(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf, w83791d); ++SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \ ++ "{bus, clientaddr, subclientaddr1, subclientaddr2}"); ++ ++static int init = 1; ++MODULE_PARM(init, "i"); ++MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); ++ ++/* Constants specified below */ ++ ++/* Length of ISA address segment */ ++#define W83781D_EXTENT 8 ++ ++/* Where are the ISA address/data registers relative to the base address */ ++#define W83781D_ADDR_REG_OFFSET 5 ++#define W83781D_DATA_REG_OFFSET 6 ++ ++/* The W83781D registers */ ++/* The W83782D registers for nr=7,8 are in bank 5 */ ++#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ ++ (0x554 + (((nr) - 7) * 2))) ++#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ ++ (0x555 + (((nr) - 7) * 2))) ++#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ ++ (0x550 + (nr) - 7)) ++ ++#define W83791D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ ++ (0xb4 + (((nr) - 7) * 2))) ++#define W83791D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ ++ (0xb5 + (((nr) - 7) * 2))) ++#define W83791D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ ++ (0xb0 + (nr) - 7)) ++ ++#define W83781D_REG_FAN_MIN(nr) ((nr < 4) ? (0x3a + (nr)) : \ ++ (0xba + (nr) - 4)) ++#define W83781D_REG_FAN(nr) ((nr < 4) ? (0x27 + (nr)) : \ ++ (0xbc + (nr) - 4)) ++ ++#define W83781D_REG_TEMP2 0x0150 ++#define W83781D_REG_TEMP3 0x0250 ++#define W83781D_REG_TEMP2_HYST 0x153 ++#define W83781D_REG_TEMP3_HYST 0x253 ++#define W83781D_REG_TEMP2_CONFIG 0x152 ++#define W83781D_REG_TEMP3_CONFIG 0x252 ++#define W83781D_REG_TEMP2_OVER 0x155 ++#define W83781D_REG_TEMP3_OVER 0x255 ++ ++#define W83781D_REG_TEMP 0x27 ++#define W83781D_REG_TEMP_OVER 0x39 ++#define W83781D_REG_TEMP_HYST 0x3A ++#define W83781D_REG_BANK 0x4E ++ ++#define W83781D_REG_CONFIG 0x40 ++#define W83781D_REG_ALARM1 0x41 ++#define W83781D_REG_ALARM2 0x42 ++#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ ++ ++#define W83781D_REG_IRQ 0x4C ++#define W83781D_REG_BEEP_CONFIG 0x4D ++#define W83781D_REG_BEEP_INTS1 0x56 ++#define W83781D_REG_BEEP_INTS2 0x57 ++#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ ++ ++#define W83781D_REG_VID_FANDIV 0x47 ++ ++#define W83781D_REG_CHIPID 0x49 ++#define W83781D_REG_WCHIPID 0x58 ++#define W83781D_REG_CHIPMAN 0x4F ++#define W83781D_REG_PIN 0x4B ++ ++/* 782D/783S only */ ++#define W83781D_REG_VBAT 0x5D ++ ++/* PWM 782D (1-4) and 783S (1-2) only */ ++#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ ++ /* on which is which; */ ++#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ ++ /* However 782d is probably wrong. */ ++#define W83781D_REG_PWM3 0x5E ++#define W83781D_REG_PWM4 0x5F ++#define W83781D_REG_PWMCLK12 0x5C ++#define W83781D_REG_PWMCLK34 0x45C ++ ++#define W83791D_REG_PWM1 0x81 ++#define W83791D_REG_PWM2 0x83 ++#define W83791D_REG_PWM3 0x94 ++ ++#define W83627HF_REG_PWM1 0x01 ++#define W83627HF_REG_PWM2 0x03 ++#define W83627HF_REG_PWMCLK1 0x00 ++#define W83627HF_REG_PWMCLK2 0x02 ++ ++static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, ++ W83781D_REG_PWM3, W83781D_REG_PWM4 ++}; ++ ++static const u8 regpwm_w83791d[] = { W83791D_REG_PWM1, W83791D_REG_PWM2, ++ W83791D_REG_PWM3 ++}; ++ ++#define W83781D_REG_PWM(type, nr) (((type) == w83791d) ? \ ++ regpwm_w83791d[(nr) - 1] : \ ++ ((type) == w83697hf) ? \ ++ (((nr) * 2) - 1) : \ ++ regpwm[(nr) - 1]) ++ ++#define W83781D_REG_I2C_ADDR 0x48 ++#define W83781D_REG_I2C_SUBADDR 0x4A ++ ++/* The following are undocumented in the data sheets however we ++ received the information in an email from Winbond tech support */ ++/* Sensor selection - not on 781d */ ++#define W83781D_REG_SCFG1 0x5D ++static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; ++#define W83781D_REG_SCFG2 0x59 ++static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; ++#define W83781D_DEFAULT_BETA 3435 ++ ++/* RT Table registers */ ++#define W83781D_REG_RT_IDX 0x50 ++#define W83781D_REG_RT_VAL 0x51 ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) ++#define IN_FROM_REG(val) (((val) * 16 + 5) / 10) ++ ++static inline u8 FAN_TO_REG(long rpm, int div) ++{ ++ if (rpm == 0) ++ return 255; ++ rpm = SENSORS_LIMIT(rpm, 1, 1000000); ++ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, ++ 254); ++} ++ ++#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) ++ ++#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ ++ ((val)+5)/10),0,255)) ++#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) ++ ++#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val) + 2)*4)/10) \ ++ << 7),0,0xffff)) ++#define AS99127_TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 10) / 4) ++ ++#define ALARMS_FROM_REG(val) (val) ++#define PWM_FROM_REG(val) (val) ++#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) ++#define BEEPS_FROM_REG(val,type) ((type)==as99127f?(val)^0x7FFF:(val)) ++#define BEEPS_TO_REG(val,type) ((type)==as99127f?(~(val))&0x7FFF:(val)&0xffffff) ++ ++#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) ++#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) ++ ++#define DIV_FROM_REG(val) (1 << (val)) ++ ++static inline u8 DIV_TO_REG(long val, enum chips type) ++{ ++ int i; ++ val = SENSORS_LIMIT(val, 1, ++ ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1; ++ for (i = 0; i < 6; i++) { ++ if (val == 0) ++ break; ++ val >>= 1; ++ } ++ return ((u8) i); ++} ++ ++/* There are some complications in a module like this. First off, W83781D chips ++ may be both present on the SMBus and the ISA bus, and we have to handle ++ those cases separately at some places. Second, there might be several ++ W83781D chips available (well, actually, that is probably never done; but ++ it is a clean illustration of how to handle a case like that). Finally, ++ a specific chip may be attached to *both* ISA and SMBus, and we would ++ not like to detect it double. Fortunately, in the case of the W83781D at ++ least, a register tells us what SMBus address we are on, so that helps ++ a bit - except if there could be more than one SMBus. Groan. No solution ++ for this yet. */ ++ ++/* This module may seem overly long and complicated. In fact, it is not so ++ bad. Quite a lot of bookkeeping is done. A real driver can often cut ++ some corners. */ ++ ++/* For each registered W83781D, we need to keep some data in memory. That ++ data is pointed to by w83781d_list[NR]->data. The structure itself is ++ dynamically allocated, at the same time when a new w83781d client is ++ allocated. */ ++struct w83781d_data { ++ struct i2c_client client; ++ struct semaphore lock; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ struct i2c_client *lm75; /* for secondary I2C addresses */ ++ /* pointer to array of 2 subclients */ ++ ++ u8 in[10]; /* Register value - 8 & 9 for 782D and 791D only 10 for 791D */ ++ u8 in_max[10]; /* Register value - 8 & 9 for 782D and 791D only 10 for 791D */ ++ u8 in_min[10]; /* Register value - 8 & 9 for 782D and 791D only 10 for 791D */ ++ u8 fan[5]; /* Register value - 4 & 5 for 791D only */ ++ u8 fan_min[5]; /* Register value - 4 & 5 for 791D only */ ++ u8 temp; ++ u8 temp_over; /* Register value */ ++ u8 temp_hyst; /* Register value */ ++ u16 temp_add[2]; /* Register value */ ++ u16 temp_add_over[2]; /* Register value */ ++ u16 temp_add_hyst[2]; /* Register value */ ++ u8 fan_div[3]; /* Register encoding, shifted right */ ++ u8 vid; /* Register encoding, combined */ ++ u32 alarms; /* Register encoding, combined */ ++ u32 beeps; /* Register encoding, combined */ ++ u8 beep_enable; /* Boolean */ ++ u8 pwm[4]; /* Register value */ ++ u8 pwmenable[4]; /* bool */ ++ u16 sens[3]; /* 782D/783S only. ++ 1 = pentium diode; 2 = 3904 diode; ++ 3000-5000 = thermistor beta. ++ Default = 3435. ++ Other Betas unimplemented */ ++#ifdef W83781D_RT ++ u8 rt[3][32]; /* Register value */ ++#endif ++ u8 vrm; ++}; ++ ++ ++static int w83781d_attach_adapter(struct i2c_adapter *adapter); ++static int w83781d_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static int w83781d_detach_client(struct i2c_client *client); ++ ++static int w83781d_read_value(struct i2c_client *client, u16 register); ++static int w83781d_write_value(struct i2c_client *client, u16 register, ++ u16 value); ++static void w83781d_update_client(struct i2c_client *client); ++static void w83781d_init_client(struct i2c_client *client); ++ ++ ++static void w83781d_in(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_fan(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_vid(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_vrm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_beep(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_pwm(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void w83781d_sens(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++#ifdef W83781D_RT ++static void w83781d_rt(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++#endif ++ ++static int w83781d_id = 0; ++ ++static struct i2c_driver w83781d_driver = { ++ .owner = THIS_MODULE, ++ .name = "W83781D sensor driver", ++ .id = I2C_DRIVERID_W83781D, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = w83781d_attach_adapter, ++ .detach_client = w83781d_detach_client, ++}; ++ ++/* The /proc/sys entries */ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define W83781D_SYSCTL_IN0 1000 /* Volts * 100 */ ++#define W83781D_SYSCTL_IN1 1001 ++#define W83781D_SYSCTL_IN2 1002 ++#define W83781D_SYSCTL_IN3 1003 ++#define W83781D_SYSCTL_IN4 1004 ++#define W83781D_SYSCTL_IN5 1005 ++#define W83781D_SYSCTL_IN6 1006 ++#define W83781D_SYSCTL_IN7 1007 ++#define W83781D_SYSCTL_IN8 1008 ++#define W83781D_SYSCTL_IN9 1009 ++#define W83781D_SYSCTL_FAN1 1101 /* Rotations/min */ ++#define W83781D_SYSCTL_FAN2 1102 ++#define W83781D_SYSCTL_FAN3 1103 ++#define W83781D_SYSCTL_FAN4 1104 ++#define W83781D_SYSCTL_FAN5 1105 ++ ++#define W83781D_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ ++#define W83781D_SYSCTL_VID 1300 /* Volts * 1000 */ ++#define W83781D_SYSCTL_VRM 1301 ++#define W83781D_SYSCTL_PWM1 1401 ++#define W83781D_SYSCTL_PWM2 1402 ++#define W83781D_SYSCTL_PWM3 1403 ++#define W83781D_SYSCTL_PWM4 1404 ++#define W83781D_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */ ++#define W83781D_SYSCTL_SENS2 1502 ++#define W83781D_SYSCTL_SENS3 1503 ++#define W83781D_SYSCTL_RT1 1601 /* 32-entry table */ ++#define W83781D_SYSCTL_RT2 1602 /* 32-entry table */ ++#define W83781D_SYSCTL_RT3 1603 /* 32-entry table */ ++#define W83781D_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ ++#define W83781D_SYSCTL_ALARMS 2001 /* bitvector */ ++#define W83781D_SYSCTL_BEEP 2002 /* bitvector */ ++ ++#define W83781D_ALARM_IN0 0x0001 ++#define W83781D_ALARM_IN1 0x0002 ++#define W83781D_ALARM_IN2 0x0004 ++#define W83781D_ALARM_IN3 0x0008 ++#define W83781D_ALARM_IN4 0x0100 ++#define W83781D_ALARM_IN5 0x0200 ++#define W83781D_ALARM_IN6 0x0400 ++#define W83782D_ALARM_IN7 0x10000 ++#define W83782D_ALARM_IN8 0x20000 ++#define W83781D_ALARM_FAN1 0x0040 ++#define W83781D_ALARM_FAN2 0x0080 ++#define W83781D_ALARM_FAN3 0x0800 ++#define W83781D_ALARM_TEMP1 0x0010 ++#define W83781D_ALARM_TEMP23 0x0020 /* 781D only */ ++#define W83781D_ALARM_TEMP2 0x0020 /* 782D/783S */ ++#define W83781D_ALARM_TEMP3 0x2000 /* 782D only */ ++#define W83781D_ALARM_CHAS 0x1000 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected chip. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++ ++/* just a guess - no datasheet */ ++static ctl_table as99127f_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {0} ++}; ++ ++static ctl_table w83781d_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++#ifdef W83781D_RT ++ {W83781D_SYSCTL_RT1, "rt1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_rt}, ++ {W83781D_SYSCTL_RT2, "rt2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_rt}, ++ {W83781D_SYSCTL_RT3, "rt3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_rt}, ++#endif ++ {0} ++}; ++ ++/* without pwm3-4 */ ++static ctl_table w83782d_isa_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {0} ++}; ++ ++/* with pwm3-4 */ ++static ctl_table w83782d_i2c_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {0} ++}; ++ ++/* w83791D has 10 voltages 5 fans and 3 temps. 2 of the temps are on other ++ devices. */ ++static ctl_table w83791d_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN9, "in9", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {0} ++}; ++ ++static ctl_table w83783s_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ /* no in1 to maintain compatibility with 781d and 782d. */ ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vid}, ++ {W83781D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_vrm}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {0} ++}; ++ ++/* similar to w83782d but no fan3, no vid */ ++static ctl_table w83697hf_dir_table_template[] = { ++ {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ /* no in1 to maintain compatibility with 781d and 782d. */ ++ {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_in}, ++ {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan}, ++ {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp}, ++ {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_temp_add}, ++ {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_fan_div}, ++ {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_alarms}, ++ {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_beep}, ++ {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_pwm}, ++ {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &w83781d_sens}, ++ {0} ++}; ++ ++ ++/* This function is called when: ++ * w83781d_driver is inserted (when this module is loaded), for each ++ available adapter ++ * when a new adapter is inserted (and w83781d_driver is still present) */ ++static int w83781d_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, w83781d_detect); ++} ++ ++static int w83781d_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i, val1 = 0, val2, id; ++ struct i2c_client *new_client; ++ struct w83781d_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ int is_isa = i2c_is_isa_adapter(adapter); ++ enum vendor { winbond, asus } vendid; ++ ++ if (!is_isa ++ && !i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) goto ++ ERROR0; ++ ++ if (is_isa) { ++ if (!request_region(address, W83781D_EXTENT, "w83781d")) ++ goto ERROR0; ++ release_region(address, W83781D_EXTENT); ++ } ++ ++ /* Probe whether there is anything available on this address. Already ++ done for SMBus clients */ ++ if (kind < 0) { ++ if (is_isa) { ++ ++#define REALLY_SLOW_IO ++ /* We need the timeouts for at least some LM78-like chips. But only ++ if we read 'undefined' registers. */ ++ i = inb_p(address + 1); ++ if (inb_p(address + 2) != i) ++ goto ERROR0; ++ if (inb_p(address + 3) != i) ++ goto ERROR0; ++ if (inb_p(address + 7) != i) ++ goto ERROR0; ++#undef REALLY_SLOW_IO ++ ++ /* Let's just hope nothing breaks here */ ++ i = inb_p(address + 5) & 0x7f; ++ outb_p(~i & 0x7f, address + 5); ++ if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { ++ outb_p(i, address + 5); ++ return 0; ++ } ++ } ++ } ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access w83781d_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ init_MUTEX(&data->lock); ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &w83781d_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ /* The w8378?d may be stuck in some other bank than bank 0. This may ++ make reading other information impossible. Specify a force=... or ++ force_*=... parameter, and the Winbond will be reset to the right ++ bank. */ ++ if (kind < 0) { ++ if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & ++ 0x80) { ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ val1 = w83781d_read_value(new_client, W83781D_REG_BANK); ++ val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); ++ /* Check for Winbond or Asus ID if in bank 0 */ ++ if ((!(val1 & 0x07)) && ++ (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) ++ || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ /* If Winbond SMBus, check address at 0x48. ++ Asus doesn't support, except for the as99127f rev.2 */ ++ if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || ++ ((val1 & 0x80) && (val2 == 0x5c)))) { ++ if (w83781d_read_value ++ (new_client, W83781D_REG_I2C_ADDR) != address) { ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ } ++ } ++ ++ /* We have either had a force parameter, or we have already detected the ++ Winbond. Put it now into bank 0 and Vendor ID High Byte */ ++ w83781d_write_value(new_client, W83781D_REG_BANK, ++ (w83781d_read_value(new_client, ++ W83781D_REG_BANK) & 0x78) | ++ 0x80); ++ ++ /* Determine the chip type. */ ++ if (kind <= 0) { ++ /* get vendor ID */ ++ val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); ++ if (val2 == 0x5c) ++ vendid = winbond; ++ else if (val2 == 0x12) ++ vendid = asus; ++ else { ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ val1 = ++ w83781d_read_value(new_client, W83781D_REG_WCHIPID); ++ if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) ++ kind = w83781d; ++ else if (val1 == 0x30 && vendid == winbond) ++ kind = w83782d; ++ else if (val1 == 0x40 && vendid == winbond && !is_isa && address == 0x2d) ++ kind = w83783s; ++ else if (val1 == 0x21 && vendid == winbond) ++ kind = w83627hf; ++ else if (val1 == 0x71 && vendid == winbond && address >= 0x2c) ++ kind = w83791d; ++ else if (val1 == 0x31 && !is_isa && address >= 0x28) ++ kind = as99127f; ++ else if (val1 == 0x60 && vendid == winbond && is_isa) ++ kind = w83697hf; ++ else { ++ if (kind == 0) ++ printk ++ (KERN_WARNING "w83781d.o: Ignoring 'force' parameter for unknown chip at" ++ "adapter %d, address 0x%02x\n", ++ i2c_adapter_id(adapter), address); ++ err = -EINVAL; ++ goto ERROR1; ++ } ++ } ++ ++ if (kind == w83781d) { ++ type_name = "w83781d"; ++ client_name = "W83781D chip"; ++ } else if (kind == w83782d) { ++ type_name = "w83782d"; ++ client_name = "W83782D chip"; ++ } else if (kind == w83783s) { ++ type_name = "w83783s"; ++ client_name = "W83783S chip"; ++ } else if (kind == w83627hf) { ++ type_name = "w83627hf"; ++ client_name = "W83627HF chip"; ++ } else if (kind == as99127f) { ++ type_name = "as99127f"; ++ client_name = "AS99127F chip"; ++ } else if (kind == w83697hf) { ++ type_name = "w83697hf"; ++ client_name = "W83697HF chip"; ++ } else if (kind == w83791d) { ++ type_name = "w83791d"; ++ client_name = "W83791D chip"; ++ } else { ++#ifdef DEBUG ++ printk(KERN_ERR "w83781d.o: Internal error: unknown kind (%d)?!?", ++ kind); ++#endif ++ err = -ENODEV; ++ goto ERROR1; ++ } ++ ++ /* Reserve the ISA region */ ++ if (is_isa) ++ request_region(address, W83781D_EXTENT, type_name); ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = w83781d_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto ERROR3; ++ ++ /* attach secondary i2c lm75-like clients */ ++ if (!is_isa) { ++ if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client), ++ GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto ERROR4; ++ } ++ id = i2c_adapter_id(adapter); ++ if(force_subclients[0] == id && force_subclients[1] == address) { ++ for(i = 2; i <= 3; i++) { ++ if(force_subclients[i] < 0x48 || ++ force_subclients[i] > 0x4f) { ++ printk(KERN_ERR "w83781d.o: Invalid subclient address %d; must be 0x48-0x4f\n", ++ force_subclients[i]); ++ err = -EINVAL; ++ goto ERROR5; ++ } ++ } ++ w83781d_write_value(new_client, ++ W83781D_REG_I2C_SUBADDR, ++ (force_subclients[2] & 0x07) | ++ ((force_subclients[3] & 0x07) <<4)); ++ data->lm75[0].addr = force_subclients[2]; ++ } else { ++ val1 = w83781d_read_value(new_client, ++ W83781D_REG_I2C_SUBADDR); ++ data->lm75[0].addr = 0x48 + (val1 & 0x07); ++ } ++ if (kind != w83783s) { ++ if(force_subclients[0] == id && ++ force_subclients[1] == address) { ++ data->lm75[1].addr = force_subclients[3]; ++ } else { ++ data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07); ++ } ++ if(data->lm75[0].addr == data->lm75[1].addr) { ++ printk(KERN_ERR "w83781d.o: Duplicate addresses 0x%x for subclients.\n", ++ data->lm75[0].addr); ++ err = -EBUSY; ++ goto ERROR5; ++ } ++ } ++ if (kind == w83781d) ++ client_name = "W83781D subclient"; ++ else if (kind == w83782d) ++ client_name = "W83782D subclient"; ++ else if (kind == w83783s) ++ client_name = "W83783S subclient"; ++ else if (kind == w83627hf) ++ client_name = "W83627HF subclient"; ++ else if (kind == as99127f) ++ client_name = "AS99127F subclient"; ++ else if (kind == w83791d) ++ client_name = "W83791D subclient"; ++ ++ ++ for (i = 0; i <= 1; i++) { ++ data->lm75[i].data = NULL; /* store all data in w83781d */ ++ data->lm75[i].adapter = adapter; ++ data->lm75[i].driver = &w83781d_driver; ++ data->lm75[i].flags = 0; ++ strcpy(data->lm75[i].name, client_name); ++ data->lm75[i].id = w83781d_id++; ++ if ((err = i2c_attach_client(&(data->lm75[i])))) { ++ printk(KERN_ERR "w83781d.o: Subclient %d registration at address 0x%x failed.\n", ++ i, data->lm75[i].addr); ++ if (i == 1) ++ goto ERROR6; ++ goto ERROR5; ++ } ++ if (kind == w83783s) ++ break; ++ } ++ } else { ++ data->lm75 = NULL; ++ } ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, ++ type_name, ++ (kind == as99127f) ? ++ as99127f_dir_table_template : ++ (kind == w83781d) ? ++ w83781d_dir_table_template : ++ (kind == w83783s) ? ++ w83783s_dir_table_template : ++ (kind == w83697hf) ? ++ w83697hf_dir_table_template : ++ (kind == w83791d ) ? ++ w83791d_dir_table_template : ++ (is_isa || kind == w83627hf) ? ++ w83782d_isa_dir_table_template : ++ w83782d_i2c_dir_table_template)) < 0) { ++ err = i; ++ goto ERROR7; ++ } ++ data->sysctl_id = i; ++ ++ /* Only PWM2 can be disabled */ ++ for(i = 0; i < 4; i++) ++ data->pwmenable[i] = 1; ++ ++ /* Initialize the chip */ ++ w83781d_init_client(new_client); ++ return 0; ++ ++/* OK, this is not exactly good programming practice, usually. But it is ++ very code-efficient in this case. */ ++ ++ ERROR7: ++ if (!is_isa) ++ i2c_detach_client(& ++ (((struct ++ w83781d_data *) (new_client->data))-> ++ lm75[1])); ++ ERROR6: ++ if (!is_isa) ++ i2c_detach_client(& ++ (((struct ++ w83781d_data *) (new_client->data))-> ++ lm75[0])); ++ ERROR5: ++ if (!is_isa) ++ kfree(((struct w83781d_data *) (new_client->data))->lm75); ++ ERROR4: ++ i2c_detach_client(new_client); ++ ERROR3: ++ if (is_isa) ++ release_region(address, W83781D_EXTENT); ++ ERROR1: ++ kfree(data); ++ ERROR0: ++ return err; ++} ++ ++static int w83781d_detach_client(struct i2c_client *client) ++{ ++ int err; ++ struct w83781d_data *data = client->data; ++ ++ i2c_deregister_entry(data->sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ (KERN_ERR "w83781d.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ if(i2c_is_isa_client(client)) { ++ release_region(client->addr, W83781D_EXTENT); ++ } else { ++ i2c_detach_client(&(data->lm75[0])); ++ if (data->type != w83783s) ++ i2c_detach_client(&(data->lm75[1])); ++ kfree(data->lm75); ++ } ++ kfree(data); ++ ++ return 0; ++} ++ ++/* The SMBus locks itself, usually, but nothing may access the Winbond between ++ bank switches. ISA access must always be locked explicitly! ++ We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, ++ would slow down the W83781D access and should not be necessary. ++ There are some ugly typecasts here, but the good news is - they should ++ nowhere else be necessary! */ ++static int w83781d_read_value(struct i2c_client *client, u16 reg) ++{ ++ int res, word_sized, bank; ++ struct i2c_client *cl; ++ ++ down(&(((struct w83781d_data *) (client->data))->lock)); ++ if (i2c_is_isa_client(client)) { ++ word_sized = (((reg & 0xff00) == 0x100) ++ || ((reg & 0xff00) == 0x200)) ++ && (((reg & 0x00ff) == 0x50) ++ || ((reg & 0x00ff) == 0x53) ++ || ((reg & 0x00ff) == 0x55)); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(reg >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); ++ res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); ++ if (word_sized) { ++ outb_p((reg & 0xff) + 1, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ res = ++ (res << 8) + inb_p(client->addr + ++ W83781D_DATA_REG_OFFSET); ++ } ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ } else { ++ bank = (reg >> 8) & 0x0f; ++ if (bank > 2) ++ /* switch banks */ ++ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, ++ bank); ++ if (bank == 0 || bank > 2) { ++ res = i2c_smbus_read_byte_data(client, reg & 0xff); ++ } else { ++ /* switch to subclient */ ++ cl = ++ &(((struct w83781d_data *) (client->data))-> ++ lm75[bank - 1]); ++ /* convert from ISA to LM75 I2C addresses */ ++ switch (reg & 0xff) { ++ case 0x50: /* TEMP */ ++ res = swab16(i2c_smbus_read_word_data(cl, 0)); ++ break; ++ case 0x52: /* CONFIG */ ++ res = i2c_smbus_read_byte_data(cl, 1); ++ break; ++ case 0x53: /* HYST */ ++ res = swab16(i2c_smbus_read_word_data(cl, 2)); ++ break; ++ case 0x55: /* OVER */ ++ default: ++ res = swab16(i2c_smbus_read_word_data(cl, 3)); ++ break; ++ } ++ } ++ if (bank > 2) ++ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, ++ 0); ++ } ++ up(&(((struct w83781d_data *) (client->data))->lock)); ++ return res; ++} ++ ++static int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) ++{ ++ int word_sized, bank; ++ struct i2c_client *cl; ++ ++ down(&(((struct w83781d_data *) (client->data))->lock)); ++ if (i2c_is_isa_client(client)) { ++ word_sized = (((reg & 0xff00) == 0x100) ++ || ((reg & 0xff00) == 0x200)) ++ && (((reg & 0x00ff) == 0x53) ++ || ((reg & 0x00ff) == 0x55)); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(reg >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); ++ if (word_sized) { ++ outb_p(value >> 8, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ outb_p((reg & 0xff) + 1, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ } ++ outb_p(value & 0xff, ++ client->addr + W83781D_DATA_REG_OFFSET); ++ if (reg & 0xff00) { ++ outb_p(W83781D_REG_BANK, ++ client->addr + W83781D_ADDR_REG_OFFSET); ++ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); ++ } ++ } else { ++ bank = (reg >> 8) & 0x0f; ++ if (bank > 2) ++ /* switch banks */ ++ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, ++ bank); ++ if (bank == 0 || bank > 2) { ++ i2c_smbus_write_byte_data(client, reg & 0xff, ++ value & 0xff); ++ } else { ++ /* switch to subclient */ ++ cl = &(((struct w83781d_data *) (client->data))-> ++ lm75[bank - 1]); ++ /* convert from ISA to LM75 I2C addresses */ ++ switch (reg & 0xff) { ++ case 0x52: /* CONFIG */ ++ i2c_smbus_write_byte_data(cl, 1, value & 0xff); ++ break; ++ case 0x53: /* HYST */ ++ i2c_smbus_write_word_data(cl, 2, swab16(value)); ++ break; ++ case 0x55: /* OVER */ ++ i2c_smbus_write_word_data(cl, 3, swab16(value)); ++ break; ++ } ++ } ++ if (bank > 2) ++ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, ++ 0); ++ } ++ up(&(((struct w83781d_data *) (client->data))->lock)); ++ return 0; ++} ++ ++/* Called when we have found a new W83781D. It should set limits, etc. */ ++static void w83781d_init_client(struct i2c_client *client) ++{ ++ struct w83781d_data *data = client->data; ++ int i, p; ++ int type = data->type; ++ u8 tmp; ++ ++ if(init && type != as99127f) { /* this resets registers we don't have ++ documentation for on the as99127f */ ++ /* save these registers */ ++ i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); ++ p = w83781d_read_value(client, W83781D_REG_PWMCLK12); ++ /* Reset all except Watchdog values and last conversion values ++ This sets fan-divs to 2, among others */ ++ w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); ++ /* Restore the registers and disable power-on abnormal beep. ++ This saves FAN 1/2/3 input/output values set by BIOS. */ ++ w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); ++ w83781d_write_value(client, W83781D_REG_PWMCLK12, p); ++ /* Disable master beep-enable (reset turns it on). ++ Individual beeps should be reset to off but for some reason ++ disabling this bit helps some people not get beeped */ ++ w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); ++ } ++ ++ data->vrm = (type == w83791d) ? 90 : 82; ++ ++ if ((type != w83781d) && (type != as99127f)) { ++ tmp = w83781d_read_value(client, W83781D_REG_SCFG1); ++ for (i = 1; i <= 3; i++) { ++ if (!(tmp & BIT_SCFG1[i - 1])) { ++ data->sens[i - 1] = W83781D_DEFAULT_BETA; ++ } else { ++ if (w83781d_read_value ++ (client, ++ W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) ++ data->sens[i - 1] = 1; ++ else ++ data->sens[i - 1] = 2; ++ } ++ if ((type == w83783s || type == w83697hf) && (i == 2)) ++ break; ++ } ++ } ++#ifdef W83781D_RT ++/* ++ Fill up the RT Tables. ++ We assume that they are 32 bytes long, in order for temp 1-3. ++ Data sheet documentation is sparse. ++ We also assume that it is only for the 781D although I suspect ++ that the others support it as well.... ++*/ ++ ++ if (init && type == w83781d) { ++ u16 k = 0; ++/* ++ Auto-indexing doesn't seem to work... ++ w83781d_write_value(client,W83781D_REG_RT_IDX,0); ++*/ ++ for (i = 0; i < 3; i++) { ++ int j; ++ for (j = 0; j < 32; j++) { ++ w83781d_write_value(client, ++ W83781D_REG_RT_IDX, ++ k++); ++ data->rt[i][j] = ++ w83781d_read_value(client, ++ W83781D_REG_RT_VAL); ++ } ++ } ++ } ++#endif /* W83781D_RT */ ++ ++ if(init) { ++ w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00); ++ if (type != w83783s && type != w83697hf) { ++ w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG, ++ 0x00); ++ } ++ if (type != w83781d) { ++ /* enable comparator mode for temp2 and temp3 so ++ alarm indication will work correctly */ ++ i = w83781d_read_value(client, W83781D_REG_IRQ); ++ if (!(i & 0x40)) ++ w83781d_write_value(client, W83781D_REG_IRQ, ++ i | 0x40); ++ } ++ } ++ ++ /* Start monitoring */ ++ w83781d_write_value(client, W83781D_REG_CONFIG, ++ (w83781d_read_value(client, ++ W83781D_REG_CONFIG) & 0xf7) ++ | 0x01); ++} ++ ++static void w83781d_update_client(struct i2c_client *client) ++{ ++ struct w83781d_data *data = client->data; ++ int i; ++ ++ down(&data->update_lock); ++ ++ if (time_after(jiffies - data->last_updated, HZ + HZ / 2) || ++ time_before(jiffies, data->last_updated) || !data->valid) { ++ pr_debug(KERN_DEBUG "Starting device update\n"); ++ ++ for (i = 0; i <= 9; i++) { ++ if ((data->type == w83783s || data->type == w83697hf) ++ && (i == 1)) ++ continue; /* 783S has no in1 */ ++ if (data->type == w83791d) { ++ data->in[i] = ++ w83781d_read_value(client, W83791D_REG_IN(i)); ++ data->in_min[i] = ++ w83781d_read_value(client, ++ W83791D_REG_IN_MIN(i)); ++ data->in_max[i] = ++ w83781d_read_value(client, ++ W83791D_REG_IN_MAX(i)); ++ } else { ++ data->in[i] = ++ w83781d_read_value(client, W83781D_REG_IN(i)); ++ data->in_min[i] = ++ w83781d_read_value(client, ++ W83781D_REG_IN_MIN(i)); ++ data->in_max[i] = ++ w83781d_read_value(client, ++ W83781D_REG_IN_MAX(i)); ++ } ++ if ((data->type != w83782d) && (data->type != w83697hf) ++ && (data->type != w83627hf) && (i == 6) ++ && (data->type != w83791d)) ++ break; ++ ++ if (data->type != w83791d && i == 8) ++ break; ++ } ++ for (i = 1; i <= 5; i++) { ++ data->fan[i - 1] = ++ w83781d_read_value(client, W83781D_REG_FAN(i)); ++ data->fan_min[i - 1] = ++ w83781d_read_value(client, ++ W83781D_REG_FAN_MIN(i)); ++ if (data->type != w83791d && i == 3) break; ++ } ++ if (data->type != w83781d && data->type != as99127f) { ++ for (i = 1; i <= 4; i++) { ++ data->pwm[i - 1] = ++ w83781d_read_value(client, ++ W83781D_REG_PWM(data->type, i)); ++ if (((data->type == w83783s) ++ || (data->type == w83627hf) ++ || (data->type == w83697hf) ++ || ((data->type == w83782d) ++ && i2c_is_isa_client(client))) ++ && i == 2) ++ break; ++ } ++ /* Only PWM2 can be disabled */ ++ data->pwmenable[1] = (w83781d_read_value(client, ++ W83781D_REG_PWMCLK12) & 0x08) >> 3; ++ } ++ ++ data->temp = w83781d_read_value(client, W83781D_REG_TEMP); ++ data->temp_over = ++ w83781d_read_value(client, W83781D_REG_TEMP_OVER); ++ data->temp_hyst = ++ w83781d_read_value(client, W83781D_REG_TEMP_HYST); ++ data->temp_add[0] = ++ w83781d_read_value(client, W83781D_REG_TEMP2); ++ data->temp_add_over[0] = ++ w83781d_read_value(client, W83781D_REG_TEMP2_OVER); ++ data->temp_add_hyst[0] = ++ w83781d_read_value(client, W83781D_REG_TEMP2_HYST); ++ if (data->type != w83783s && data->type != w83697hf) { ++ data->temp_add[1] = ++ w83781d_read_value(client, W83781D_REG_TEMP3); ++ data->temp_add_over[1] = ++ w83781d_read_value(client, W83781D_REG_TEMP3_OVER); ++ data->temp_add_hyst[1] = ++ w83781d_read_value(client, W83781D_REG_TEMP3_HYST); ++ } ++ i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); ++ if (data->type != w83697hf) { ++ data->vid = i & 0x0f; ++ data->vid |= ++ (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) ++ << 4; ++ } ++ data->fan_div[0] = (i >> 4) & 0x03; ++ data->fan_div[1] = (i >> 6) & 0x03; ++ if (data->type != w83697hf) { ++ data->fan_div[2] = (w83781d_read_value(client, ++ W83781D_REG_PIN) >> 6) & 0x03; ++ } ++ if ((data->type != w83781d) && (data->type != as99127f)) { ++ i = w83781d_read_value(client, W83781D_REG_VBAT); ++ data->fan_div[0] |= (i >> 3) & 0x04; ++ data->fan_div[1] |= (i >> 4) & 0x04; ++ if (data->type != w83697hf) ++ data->fan_div[2] |= (i >> 5) & 0x04; ++ } ++ data->alarms = ++ w83781d_read_value(client, ++ W83781D_REG_ALARM1) + ++ (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); ++ if ((data->type == w83782d) || (data->type == w83627hf) || ++ (data->type == w83697hf)) { ++ data->alarms |= ++ w83781d_read_value(client, ++ W83781D_REG_ALARM3) << 16; ++ } ++ i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); ++ data->beep_enable = i >> 7; ++ data->beeps = ((i & 0x7f) << 8) + ++ w83781d_read_value(client, W83781D_REG_BEEP_INTS1); ++ if ((data->type != w83781d) && (data->type != as99127f) ++ && (data->type != w83791d)) { ++ data->beeps |= ++ w83781d_read_value(client, ++ W83781D_REG_BEEP_INTS3) << 16; ++ } ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++ ++/* The next few functions are the call-back functions of the /proc/sys and ++ sysctl files. Which function is used is defined in the ctl_table in ++ the extra1 field. ++ Each function must return the magnitude (power of 10 to divide the date ++ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must ++ put a maximum of *nrels elements in results reflecting the data of this ++ file, and set *nrels to the number it actually put in it, if operation== ++ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from ++ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. ++ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is ++ large enough (by checking the incoming value of *nrels). This is not very ++ good practice, but as long as you put less than about 5 values in results, ++ you can assume it is large enough. */ ++static void w83781d_in(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_IN0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 2; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = IN_FROM_REG(data->in_min[nr]); ++ results[1] = IN_FROM_REG(data->in_max[nr]); ++ results[2] = IN_FROM_REG(data->in[nr]); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->in_min[nr] = IN_TO_REG(results[0]); ++ w83781d_write_value(client, W83781D_REG_IN_MIN(nr), ++ data->in_min[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ data->in_max[nr] = IN_TO_REG(results[1]); ++ w83781d_write_value(client, W83781D_REG_IN_MAX(nr), ++ data->in_max[nr]); ++ } ++ } ++} ++ ++void w83781d_fan(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = FAN_FROM_REG(data->fan_min[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ results[1] = FAN_FROM_REG(data->fan[nr - 1], ++ DIV_FROM_REG(data->fan_div[nr - 1])); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->fan_min[nr - 1] = ++ FAN_TO_REG(results[0], ++ DIV_FROM_REG(data->fan_div[nr-1])); ++ w83781d_write_value(client, ++ W83781D_REG_FAN_MIN(nr), ++ data->fan_min[nr - 1]); ++ } ++ } ++} ++ ++void w83781d_temp(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp_hyst); ++ results[2] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->temp_over = TEMP_TO_REG(results[0]); ++ w83781d_write_value(client, W83781D_REG_TEMP_OVER, ++ data->temp_over); ++ } ++ if (*nrels_mag >= 2) { ++ data->temp_hyst = TEMP_TO_REG(results[1]); ++ w83781d_write_value(client, W83781D_REG_TEMP_HYST, ++ data->temp_hyst); ++ } ++ } ++} ++ ++void w83781d_temp_add(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = ctl_name - W83781D_SYSCTL_TEMP2; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ if (data->type == as99127f) { ++ results[0] = ++ AS99127_TEMP_ADD_FROM_REG(data-> ++ temp_add_over[nr]); ++ results[1] = ++ AS99127_TEMP_ADD_FROM_REG(data-> ++ temp_add_hyst[nr]); ++ results[2] = ++ AS99127_TEMP_ADD_FROM_REG(data->temp_add[nr]); ++ } else { ++ results[0] = ++ LM75_TEMP_FROM_REG(data->temp_add_over[nr]); ++ results[1] = ++ LM75_TEMP_FROM_REG(data->temp_add_hyst[nr]); ++ results[2] = LM75_TEMP_FROM_REG(data->temp_add[nr]); ++ } ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ if (data->type == as99127f) ++ data->temp_add_over[nr] = ++ AS99127_TEMP_ADD_TO_REG(results[0]); ++ else ++ data->temp_add_over[nr] = ++ LM75_TEMP_TO_REG(results[0]); ++ w83781d_write_value(client, ++ nr ? W83781D_REG_TEMP3_OVER : ++ W83781D_REG_TEMP2_OVER, ++ data->temp_add_over[nr]); ++ } ++ if (*nrels_mag >= 2) { ++ if (data->type == as99127f) ++ data->temp_add_hyst[nr] = ++ AS99127_TEMP_ADD_TO_REG(results[1]); ++ else ++ data->temp_add_hyst[nr] = ++ LM75_TEMP_TO_REG(results[1]); ++ w83781d_write_value(client, ++ nr ? W83781D_REG_TEMP3_HYST : ++ W83781D_REG_TEMP2_HYST, ++ data->temp_add_hyst[nr]); ++ } ++ } ++} ++ ++ ++void w83781d_vid(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 3; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = vid_from_reg(data->vid, data->vrm); ++ *nrels_mag = 1; ++ } ++} ++ ++void w83781d_vrm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 1; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->vrm; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) ++ data->vrm = results[0]; ++ } ++} ++ ++void w83781d_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = ALARMS_FROM_REG(data->alarms); ++ *nrels_mag = 1; ++ } ++} ++ ++void w83781d_beep(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int val; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); ++ results[1] = BEEPS_FROM_REG(data->beeps, data->type); ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 2) { ++ data->beeps = BEEPS_TO_REG(results[1], data->type); ++ w83781d_write_value(client, W83781D_REG_BEEP_INTS1, ++ data->beeps & 0xff); ++ if ((data->type != w83781d) && ++ (data->type != as99127f)) { ++ w83781d_write_value(client, ++ W83781D_REG_BEEP_INTS3, ++ ((data-> beeps) >> 16) & ++ 0xff); ++ } ++ val = (data->beeps >> 8) & 0x7f; ++ } else if (*nrels_mag >= 1) ++ val = ++ w83781d_read_value(client, ++ W83781D_REG_BEEP_INTS2) & ++ 0x7f; ++ if (*nrels_mag >= 1) { ++ data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); ++ w83781d_write_value(client, W83781D_REG_BEEP_INTS2, ++ val | data->beep_enable << 7); ++ } ++ } ++} ++ ++/* w83697hf only has two fans */ ++void w83781d_fan_div(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int old, old2, old3 = 0; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = DIV_FROM_REG(data->fan_div[0]); ++ results[1] = DIV_FROM_REG(data->fan_div[1]); ++ if (data->type == w83697hf) { ++ *nrels_mag = 2; ++ } else { ++ results[2] = DIV_FROM_REG(data->fan_div[2]); ++ *nrels_mag = 3; ++ } ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ old = w83781d_read_value(client, W83781D_REG_VID_FANDIV); ++ /* w83781d and as99127f don't have extended divisor bits */ ++ if ((data->type != w83781d) && data->type != as99127f) { ++ old3 = ++ w83781d_read_value(client, W83781D_REG_VBAT); ++ } ++ if (*nrels_mag >= 3 && data->type != w83697hf) { ++ data->fan_div[2] = ++ DIV_TO_REG(results[2], data->type); ++ old2 = w83781d_read_value(client, W83781D_REG_PIN); ++ old2 = ++ (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); ++ w83781d_write_value(client, W83781D_REG_PIN, old2); ++ if ((data->type != w83781d) && ++ (data->type != as99127f)) { ++ old3 = ++ (old3 & 0x7f) | ++ ((data->fan_div[2] & 0x04) << 5); ++ } ++ } ++ if (*nrels_mag >= 2) { ++ data->fan_div[1] = ++ DIV_TO_REG(results[1], data->type); ++ old = ++ (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); ++ if ((data->type != w83781d) && ++ (data->type != as99127f)) { ++ old3 = ++ (old3 & 0xbf) | ++ ((data->fan_div[1] & 0x04) << 4); ++ } ++ } ++ if (*nrels_mag >= 1) { ++ data->fan_div[0] = ++ DIV_TO_REG(results[0], data->type); ++ old = ++ (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); ++ w83781d_write_value(client, W83781D_REG_VID_FANDIV, ++ old); ++ if ((data->type != w83781d) && ++ (data->type != as99127f)) { ++ old3 = ++ (old3 & 0xdf) | ++ ((data->fan_div[0] & 0x04) << 3); ++ w83781d_write_value(client, ++ W83781D_REG_VBAT, ++ old3); ++ } ++ } ++ } ++} ++ ++void w83781d_pwm(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1; ++ int j, k; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ w83781d_update_client(client); ++ results[0] = PWM_FROM_REG(data->pwm[nr - 1]); ++ results[1] = data->pwmenable[nr - 1]; ++ *nrels_mag = 2; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->pwm[nr - 1] = PWM_TO_REG(results[0]); ++ w83781d_write_value(client, ++ W83781D_REG_PWM(data->type, nr), ++ data->pwm[nr - 1]); ++ } ++ /* only PWM2 can be enabled/disabled */ ++ if (*nrels_mag >= 2 && nr == 2) { ++ j = w83781d_read_value(client, W83781D_REG_PWMCLK12); ++ k = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); ++ if(results[1]) { ++ if(!(j & 0x08)) ++ w83781d_write_value(client, ++ W83781D_REG_PWMCLK12, j | 0x08); ++ if(k & 0x10) ++ w83781d_write_value(client, ++ W83781D_REG_BEEP_CONFIG, k & 0xef); ++ data->pwmenable[1] = 1; ++ } else { ++ if(j & 0x08) ++ w83781d_write_value(client, ++ W83781D_REG_PWMCLK12, j & 0xf7); ++ if(!(k & 0x10)) ++ w83781d_write_value(client, ++ W83781D_REG_BEEP_CONFIG, j | 0x10); ++ data->pwmenable[1] = 0; ++ } ++ } ++ } ++} ++ ++void w83781d_sens(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1; ++ u8 tmp; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ results[0] = data->sens[nr - 1]; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ switch (results[0]) { ++ case 1: /* PII/Celeron diode */ ++ tmp = w83781d_read_value(client, ++ W83781D_REG_SCFG1); ++ w83781d_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp | BIT_SCFG1[nr - ++ 1]); ++ tmp = w83781d_read_value(client, ++ W83781D_REG_SCFG2); ++ w83781d_write_value(client, ++ W83781D_REG_SCFG2, ++ tmp | BIT_SCFG2[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ case 2: /* 3904 */ ++ tmp = w83781d_read_value(client, ++ W83781D_REG_SCFG1); ++ w83781d_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp | BIT_SCFG1[nr - ++ 1]); ++ tmp = w83781d_read_value(client, ++ W83781D_REG_SCFG2); ++ w83781d_write_value(client, ++ W83781D_REG_SCFG2, ++ tmp & ~BIT_SCFG2[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ case W83781D_DEFAULT_BETA: /* thermistor */ ++ tmp = w83781d_read_value(client, ++ W83781D_REG_SCFG1); ++ w83781d_write_value(client, ++ W83781D_REG_SCFG1, ++ tmp & ~BIT_SCFG1[nr - ++ 1]); ++ data->sens[nr - 1] = results[0]; ++ break; ++ default: ++ printk ++ (KERN_ERR "w83781d.o: Invalid sensor type %ld; must be 1, 2, or %d\n", ++ results[0], W83781D_DEFAULT_BETA); ++ break; ++ } ++ } ++ } ++} ++ ++#ifdef W83781D_RT ++static void w83781d_rt(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct w83781d_data *data = client->data; ++ int nr = 1 + ctl_name - W83781D_SYSCTL_RT1; ++ int i; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ for (i = 0; i < 32; i++) { ++ results[i] = data->rt[nr - 1][i]; ++ } ++ *nrels_mag = 32; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag > 32) ++ *nrels_mag = 32; ++ for (i = 0; i < *nrels_mag; i++) { ++ /* fixme: no bounds checking 0-255 */ ++ data->rt[nr - 1][i] = results[i]; ++ w83781d_write_value(client, W83781D_REG_RT_IDX, i); ++ w83781d_write_value(client, W83781D_REG_RT_VAL, ++ data->rt[nr - 1][i]); ++ } ++ } ++} ++#endif ++ ++static int __init sm_w83781d_init(void) ++{ ++ printk(KERN_INFO "w83781d.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&w83781d_driver); ++} ++ ++static void __exit sm_w83781d_exit(void) ++{ ++ i2c_del_driver(&w83781d_driver); ++} ++ ++ ++ ++MODULE_AUTHOR("Frodo Looijaard , " ++ "Philip Edelbrock , " ++ "and Mark Studebaker "); ++MODULE_DESCRIPTION("W83781D driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_w83781d_init); ++module_exit(sm_w83781d_exit); +--- linux-old/drivers/sensors/w83l785ts.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/w83l785ts.c Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,388 @@ ++/* ++ * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring ++ * Copyright (C) 2003-2004 Jean Delvare ++ * ++ * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made ++ * by Winbond. It reports a single external temperature with a 1 deg ++ * resolution and a 3 deg accuracy. Data sheet can be obtained from ++ * Winbond's website at: ++ * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf ++ * ++ * Thanks to James Bolt for benchmarking the read ++ * error handling mechanism. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#ifndef I2C_DRIVERID_W83L785TS ++#define I2C_DRIVERID_W83L785TS 1047 ++#endif ++ ++/* How many retries on register read error */ ++#define MAX_RETRIES 5 ++ ++/* ++ * Address to scan ++ * Address is fully defined internally and cannot be changed. ++ */ ++ ++static unsigned short normal_i2c[] = { 0x2e, SENSORS_I2C_END }; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* ++ * Insmod parameters ++ */ ++ ++SENSORS_INSMOD_1(w83l785ts); ++ ++/* ++ * The W83L785TS-S registers ++ * Manufacturer ID is 0x5CA3 for Winbond. ++ */ ++ ++#define W83L785TS_REG_MAN_ID1 0x4D ++#define W83L785TS_REG_MAN_ID2 0x4C ++#define W83L785TS_REG_CHIP_ID 0x4E ++#define W83L785TS_REG_CONFIG 0x40 ++#define W83L785TS_REG_TYPE 0x52 ++#define W83L785TS_REG_TEMP 0x27 ++#define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */ ++ ++/* ++ * Conversions ++ * The W83L785TS-S uses signed 8-bit values. ++ */ ++ ++#define TEMP_FROM_REG(val) (val & 0x80 ? val-0x100 : val) ++ ++/* ++ * Functions declaration ++ */ ++ ++static int w83l785ts_attach_adapter(struct i2c_adapter *adapter); ++static int w83l785ts_detect(struct i2c_adapter *adapter, int address, unsigned ++ short flags, int kind); ++static int w83l785ts_detach_client(struct i2c_client *client); ++static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); ++static void w83l785ts_update_client(struct i2c_client *client); ++static void w83l785ts_temp(struct i2c_client *client, int operation, int ++ ctl_name, int *nrels_mag, long *results); ++ ++/* ++ * Driver data (common to all clients) ++ */ ++ ++static struct i2c_driver w83l785ts_driver = { ++ .owner = THIS_MODULE, ++ .name = "W83L785S-S sensor driver", ++ .id = I2C_DRIVERID_W83L785TS, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = w83l785ts_attach_adapter, ++ .detach_client = w83l785ts_detach_client, ++}; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++ ++struct w83l785ts_data { ++ struct i2c_client client; ++ int sysctl_id; ++ ++ struct semaphore update_lock; ++ char valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* registers values */ ++ u8 temp, temp_over; ++}; ++ ++/* ++ * Proc entries ++ * These files are created for each detected W83L785TS-S. ++ */ ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define W83L785TS_SYSCTL_TEMP 1200 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++ ++static ctl_table w83l785ts_dir_table_template[] = ++{ ++ {W83L785TS_SYSCTL_TEMP, "temp", NULL, 0, 0444, NULL, ++ &i2c_proc_real, &i2c_sysctl_real, NULL, &w83l785ts_temp}, ++ {0} ++}; ++ ++/* ++ * Internal variables ++ */ ++ ++static int w83l785ts_id = 0; ++ ++/* ++ * Real code ++ */ ++ ++static int w83l785ts_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, w83l785ts_detect); ++} ++ ++/* ++ * The following function does more than just detection. If detection ++ * succeeds, it also registers the new chip. ++ */ ++static int w83l785ts_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ struct i2c_client *new_client; ++ struct w83l785ts_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "w83l785ts.o: I2C bus doesn't support " ++ "byte read mode, skipping.\n"); ++#endif ++ return 0; ++ } ++ ++ if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) { ++ printk(KERN_ERR "w83l785ts.o: Out of memory in w83l785ts_detect " ++ "(new_client).\n"); ++ return -ENOMEM; ++ } ++ ++ /* ++ * The common I2C client data is placed right after the ++ * W83L785TS-specific. The W83L785TS-specific data is pointed to by the ++ * data field from the I2C client data. ++ */ ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &w83l785ts_driver; ++ new_client->flags = 0; ++ ++ /* ++ * Now we do the remaining detection. A negative kind means that ++ * the driver was loaded with no force parameter (default), so we ++ * must both detect and identify the chip (actually there is only ++ * one possible kind of chip for now, W83L785TS-S). A zero kind means ++ * that the driver was loaded with the force parameter, the detection ++ * step shall be skipped. A positive kind means that the driver ++ * was loaded with the force parameter and a given kind of chip is ++ * requested, so both the detection and the identification steps ++ * are skipped. ++ */ ++ ++ if (kind < 0) { /* detection */ ++ if (((w83l785ts_read_value(new_client, W83L785TS_REG_CONFIG, 0) ++ & 0x80) != 0x00) ++ || ((w83l785ts_read_value(new_client, W83L785TS_REG_TYPE, 0) ++ & 0xFC) != 0x00)) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "w83l785ts.o: Detection failed at " ++ "0x%02x.\n", address); ++#endif ++ goto ERROR1; ++ } ++ } ++ ++ if (kind <= 0) { /* identification */ ++ u16 man_id; ++ u8 chip_id; ++ ++ man_id = (w83l785ts_read_value(new_client, W83L785TS_REG_MAN_ID1, 0) << 8) ++ + w83l785ts_read_value(new_client, W83L785TS_REG_MAN_ID2, 0); ++ chip_id = w83l785ts_read_value(new_client, W83L785TS_REG_CHIP_ID, 0); ++ if (man_id == 0x5CA3) { /* Winbond */ ++ if (chip_id == 0x70) ++ kind = w83l785ts; ++ } ++ } ++ ++ if (kind <= 0) { /* identification failed */ ++ printk(KERN_INFO "w83l785ts.o: Unsupported chip.\n"); ++ goto ERROR1; ++ } ++ ++ if (kind == w83l785ts) { ++ type_name = "w83l785ts"; ++ client_name = "W83L785TS-S chip"; ++ } else { ++ printk(KERN_ERR "w83l785ts.o: Unknown kind %d.\n", kind); ++ goto ERROR1; ++ } ++ ++ /* ++ * OK, we got a valid chip so we can fill in the remaining client ++ * fields. ++ */ ++ ++ strcpy(new_client->name, client_name); ++ new_client->id = w83l785ts_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* ++ * Tell the I2C layer a new client has arrived. ++ */ ++ ++ if ((err = i2c_attach_client(new_client))) { ++#ifdef DEBUG ++ printk(KERN_ERR "w83l785ts.o: Failed attaching client.\n"); ++#endif ++ goto ERROR1; ++ } ++ ++ /* ++ * Register a new directory entry. ++ */ ++ ++ if ((err = i2c_register_entry(new_client, type_name, ++ w83l785ts_dir_table_template)) < 0) { ++#ifdef DEBUG ++ printk(KERN_ERR "w83l785ts.o: Failed registering directory " ++ "entry.\n"); ++#endif ++ goto ERROR2; ++ } ++ data->sysctl_id = err; ++ ++ /* ++ * Initialize the W83L785TS chip ++ * Nothing yet, assume it is already started. ++ */ ++ ++ return 0; ++ ++ ERROR2: ++ i2c_detach_client(new_client); ++ ERROR1: ++ kfree(data); ++ return err; ++} ++ ++static int w83l785ts_detach_client(struct i2c_client *client) ++{ ++ int err; ++ ++ i2c_deregister_entry(((struct w83l785ts_data *) (client->data))->sysctl_id); ++ if ((err = i2c_detach_client(client))) { ++ printk(KERN_ERR "w83l785ts.o: Client deregistration failed, " ++ "client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ return 0; ++} ++ ++static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) ++{ ++ int value, i; ++ ++ /* Frequent read errors have been reported on Asus boards, so we ++ * retry on read errors. If it still fails (unlikely), return the ++ * default value requested by the caller. */ ++ for (i = 1; i <= MAX_RETRIES; i++) { ++ value = i2c_smbus_read_byte_data(client, reg); ++ if (value >= 0) ++ return value; ++ printk(KERN_WARNING "w83l785ts.o: Read failed, will retry " ++ "in %d.\n", i); ++ mdelay(i); ++ } ++ ++ printk(KERN_ERR "w83l785ts.o: Couldn't read value from register. " ++ "Please report.\n"); ++ return defval; ++} ++ ++static void w83l785ts_update_client(struct i2c_client *client) ++{ ++ struct w83l785ts_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ * 2) ++ || (jiffies < data->last_updated) ++ || !data->valid) { ++#ifdef DEBUG ++ printk(KERN_DEBUG "w83l785ts.o: Updating data.\n"); ++#endif ++ data->temp = w83l785ts_read_value(client, W83L785TS_REG_TEMP, ++ data->temp); ++ data->temp_over = w83l785ts_read_value(client, ++ W83L785TS_REG_TEMP_OVER, data->temp_over); ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++static void w83l785ts_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct w83l785ts_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) { ++ *nrels_mag = 0; /* magnitude */ ++ } else if (operation == SENSORS_PROC_REAL_READ) { ++ w83l785ts_update_client(client); ++ results[0] = TEMP_FROM_REG(data->temp_over); ++ results[1] = TEMP_FROM_REG(data->temp); ++ *nrels_mag = 2; ++ } ++} ++ ++static int __init sm_w83l785ts_init(void) ++{ ++ printk(KERN_INFO "w83l785ts.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&w83l785ts_driver); ++} ++ ++static void __exit sm_w83l785ts_exit(void) ++{ ++ i2c_del_driver(&w83l785ts_driver); ++} ++ ++MODULE_AUTHOR("Jean Delvare "); ++MODULE_DESCRIPTION("W83L785TS-S sensor driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sm_w83l785ts_init); ++module_exit(sm_w83l785ts_exit); +--- linux-old/drivers/sensors/xeontemp.c Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/xeontemp.c Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,388 @@ ++/* ++ xeontemp.c - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 1998, 1999,2003 Frodo Looijaard , ++ Philip Edelbrock , and ++ Mark D. Studebaker ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* The Xeon temperature sensor looks just like an ADM1021 with the remote ++ sensor only. There is are no ID registers so detection is difficult. */ ++ ++#include ++#include ++#include ++#include ++#include ++#define LM_DATE "20041007" ++#define LM_VERSION "2.8.8" ++ ++#ifndef I2C_DRIVERID_XEONTEMP ++#define I2C_DRIVERID_XEONTEMP 1045 ++#endif ++ ++/* Addresses to scan */ ++static unsigned short normal_i2c[] = { 0x18, 0x1a, 0x29, 0x2b, ++ 0x4c, 0x4e, SENSORS_I2C_END ++}; ++static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; ++static unsigned int normal_isa[] = { SENSORS_ISA_END }; ++static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; ++ ++/* Insmod parameters */ ++SENSORS_INSMOD_1(xeontemp); ++ ++/* xeontemp constants specified below */ ++ ++/* The registers */ ++/* Read-only */ ++#define XEONTEMP_REG_REMOTE_TEMP 0x01 ++#define XEONTEMP_REG_STATUS 0x02 ++/* These use different addresses for reading/writing */ ++#define XEONTEMP_REG_CONFIG_R 0x03 ++#define XEONTEMP_REG_CONFIG_W 0x09 ++#define XEONTEMP_REG_CONV_RATE_R 0x04 ++#define XEONTEMP_REG_CONV_RATE_W 0x0A ++/* limits */ ++#define XEONTEMP_REG_REMOTE_TOS_R 0x07 ++#define XEONTEMP_REG_REMOTE_TOS_W 0x0D ++#define XEONTEMP_REG_REMOTE_THYST_R 0x08 ++#define XEONTEMP_REG_REMOTE_THYST_W 0x0E ++/* write-only */ ++#define XEONTEMP_REG_ONESHOT 0x0F ++ ++#define XEONTEMP_ALARM_RTEMP (XEONTEMP_ALARM_RTEMP_HIGH | XEONTEMP_ALARM_RTEMP_LOW\ ++ | XEONTEMP_ALARM_RTEMP_NA) ++#define XEONTEMP_ALARM_ALL XEONTEMP_ALARM_RTEMP ++ ++/* Conversions. Rounding and limit checking is only done on the TO_REG ++ variants. Note that you should be a bit careful with which arguments ++ these macros are called: arguments may be evaluated more than once. ++ Fixing this is just not worth it. */ ++/* Conversions note: 1021 uses normal integer signed-byte format*/ ++#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) ++#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) ++ ++/* Each client has this additional data */ ++struct xeontemp_data { ++ struct i2c_client client; ++ int sysctl_id; ++ enum chips type; ++ ++ struct semaphore update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ ++ u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms; ++ u8 fail; ++}; ++ ++static int xeontemp_attach_adapter(struct i2c_adapter *adapter); ++static int xeontemp_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind); ++static void xeontemp_init_client(struct i2c_client *client); ++static int xeontemp_detach_client(struct i2c_client *client); ++static int xeontemp_read_value(struct i2c_client *client, u8 reg); ++static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask); ++static int xeontemp_write_value(struct i2c_client *client, u8 reg, ++ u16 value); ++static void xeontemp_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, ++ long *results); ++static void xeontemp_alarms(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results); ++static void xeontemp_update_client(struct i2c_client *client); ++ ++/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ ++static int read_only = 0; ++ ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver xeontemp_driver = { ++ .owner = THIS_MODULE, ++ .name = "Xeon temp sensor driver", ++ .id = I2C_DRIVERID_XEONTEMP, ++ .flags = I2C_DF_NOTIFY, ++ .attach_adapter = xeontemp_attach_adapter, ++ .detach_client = xeontemp_detach_client, ++}; ++ ++/* -- SENSORS SYSCTL START -- */ ++ ++#define XEONTEMP_SYSCTL_REMOTE_TEMP 1201 ++#define XEONTEMP_SYSCTL_ALARMS 1203 ++ ++#define XEONTEMP_ALARM_RTEMP_HIGH 0x10 ++#define XEONTEMP_ALARM_RTEMP_LOW 0x08 ++#define XEONTEMP_ALARM_RTEMP_NA 0x04 ++ ++/* -- SENSORS SYSCTL END -- */ ++ ++/* These files are created for each detected xeontemp. This is just a template; ++ though at first sight, you might think we could use a statically ++ allocated list, we need some way to get back to the parent - which ++ is done through one of the 'extra' fields which are initialized ++ when a new copy is allocated. */ ++static ctl_table xeontemp_dir_table_template[] = { ++ {XEONTEMP_SYSCTL_REMOTE_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &xeontemp_remote_temp}, ++ {XEONTEMP_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, ++ &i2c_sysctl_real, NULL, &xeontemp_alarms}, ++ {0} ++}; ++ ++static int xeontemp_id = 0; ++ ++static int xeontemp_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_detect(adapter, &addr_data, xeontemp_detect); ++} ++ ++static int xeontemp_detect(struct i2c_adapter *adapter, int address, ++ unsigned short flags, int kind) ++{ ++ int i; ++ struct i2c_client *new_client; ++ struct xeontemp_data *data; ++ int err = 0; ++ const char *type_name = ""; ++ const char *client_name = ""; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ goto error0; ++ ++ /* OK. For now, we presume we have a valid client. We now create the ++ client structure, even though we cannot fill it completely yet. ++ But it allows us to access xeontemp_{read,write}_value. */ ++ ++ if (!(data = kmalloc(sizeof(struct xeontemp_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto error0; ++ } ++ ++ new_client = &data->client; ++ new_client->addr = address; ++ new_client->data = data; ++ new_client->adapter = adapter; ++ new_client->driver = &xeontemp_driver; ++ new_client->flags = 0; ++ ++ /* Now, we do the remaining detection. */ ++ ++ if (kind < 0) { ++ if ( ++ (xeontemp_read_value(new_client, XEONTEMP_REG_STATUS) & ++ 0x03) != 0x00) ++ goto error1; ++ } ++ ++ /* Determine the chip type. */ ++ ++ if (kind <= 0) { ++ kind = xeontemp; ++ } ++ ++ type_name = "xeontemp"; ++ client_name = "xeon sensors"; ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strcpy(new_client->name, client_name); ++ data->type = kind; ++ ++ new_client->id = xeontemp_id++; ++ data->valid = 0; ++ init_MUTEX(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto error3; ++ ++ /* Register a new directory entry with module sensors */ ++ if ((i = i2c_register_entry(new_client, type_name, ++ xeontemp_dir_table_template)) < 0) { ++ err = i; ++ goto error4; ++ } ++ data->sysctl_id = i; ++ ++ xeontemp_init_client(new_client); ++ return 0; ++ ++ error4: ++ i2c_detach_client(new_client); ++ error3: ++ error1: ++ kfree(data); ++ error0: ++ return err; ++} ++ ++static void xeontemp_init_client(struct i2c_client *client) ++{ ++ /* Enable ADC and disable suspend mode */ ++ xeontemp_write_value(client, XEONTEMP_REG_CONFIG_W, 0); ++ /* Set Conversion rate to 1/sec (this can be tinkered with) */ ++ xeontemp_write_value(client, XEONTEMP_REG_CONV_RATE_W, 0x04); ++} ++ ++static int xeontemp_detach_client(struct i2c_client *client) ++{ ++ ++ int err; ++ ++ i2c_deregister_entry(((struct xeontemp_data *) (client->data))-> ++ sysctl_id); ++ ++ if ((err = i2c_detach_client(client))) { ++ printk ++ ("xeontemp.o: Client deregistration failed, client not detached.\n"); ++ return err; ++ } ++ ++ kfree(client->data); ++ ++ return 0; ++ ++} ++ ++ ++/* All registers are byte-sized */ ++static int xeontemp_read_value(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* only update value if read succeeded; set fail bit if failed */ ++static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask) ++{ ++ int i; ++ struct xeontemp_data *data = client->data; ++ ++ i = i2c_smbus_read_byte_data(client, reg); ++ if (i < 0) { ++ data->fail |= mask; ++ return i; ++ } ++ *val = i; ++ return 0; ++} ++ ++static int xeontemp_write_value(struct i2c_client *client, u8 reg, u16 value) ++{ ++ if (read_only > 0) ++ return 0; ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static void xeontemp_update_client(struct i2c_client *client) ++{ ++ struct xeontemp_data *data = client->data; ++ ++ down(&data->update_lock); ++ ++ if ((jiffies - data->last_updated > HZ + HZ / 2) || ++ (jiffies < data->last_updated) || !data->valid) { ++ ++#ifdef DEBUG ++ printk("Starting xeontemp update\n"); ++#endif ++ ++ data->fail = 0; ++ xeontemp_rd_good(&(data->remote_temp), client, ++ XEONTEMP_REG_REMOTE_TEMP, XEONTEMP_ALARM_RTEMP); ++ xeontemp_rd_good(&(data->remote_temp_os), client, ++ XEONTEMP_REG_REMOTE_TOS_R, XEONTEMP_ALARM_RTEMP); ++ xeontemp_rd_good(&(data->remote_temp_hyst), client, ++ XEONTEMP_REG_REMOTE_THYST_R, ++ XEONTEMP_ALARM_RTEMP); ++ data->alarms = XEONTEMP_ALARM_ALL; ++ if (!xeontemp_rd_good(&(data->alarms), client, ++ XEONTEMP_REG_STATUS, 0)) ++ data->alarms &= XEONTEMP_ALARM_ALL; ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ up(&data->update_lock); ++} ++ ++void xeontemp_remote_temp(struct i2c_client *client, int operation, ++ int ctl_name, int *nrels_mag, long *results) ++{ ++ struct xeontemp_data *data = client->data; ++ ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ xeontemp_update_client(client); ++ results[0] = TEMP_FROM_REG(data->remote_temp_os); ++ results[1] = TEMP_FROM_REG(data->remote_temp_hyst); ++ results[2] = TEMP_FROM_REG(data->remote_temp); ++ *nrels_mag = 3; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ if (*nrels_mag >= 1) { ++ data->remote_temp_os = TEMP_TO_REG(results[0]); ++ xeontemp_write_value(client, ++ XEONTEMP_REG_REMOTE_TOS_W, ++ data->remote_temp_os); ++ } ++ if (*nrels_mag >= 2) { ++ data->remote_temp_hyst = TEMP_TO_REG(results[1]); ++ xeontemp_write_value(client, ++ XEONTEMP_REG_REMOTE_THYST_W, ++ data->remote_temp_hyst); ++ } ++ } ++} ++ ++void xeontemp_alarms(struct i2c_client *client, int operation, int ctl_name, ++ int *nrels_mag, long *results) ++{ ++ struct xeontemp_data *data = client->data; ++ if (operation == SENSORS_PROC_REAL_INFO) ++ *nrels_mag = 0; ++ else if (operation == SENSORS_PROC_REAL_READ) { ++ xeontemp_update_client(client); ++ results[0] = data->alarms | data->fail; ++ *nrels_mag = 1; ++ } else if (operation == SENSORS_PROC_REAL_WRITE) { ++ /* Can't write to it */ ++ } ++} ++ ++static int __init sm_xeontemp_init(void) ++{ ++ printk(KERN_INFO "xeontemp.o version %s (%s)\n", LM_VERSION, LM_DATE); ++ return i2c_add_driver(&xeontemp_driver); ++} ++ ++static void __exit sm_xeontemp_exit(void) ++{ ++ i2c_del_driver(&xeontemp_driver); ++} ++ ++MODULE_AUTHOR ++ ("Frodo Looijaard and Philip Edelbrock "); ++MODULE_DESCRIPTION("xeontemp driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(read_only, "i"); ++MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); ++ ++module_init(sm_xeontemp_init) ++module_exit(sm_xeontemp_exit) +--- linux-old/include/linux/sensors_compat.h Thu Jan 1 00:00:00 1970 ++++ linux/include/linux/sensors_compat.h Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,31 @@ ++/* ++ * Stolen from kernel 2.5.69 ++ * device.h - generic, centralized driver model ++ * To make it easier to backport from 2.5 ++ * ++ * Copyright (c) 2001-2003 Patrick Mochel ++ * ++ */ ++ ++#ifndef _SENSORS_COMPAT_H_ ++#define _SENSORS_COMPAT_H_ ++ ++/* debugging and troubleshooting/diagnostic helpers. */ ++#define dev_printk(level, dev, format, arg...) \ ++ printk(level "%s: " format , (dev)->name , ## arg) ++ ++#ifdef DEBUG ++#define dev_dbg(dev, format, arg...) \ ++ dev_printk(KERN_DEBUG , dev , format , ## arg) ++#else ++#define dev_dbg(dev, format, arg...) do {} while (0) ++#endif ++ ++#define dev_err(dev, format, arg...) \ ++ dev_printk(KERN_ERR , dev , format , ## arg) ++#define dev_info(dev, format, arg...) \ ++ dev_printk(KERN_INFO , dev , format , ## arg) ++#define dev_warn(dev, format, arg...) \ ++ dev_printk(KERN_WARNING , dev , format , ## arg) ++ ++#endif /* _SENSORS_COMPAT_H_ */ +--- linux-old/include/linux/sensors_vid.h Thu Jan 1 00:00:00 1970 ++++ linux/include/linux/sensors_vid.h Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,95 @@ ++/* ++ sensors_vid.h - Part of lm_sensors, Linux kernel modules for hardware ++ monitoring ++ Copyright (c) 2002-2004 Mark D. Studebaker ++ With assistance from Trent Piepho ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ This file contains common code for decoding VID pins. ++ This file is #included in various sensor chip drivers. ++ As the user is unlikely to load more than one driver which ++ includes this code we don't worry about the wasted space. ++ References: VRM x.y DC-DC Converter Design Guidelines, ++ VRD 10.0 Design Guide, ++ available at http://developer.intel.com ++*/ ++ ++/* ++ AMD Opteron processors don't follow the Intel VRM spec. ++ I'm going to "make up" 2.4 as the VRM spec for the Opterons. ++ No good reason just a mnemonic for the 24x Opteron processor ++ series ++ ++ Opteron VID encoding is: ++ ++ 00000 = 1.550 V ++ 00001 = 1.525 V ++ . . . . ++ 11110 = 0.800 V ++ 11111 = 0.000 V (off) ++ */ ++ ++/* ++ Legal val values 00 - 1F except for VRD 10.0, 0x00-0x3f. ++ vrm is the Intel VRM document version. ++ Note: vrm version is scaled by 10 and the return value is scaled by 1000 ++ to avoid floating point in the kernel. ++*/ ++ ++#define DEFAULT_VRM 82 ++ ++static inline int vid_from_reg(int val, int vrm) ++{ ++ int vid; ++ ++ switch(vrm) { ++ ++ case 100: /* VRD 10.0 */ ++ if((val & 0x1f) == 0x1f) ++ return 0; ++ if((val & 0x1f) <= 0x09 || val == 0x0a) ++ vid = 10875 - (val & 0x1f) * 250; ++ else ++ vid = 18625 - (val & 0x1f) * 250; ++ if(val & 0x20) ++ vid -= 125; ++ vid /= 10; /* only return 3 dec. places for now */ ++ return vid; ++ ++ case 24: /* Opteron processor */ ++ return(val == 0x1f ? 0 : 1550 - val * 25); ++ ++ case 91: /* VRM 9.1 */ ++ case 90: /* VRM 9.0 */ ++ return(val == 0x1f ? 0 : ++ 1850 - val * 25); ++ ++ case 85: /* VRM 8.5 */ ++ return((val & 0x10 ? 25 : 0) + ++ ((val & 0x0f) > 0x04 ? 2050 : 1250) - ++ ((val & 0x0f) * 50)); ++ ++ case 84: /* VRM 8.4 */ ++ val &= 0x0f; ++ /* fall through */ ++ default: /* VRM 8.2 */ ++ return(val == 0x1f ? 0 : ++ val & 0x10 ? 5100 - (val) * 100 : ++ 2050 - (val) * 50); ++ } ++} +--- linux-old/drivers/sensors/Config.in Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/Config.in Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,62 @@ ++# ++# Sensor device configuration ++# All depend on CONFIG_I2C_PROC. ++# ISA-only devices depend on CONFIG_I2C_ISA also. ++# ++ ++if [ "$CONFIG_I2C" = "m" -o "$CONFIG_I2C" = "y" ] ; then ++if [ "$CONFIG_I2C_PROC" = "m" -o "$CONFIG_I2C_PROC" = "y" ] ; then ++ mainmenu_option next_comment ++ comment 'Hardware sensors support' ++ ++ dep_mbool 'Hardware sensors support' CONFIG_SENSORS $CONFIG_I2C $CONFIG_I2C_PROC ++ ++ if [ "$CONFIG_SENSORS" != "n" ]; then ++ dep_tristate ' Analog Devices ADM1021 and compatibles' CONFIG_SENSORS_ADM1021 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Analog Devices ADM1024' CONFIG_SENSORS_ADM1024 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Analog Devices ADM1025' CONFIG_SENSORS_ADM1025 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Analog Devices ADM1026' CONFIG_SENSORS_ADM1026 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Analog Devices ADM9240 and compatibles' CONFIG_SENSORS_ADM9240 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Asus ASB100' CONFIG_SENSORS_ASB100 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Dallas DS1621 and DS1625' CONFIG_SENSORS_DS1621 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Fujitsu-Siemens Poseidon' CONFIG_SENSORS_FSCPOS $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Fujitsu-Siemens Scylla' CONFIG_SENSORS_FSCSCY $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Genesys Logic GL518SM' CONFIG_SENSORS_GL518SM $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Genesys Logic GL520SM' CONFIG_SENSORS_GL520SM $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' HP Maxilife' CONFIG_SENSORS_MAXILIFE $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Intel Xeon Thermal Sensor' CONFIG_SENSORS_XEONTEMP $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' ITE 8705/8712, SiS950' CONFIG_SENSORS_IT87 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Maxim MAX6650, MAX6651' CONFIG_SENSORS_MAX6650 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Myson MTP008' CONFIG_SENSORS_MTP008 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM75 and compatibles' CONFIG_SENSORS_LM75 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM78' CONFIG_SENSORS_LM78 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM80' CONFIG_SENSORS_LM80 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM83' CONFIG_SENSORS_LM83 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM85, Analog Devices ADM1027' CONFIG_SENSORS_LM85 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM87' CONFIG_SENSORS_LM87 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM90 and compatibles' CONFIG_SENSORS_LM90 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor LM92' CONFIG_SENSORS_LM92 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' National Semiconductor PC8736x Sensors' CONFIG_SENSORS_PC87360 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Silicon Integrated Systems Corp. SiS5595' CONFIG_SENSORS_SIS5595 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' SMSC47M1xx Integrated Sensors' CONFIG_SENSORS_SMSC47M1 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' Texas Instruments THMC50 and compatibles' CONFIG_SENSORS_THMC50 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' VIA 686a Integrated Hardware Monitor' CONFIG_SENSORS_VIA686A $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' VIA VT1211 Integrated Sensors' CONFIG_SENSORS_VT1211 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' VIA VT8231 Integrated Sensors' CONFIG_SENSORS_VT8231 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F' CONFIG_SENSORS_W83781D $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Winbond W83627HF, W83627THF, W83697HF' CONFIG_SENSORS_W83627HF $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA ++ dep_tristate ' Winbond W83L785TS-S' CONFIG_SENSORS_W83L785TS $CONFIG_I2C $CONFIG_I2C_PROC ++ bool 'Other I2C devices' CONFIG_SENSORS_OTHER ++ if [ "$CONFIG_SENSORS_OTHER" = "y" ] ; then ++ dep_tristate ' Brooktree BT869 Video Modulator' CONFIG_SENSORS_BT869 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' DDC Monitor EDID EEPROM' CONFIG_SENSORS_DDCMON $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' EEprom (DIMM) reader ' CONFIG_SENSORS_EEPROM $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Matrix-Orbital LCD Displays' CONFIG_SENSORS_MATORB $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Philips PCF8574 Parallel I/O' CONFIG_SENSORS_PCF8574 $CONFIG_I2C $CONFIG_I2C_PROC ++ dep_tristate ' Philips PCF8591 D/A and A/D' CONFIG_SENSORS_PCF8591 $CONFIG_I2C $CONFIG_I2C_PROC ++ fi ++ fi ++ endmenu ++fi ++fi ++ +--- linux-old/Makefile Sat Aug 14 18:38:44 2004 ++++ linux/Makefile Mon Dec 13 20:18:54 2004 +@@ -195,6 +195,7 @@ + DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o + DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o + ++DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensor.o + DRIVERS := $(DRIVERS-y) + + +--- linux-old/drivers/Makefile Mon Nov 17 01:07:35 2003 ++++ linux/drivers/Makefile Mon Dec 13 20:18:54 2004 +@@ -8,7 +8,7 @@ + + mod-subdirs := dio hil mtd sbus video macintosh usb input telephony ide \ + message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ +- fc4 net/hamradio i2c acpi bluetooth usb/gadget ++ fc4 net/hamradio i2c acpi bluetooth usb/gadget sensors + + subdir-y := parport char block net sound misc media cdrom hotplug + subdir-m := $(subdir-y) +@@ -45,6 +45,7 @@ + # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set -- ch + subdir-$(CONFIG_HAMRADIO) += net/hamradio + subdir-$(CONFIG_I2C) += i2c ++subdir-$(CONFIG_SENSORS) += sensors + subdir-$(CONFIG_ACPI_BOOT) += acpi + + subdir-$(CONFIG_BLUEZ) += bluetooth +--- linux-old/drivers/sensors/Makefile Thu Jan 1 00:00:00 1970 ++++ linux/drivers/sensors/Makefile Mon Dec 13 20:18:54 2004 +@@ -0,0 +1,49 @@ ++# ++# Makefile for the kernel hardware sensors drivers. ++# ++ ++MOD_LIST_NAME := SENSORS_MODULES ++O_TARGET := sensor.o ++ ++obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o ++obj-$(CONFIG_SENSORS_ADM1024) += adm1024.o ++obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o ++obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o ++obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o ++obj-$(CONFIG_SENSORS_ASB100) += asb100.o ++obj-$(CONFIG_SENSORS_BT869) += bt869.o ++obj-$(CONFIG_SENSORS_DDCMON) += ddcmon.o ++obj-$(CONFIG_SENSORS_DS1621) += ds1621.o ++obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o ++obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o ++obj-$(CONFIG_SENSORS_FSCSCY) += fscscy.o ++obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o ++obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o ++obj-$(CONFIG_SENSORS_IT87) += it87.o ++obj-$(CONFIG_SENSORS_LM75) += lm75.o ++obj-$(CONFIG_SENSORS_LM78) += lm78.o ++obj-$(CONFIG_SENSORS_LM80) += lm80.o ++obj-$(CONFIG_SENSORS_LM83) += lm83.o ++obj-$(CONFIG_SENSORS_LM85) += lm85.o ++obj-$(CONFIG_SENSORS_LM87) += lm87.o ++obj-$(CONFIG_SENSORS_LM90) += lm90.o ++obj-$(CONFIG_SENSORS_LM92) += lm92.o ++obj-$(CONFIG_SENSORS_MAX6650) += max6650.o ++obj-$(CONFIG_SENSORS_MAXILIFE) += maxilife.o ++obj-$(CONFIG_SENSORS_MTP008) += mtp008.o ++obj-$(CONFIG_SENSORS_PC87360) += pc87360.o ++obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o ++obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o ++obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o ++obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o ++obj-$(CONFIG_SENSORS_THMC50) += thmc50.o ++obj-$(CONFIG_SENSORS_VIA686A) += via686a.o ++obj-$(CONFIG_SENSORS_VT1211) += vt1211.o ++obj-$(CONFIG_SENSORS_VT8231) += vt8231.o ++obj-$(CONFIG_SENSORS_W83781D) += w83781d.o ++obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o ++obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o ++obj-$(CONFIG_SENSORS_XEONTEMP) += xeontemp.o ++ ++include $(TOPDIR)/Rules.make ++ +--- linux-old/drivers/char/Config.in Sat Aug 14 18:38:49 2004 ++++ linux/drivers/char/Config.in Mon Dec 13 20:18:54 2004 +@@ -191,6 +191,8 @@ + + source drivers/i2c/Config.in + ++source drivers/sensors/Config.in ++ + mainmenu_option next_comment + comment 'Mice' + tristate 'Bus Mouse Support' CONFIG_BUSMOUSE +--- linux-old/drivers/i2c/Config.in Wed Jul 7 00:38:02 2004 ++++ linux/drivers/i2c/Config.in Mon Dec 13 20:18:54 2004 +@@ -63,6 +63,30 @@ + fi + + # This is needed for automatic patch generation: sensors code starts here ++ bool 'I2C mainboard interfaces' CONFIG_I2C_MAINBOARD ++ if [ "$CONFIG_I2C_MAINBOARD" = "y" ]; then ++ dep_tristate ' Acer Labs ALI 1535' CONFIG_I2C_ALI1535 $CONFIG_I2C ++ dep_tristate ' Acer Labs ALI 1533 and 1543C' CONFIG_I2C_ALI15X3 $CONFIG_I2C ++ dep_tristate ' Apple Hydra Mac I/O' CONFIG_I2C_HYDRA $CONFIG_I2C_ALGOBIT ++ dep_tristate ' AMD 756/766/768/8111' CONFIG_I2C_AMD756 $CONFIG_I2C ++ dep_tristate ' AMD 8111 SMBus 2.0' CONFIG_I2C_AMD8111 $CONFIG_I2C ++ if [ "$CONFIG_ALPHA" = "y" ]; then ++ dep_tristate ' DEC Tsunami I2C interface' CONFIG_I2C_TSUNAMI $CONFIG_I2C_ALGOBIT ++ fi ++ dep_tristate ' Intel 82801AA, AB, BA, DB' CONFIG_I2C_I801 $CONFIG_I2C ++ dep_tristate ' Intel i810AA/AB/E and i815' CONFIG_I2C_I810 $CONFIG_I2C_ALGOBIT ++ dep_tristate ' Intel 82371AB PIIX4(E), 443MX, ServerWorks OSB4/CSB5, SMSC Victory66' CONFIG_I2C_PIIX4 $CONFIG_I2C ++ dep_tristate ' Nvidia Nforce2' CONFIG_I2C_NFORCE2 $CONFIG_I2C ++ dep_tristate ' SiS 5595' CONFIG_I2C_SIS5595 $CONFIG_I2C ++ dep_tristate ' SiS 630/730' CONFIG_I2C_SIS630 $CONFIG_I2C ++ dep_tristate ' SiS 645/961,645DX/961,735' CONFIG_I2C_SIS645 $CONFIG_I2C $CONFIG_HOTPLUG ++ dep_tristate ' Savage 4' CONFIG_I2C_SAVAGE4 $CONFIG_I2C_ALGOBIT ++ dep_tristate ' VIA Technologies, Inc. VT82C586B' CONFIG_I2C_VIA $CONFIG_I2C_ALGOBIT ++ dep_tristate ' VIA Technologies, Inc. VT596A/B, 686A/B, 8231, 8233, 8233A, 8235' CONFIG_I2C_VIAPRO $CONFIG_I2C ++ dep_tristate ' Voodoo3 I2C interface' CONFIG_I2C_VOODOO3 $CONFIG_I2C_ALGOBIT ++ dep_tristate ' Pseudo ISA adapter (for some hardware sensors)' CONFIG_I2C_ISA $CONFIG_I2C ++ fi ++ + # This is needed for automatic patch generation: sensors code ends here + + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C +--- linux-old/drivers/i2c/Makefile Wed Jul 7 00:38:02 2004 ++++ linux/drivers/i2c/Makefile Mon Dec 13 20:18:54 2004 +@@ -28,6 +28,24 @@ + obj-$(CONFIG_I2C_ALGO_AU1550) += i2c-algo-au1550.o i2c-au1550.o + + # This is needed for automatic patch generation: sensors code starts here ++obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o ++obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o ++obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o ++obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o ++obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o ++obj-$(CONFIG_I2C_I801) += i2c-i801.o ++obj-$(CONFIG_I2C_I810) += i2c-i810.o ++obj-$(CONFIG_I2C_ISA) += i2c-isa.o ++obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o ++obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o ++obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o ++obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o ++obj-$(CONFIG_I2C_SIS645) += i2c-sis645.o ++obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o ++obj-$(CONFIG_I2C_TSUNAMI) += i2c-tsunami.o ++obj-$(CONFIG_I2C_VIA) += i2c-via.o ++obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o ++obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o + # This is needed for automatic patch generation: sensors code ends here + + include $(TOPDIR)/Rules.make +--- linux-old/Documentation/Configure.help Mon Dec 13 20:09:52 2004 ++++ linux/Documentation/Configure.help Mon Dec 13 20:18:56 2004 +@@ -29449,4 +29449,527 @@ + # fill-prefix:" " + # adaptive-fill:nil + # fill-column:70 ++I2C mainboard interfaces ++CONFIG_I2C_MAINBOARD ++ Many modern mainboards have some kind of I2C interface integrated. This ++ is often in the form of a SMBus, or System Management Bus, which is ++ basically the same as I2C but which uses only a subset of the I2C ++ protocol. ++ ++ You will also want the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Acer Labs ALI 1535 ++CONFIG_I2C_ALI1535 ++ If you say yes to this option, support will be included for the Acer ++ Labs ALI 1535 mainboard I2C interface. This can also be ++ built as a module. ++ ++Acer Labs ALI 1533 and 1543C ++CONFIG_I2C_ALI15X3 ++ If you say yes to this option, support will be included for the Acer ++ Labs ALI 1533 and 1543C mainboard I2C interfaces. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++AMD 756/766/768/8111 ++CONFIG_I2C_AMD756 ++ If you say yes to this option, support will be included for the AMD ++ 756/766/768/8111 mainboard I2C interfaces. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++AMD 8111 SMBus 2.0 ++CONFIG_I2C_AMD8111 ++ If you say yes to this option, support will be included for the AMD ++ 8111 mainboard SMBus 2.0 interface. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Apple Hydra Mac I/O ++CONFIG_I2C_HYDRA ++ If you say yes to this option, support will be included for the ++ Hydra mainboard I2C interface. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++Intel I801 ++CONFIG_I2C_I801 ++ If you say yes to this option, support will be included for the ++ Intel I801 mainboard I2C interfaces. "I810" mainboard sensor chips are ++ generally located on the I801's I2C bus. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Intel I810/I815 based Mainboard ++CONFIG_I2C_I810 ++ If you say yes to this option, support will be included for the ++ Intel I810/I815 mainboard I2C interfaces. The I2C busses these chips ++ are generally used only for video devices. For "810" mainboard sensor ++ chips, use the I801 I2C driver instead. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Intel 82371AB PIIX4(E) / ServerWorks OSB4 and CSB5 ++CONFIG_I2C_PIIX4 ++ If you say yes to this option, support will be included for the ++ Intel PIIX4, PIIX4E, and 443MX, Serverworks OSB4/CSB5, ++ and SMSC Victory66 mainboard ++ I2C interfaces. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Nvidia Nforce2 based Mainboard ++CONFIG_I2C_NFORCE2 ++ If you say yes to this option, support will be included for the ++ Nvidia Nforce2 family of mainboard I2C interfaces. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Silicon Integrated Systems Corp. SiS5595 based Mainboard ++CONFIG_I2C_SIS5595 ++ If you say yes to this option, support will be included for the ++ SiS5595 mainboard I2C interfaces. For integrated sensors on the ++ Sis5595, use CONFIG_SENSORS_SIS5595. This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++Silicon Integrated Systems Corp. SiS630/730 based Mainboard ++CONFIG_I2C_SIS630 ++ If you say yes to this option, support will be included for the SiS 630 ++ and 730 mainboard I2C interfaces. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++Silicon Integrated Systems Corp. SiS645/961,645DX/961,735 based Mainboard ++CONFIG_I2C_SIS645 ++ If you say yes to this option, support will be included for the SiS 645/961, ++ 645DX/961 and 735 mainboard I2C interfaces. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++VIA Technologies, Inc. VT82C586B ++CONFIG_I2C_VIA ++ If you say yes to this option, support will be included for the VIA ++ Technologies I2C adapter found on some motherboards. This can also ++ be built as a module which can be inserted and removed while the ++ kernel is running. ++ ++VIA Technologies, Inc. VT82C596, 596B, 686A/B, 8233, 8235 ++CONFIG_I2C_VIAPRO ++ If you say yes to this option, support will be included for the VIA ++ Technologies I2C adapter on these chips. For integrated sensors on the ++ Via 686A/B, use CONFIG_SENSORS_VIA686A. This can also be ++ be built as a module which can be inserted and removed while the ++ kernel is running. ++ ++3DFX Banshee / Voodoo3 ++CONFIG_I2C_VOODOO3 ++ If you say yes to this option, support will be included for the ++ 3DFX Banshee and Voodoo3 I2C interfaces. The I2C busses on the these ++ chips are generally used only for video devices. ++ This can also be ++ built as a module which can be inserted and removed while the kernel ++ is running. ++ ++DEC Tsunami 21272 ++CONFIG_I2C_TSUNAMI ++ If you say yes to this option, support will be included for the DEC ++ Tsunami chipset I2C adapter. Requires the Alpha architecture; ++ do not enable otherwise. This can also be built as a module which ++ can be inserted and removed while the kernel is running. ++ ++Pseudo ISA adapter (for hardware sensors modules) ++CONFIG_I2C_ISA ++ This provides support for accessing some hardware sensor chips over ++ the ISA bus rather than the I2C or SMBus. If you want to do this, ++ say yes here. This feature can also be built as a module which can ++ be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Analog Devices ADM1021 and compatibles ++CONFIG_SENSORS_ADM1021 ++ If you say yes here you get support for Analog Devices ADM1021 ++ and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, ++ Genesys Logic GL523SM, National Semi LM84, TI THMC10 and Onsemi ++ MC1066. This can also be built as a module which can be inserted ++ and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Analog Devices ADM1024 ++CONFIG_SENSORS_ADM1024 ++ If you say yes here you get support for Analog Devices ADM1024 sensor ++ chips. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Analog Devices ADM1025 ++CONFIG_SENSORS_ADM1025 ++ If you say yes here you get support for Analog Devices ADM1025 sensor ++ chips. This can also be built as a module which can be inserted and ++ removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Analog Devices ADM1026 ++CONFIG_SENSORS_ADM1026 ++ If you say yes here you get support for Analog Devices ADM1026 sensor ++ chips. This can also be built as a module which can be inserted and ++ removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Analog Devices ADM9240 and compatibles ++CONFIG_SENSORS_ADM9240 ++ If you say yes here you get support for Analog Devices ADM9240 ++ sensor chips and clones: the Dallas Semiconductor DS1780 and ++ the National Semiconductor LM81. This can also be built as a ++ module which can be inserted and removed while the kernel is ++ running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Asus ASB100 ++CONFIG_SENSORS_ASB100 ++ If you say yes here you get support for the Asus ASB100 (aka ++ "Bach") sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilities: you can find ++ them in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++Dallas DS1621 and DS1625 ++CONFIG_SENSORS_DS1621 ++ If you say yes here you get support for the Dallas DS1621 and DS1625x ++ sensor chips. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Fujitsu-Siemens Poseidon ++CONFIG_SENSORS_FSCPOS ++ If you say yes here you get support for the Fujitsu-Siemens Poseidon ++ sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Fujitsu-Siemens Scylla ++CONFIG_SENSORS_FSCSCY ++ If you say yes here you get support for the Fujitsu-Siemens Scylla ++ sensor chip. This can also be built as a module. This driver may/should ++ also work with the following Fujitsu-Siemens chips: "Poseidon", ++ "Poseidon II" and "Hydra". You may have to force loading of the module ++ for motherboards in these cases. Be careful - those motherboards have ++ not been tested with this driver. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Genesys Logic GL518SM ++CONFIG_SENSORS_GL518SM ++ If you say yes here you get support for Genesys Logic GL518SM sensor ++ chips. This can also be built as a module which can be inserted and ++ removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Genesys Logic GL520SM ++CONFIG_SENSORS_GL520SM ++ If you say yes here you get support for Genesys Logic GL518SM sensor ++ chips. This can also be built as a module which can be inserted and ++ removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++HP Maxilife ++CONFIG_SENSORS_MAXILIFE ++ If you say yes here you get support for the HP Maxilife ++ sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Intel Xeon Thermal Sensor ++CONFIG_SENSORS_XEONTEMP ++ If you say yes here you get support for the Intel Xeon processor ++ built-in thermal sensor. This can also be built as a module which ++ can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilities: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++ITE 8705, 8712, Sis950 ++CONFIG_SENSORS_IT87 ++ If you say yes here you get support for the ITE 8705 and 8712 and ++ SiS950 sensor chips. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Maxim MAX6650, MAX6651 ++CONFIG_SENSORS_MAX6650 ++ If you say yes here you get support for the Maxim MAX6650 and ++ MAX6651 sensor chips. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Myson MTP008 ++CONFIG_SENSORS_MTP008 ++ If you say yes here you get support for the Myson MTP008 ++ sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM75 and compatibles ++CONFIG_SENSORS_LM75 ++ If you say yes here you get support for National Semiconductor LM75 ++ sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in ++ 9-bit precision mode), and TelCom (now Microchip) TCN75. This can ++ also be built as a module which can be inserted and removed while ++ the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM78 ++CONFIG_SENSORS_LM78 ++ If you say yes here you get support for National Semiconductor LM78 ++ sensor chips family: the LM78-J and LM79. Many clone chips will ++ also work at least somewhat with this driver. This can also be built ++ as a module which can be inserted and removed while the kernel is ++ running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM80 ++CONFIG_SENSORS_LM80 ++ If you say yes here you get support for National Semiconductor LM80 ++ sensor chips. This can also be built as a module which can be ++ inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM83 ++CONFIG_SENSORS_LM83 ++ If you say yes here you get support for the National Semiconductor ++ LM83 sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilities: you can find ++ them in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++National Semiconductor LM85 ++CONFIG_SENSORS_LM85 ++ If you say yes here you get support for National Semiconductor LM85 ++ sensor chips and compatibles. Compatible chips include the Analog ++ Devices ADM1027 and ADT7463 and SMSC EMC6D100 and EMC6D101. This ++ can also be built as a module which can be inserted and removed ++ while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM87 ++CONFIG_SENSORS_LM87 ++ If you say yes here you get support for National Semiconductor LM87 ++ sensor chips. This can also be built as a module which can be ++ inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor LM90 ++CONFIG_SENSORS_LM90 ++ If you say yes here you get support for the National Semiconductor ++ LM90, LM89 and LM99, and Analog Devices ADM1032 sensor chips. This ++ can also be built as a module. ++ ++ You will also need the latest user-space utilities: you can find ++ them in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++National Semiconductor LM92 ++CONFIG_SENSORS_LM92 ++ If you say yes here you get support for National Semiconductor LM92 ++ sensor chips. This can also be built as a module which can be ++ inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++National Semiconductor PC8736x Sensors ++CONFIG_SENSORS_PC87360 ++ If you say yes here you get support for the integrated hardware ++ monitoring in the National Semicoductor PC87360, PC87363, PC87364, ++ PC87365 and PC87366 Super I/O chips. This can also be built as a ++ module which can be inserted and removed while the kernel is ++ running. ++ ++ You will also need the latest user-space utilities: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++Philips PCF8574 ++CONFIG_SENSORS_PCF8574 ++ If you say yes here you get support for the Philips PCF8574 ++ I2C 8-bit Parallel I/O device. ++ This can also be built as a module which can be ++ inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Philips PCF8591 ++CONFIG_SENSORS_PCF8591 ++ If you say yes here you get support for the Philips PCF8591 ++ I2C Quad D/A + Single A/D I/O device. ++ This can also be built as a module which can be ++ inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Silicon Integrated Systems Corp. SiS5595 Sensor ++CONFIG_SENSORS_SIS5595 ++ If you say yes here you get support for the integrated sensors in ++ SiS5595 South Bridges. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++SMSC47M1xx Super I/O Fan Support ++CONFIG_SENSORS_SMSC47M1 ++ If you say yes here you get support for the integrated fan ++ monitoring and control in the SMSC 47M1xx Super I/O chips. ++ This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Texas Instruments THMC50 / Analog Devices ADM1022 ++CONFIG_SENSORS_THMC50 ++ If you say yes here you get support for Texas Instruments THMC50 ++ sensor chips and clones: the Analog Devices ADM1022. ++ This can also be built as a module which ++ can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Via VT82C686A/B ++CONFIG_SENSORS_VIA686A ++ If you say yes here you get support for the integrated sensors in ++ Via 686A/B South Bridges. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Via VT1211 Sensors ++CONFIG_SENSORS_VT1211 ++ If you say yes here you get support for the integrated sensors in ++ the Via VT1211 Super I/O device. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Via VT8231 Sensors ++CONFIG_SENSORS_VT8231 ++ If you say yes here you get support for the integrated sensors in ++ the Via VT8231 device. This can also be built as a module ++ which can be inserted and removed while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Winbond W83781D, W83782D, W83783S, W83627HF, AS99127F ++CONFIG_SENSORS_W83781D ++ If you say yes here you get support for the Winbond W8378x series ++ of sensor chips: the W83781D, W83782D, W83783S and W83682HF, ++ and the similar Asus AS99127F. This ++ can also be built as a module which can be inserted and removed ++ while the kernel is running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ ++Winbond W83627HF, W83627THF, W83697HF ++CONFIG_SENSORS_W83627HF ++ If you say yes here you get support for the Winbond W836x7 series ++ of sensor chips: the Winbond W83627HF, W83627THF and W83697HF. This ++ can also be built as a module which can be inserted and removed ++ while the kernel is running. ++ ++ You will also need the latest user-space utilities: you can find ++ them in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++Winbond W83L785TS-S ++CONFIG_SENSORS_W83L785TS ++ If you say yes here you get support for the Winbond W83L785TS-S ++ sensor chip. This can also be built as a module. ++ ++ You will also need the latest user-space utilities: you can find ++ them in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu/ ++ ++EEprom (DIMM) reader ++CONFIG_SENSORS_EEPROM ++ If you say yes here you get read-only access to the EEPROM data ++ available on modern memory DIMMs, and which could theoretically ++ also be available on other devices. This can also be built as a ++ module which can be inserted and removed while the kernel is ++ running. ++ ++ You will also need the latest user-space utilties: you can find them ++ in the lm_sensors package, which you can download at ++ http://www.lm-sensors.nu ++ + # End: +--- linux-old/MAINTAINERS Sun Oct 3 01:33:28 2004 ++++ linux/MAINTAINERS Mon Dec 13 20:18:56 2004 +@@ -1659,6 +1659,15 @@ + L: linux-ide@vger.kernel.org + S: Supported + ++SENSORS DRIVERS ++P: Frodo Looijaard ++M: frodol@dds.nl ++P: Philip Edelbrock ++M: phil@netroedge.com ++L: sensors@stimpy.netroedge.com ++W: http://www.lm-sensors.nu/ ++S: Maintained ++ + SGI VISUAL WORKSTATION 320 AND 540 + P: Bent Hagemark + M: bh@sgi.com +--- linux-old/arch/i386/kernel/dmi_scan.c Fri Apr 16 03:14:11 2004 ++++ linux/arch/i386/kernel/dmi_scan.c Mon Dec 13 20:18:56 2004 +@@ -15,6 +15,7 @@ + #include "pci-i386.h" + + unsigned long dmi_broken; ++int is_unsafe_smbus; + int is_sony_vaio_laptop; + + struct dmi_header +@@ -371,6 +372,19 @@ + } + + /* ++ * Don't access SMBus on IBM systems which get corrupted eeproms ++ */ ++ ++static __init int disable_smbus(struct dmi_blacklist *d) ++{ ++ if (is_unsafe_smbus == 0) { ++ is_unsafe_smbus = 1; ++ printk(KERN_INFO "%s machine detected. Disabling SMBus accesses.\n", d->ident); ++ } ++ return 0; ++} ++ ++/* + * Check for a Sony Vaio system + * + * On a Sony system we want to enable the use of the sonypi +@@ -675,6 +689,10 @@ + NO_MATCH, NO_MATCH, + } }, + ++ { disable_smbus, "IBM", { ++ MATCH(DMI_SYS_VENDOR, "IBM"), ++ NO_MATCH, NO_MATCH, NO_MATCH ++ } }, + { sony_vaio_laptop, "Sony Vaio", { /* This is a Sony Vaio laptop */ + MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + MATCH(DMI_PRODUCT_NAME, "PCG-"), +--- linux-old/arch/i386/kernel/i386_ksyms.c Fri Apr 16 03:14:11 2004 ++++ linux/arch/i386/kernel/i386_ksyms.c Mon Dec 13 20:18:56 2004 +@@ -179,6 +179,9 @@ + EXPORT_SYMBOL(atomic_dec_and_lock); + #endif + ++extern int is_unsafe_smbus; ++EXPORT_SYMBOL(is_unsafe_smbus); ++ + extern int is_sony_vaio_laptop; + EXPORT_SYMBOL(is_sony_vaio_laptop); + diff --git a/packages/linux/linux-mtx-1-2.4.27/18-i2c-au1x00gpio.patch b/packages/linux/linux-mtx-1-2.4.27/18-i2c-au1x00gpio.patch index e69de29bb2..2dccf681eb 100644 --- a/packages/linux/linux-mtx-1-2.4.27/18-i2c-au1x00gpio.patch +++ b/packages/linux/linux-mtx-1-2.4.27/18-i2c-au1x00gpio.patch @@ -0,0 +1,466 @@ +diff -urN linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/Config.in linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/Config.in +--- linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/Config.in 2004-12-13 18:03:18.000000000 +0100 ++++ linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/Config.in 2004-12-13 18:11:02.000000000 +0100 +@@ -17,6 +17,11 @@ + int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12 + int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13 + fi ++ dep_tristate ' I2C using Au1x00 GPIO pins' CONFIG_I2C_AU1X00GPIO $CONFIG_I2C_ALGOBIT ++ if [ "$CONFIG_I2C_AU1X00GPIO" != "n" ]; then ++ int ' GPIO pin used for SCL' CONFIG_I2C_AU1X00GPIO_SCL 206 ++ int ' GPIO pin used for SDA' CONFIG_I2C_AU1X00GPIO_SDA 207 ++ fi + fi + + dep_tristate 'NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C +diff -urN linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/i2c-au1x00gpio.c linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/i2c-au1x00gpio.c +--- linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/i2c-au1x00gpio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/i2c-au1x00gpio.c 2004-12-13 18:12:50.000000000 +0100 +@@ -0,0 +1,426 @@ ++/* ------------------------------------------------------------------------- */ ++/* i2c-au1x00gpio.c i2c-hw access for Au1x00 GPIO pins. */ ++/* ------------------------------------------------------------------------- */ ++/* Copyright (C) 1995-2000 Michael Stickel ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++/* With some changes from Kyösti Mälkki and even ++ Frodo Looijaard ++ Simon G. Vogl ++*/ ++ ++/* $Id: i2c-au1x00gpio.c,v 1.1.1.2 2004/01/22 15:35:47 br1 Exp $ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#ifndef __exit ++#define __exit __init ++#endif ++ ++ ++struct i2c_au1x00gpio ++{ ++ struct i2c_au1x00gpio *next; ++ ++ short scl_gpio; ++ short sda_gpio; ++ ++ unsigned long scl_mask; ++ unsigned long sda_mask; ++ ++ struct i2c_adapter adapter; ++ struct i2c_algo_bit_data bit_au1x00gpio_data; ++}; ++ ++static struct i2c_au1x00gpio *adapter_list; ++ ++ ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) /* should be reasonable open, close &c. */ ++#define DEB2(x) /* low level debugging - very slow */ ++#define DEBE(x) x /* error messages */ ++ ++/* ----- printer port defines ------------------------------------------*/ ++ ++/* ----- local functions ---------------------------------------------- */ ++ ++ ++//-- Primary GPIO ++static void bit_au1x00gpio_setscl(void *data, int state) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ if (state) ++ au_writel(adapter->scl_mask, SYS_TRIOUTCLR); // Disable Driver: Switch off Transistor => 1 ++ else ++ au_writel(adapter->scl_mask, SYS_OUTPUTCLR); // Clear Output and switch on Transistor => 0 ++} ++ ++ ++static void bit_au1x00gpio_setsda(void *data, int state) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ if (state) ++ au_writel(adapter->sda_mask, SYS_TRIOUTCLR); ++ else ++ au_writel(adapter->sda_mask, SYS_OUTPUTCLR); ++} ++ ++ ++static int bit_au1x00gpio_getscl(void *data) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ return (au_readl(SYS_PINSTATERD) & adapter->scl_mask) ? 1 : 0; ++} ++ ++ ++static int bit_au1x00gpio_getsda(void *data) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ return (au_readl(SYS_PINSTATERD) & adapter->sda_mask) ? 1 : 0; ++} ++ ++ ++ ++ ++/*-- ++ *-- Functions for accessing GPIO-2 ++ *-- ++ */ ++static void bit_au1x00gpio2_setscl(void *data, int state) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ if (state) ++ { ++ au_writel(au_readl(GPIO2_DIR) & ~adapter->scl_mask, GPIO2_DIR); ++ } ++ else ++ { ++ au_writel(au_readl(GPIO2_OUTPUT) & ~adapter->scl_mask, GPIO2_OUTPUT); ++ au_writel(au_readl(GPIO2_DIR) | adapter->scl_mask, GPIO2_DIR); ++ } ++} ++ ++static void bit_au1x00gpio2_setsda(void *data, int state) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ if (state) ++ { ++ au_writel(au_readl(GPIO2_DIR) & ~adapter->sda_mask, GPIO2_DIR); ++ } ++ else ++ { ++ au_writel(au_readl(GPIO2_OUTPUT) & ~adapter->sda_mask, GPIO2_OUTPUT); ++ au_writel(au_readl(GPIO2_DIR) | adapter->sda_mask, GPIO2_DIR); ++ } ++} ++ ++static int bit_au1x00gpio2_getscl(void *data) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ return (au_readl(GPIO2_PINSTATE) & adapter->scl_mask) ? 1 : 0; ++} ++ ++static int bit_au1x00gpio2_getsda(void *data) ++{ ++ struct i2c_au1x00gpio * adapter = (struct i2c_au1x00gpio *)data; ++ return (au_readl(GPIO2_PINSTATE) & adapter->sda_mask) ? 1 : 0; ++} ++ ++ ++ ++static int check_i2c_au1x00gpio_adapter(struct i2c_au1x00gpio *adapter) ++{ ++ int state = 0; ++ ++ adapter->bit_au1x00gpio_data.setsda (adapter, 1); ++ adapter->bit_au1x00gpio_data.setscl (adapter, 1); ++ ++ if (adapter->bit_au1x00gpio_data.getsda(adapter)==0) ++ { ++ printk ("i2c-au1x00gpio: sda line should read 1 but reads 0\n"); ++ state = -1; ++ } ++ if (adapter->bit_au1x00gpio_data.getscl(adapter)==0) ++ { ++ printk ("i2c-au1x00gpio: scl line should read 1 but reads 0\n"); ++ state = -1; ++ } ++ ++ ++ adapter->bit_au1x00gpio_data.setsda (adapter, 0); ++ adapter->bit_au1x00gpio_data.setscl (adapter, 0); ++ ++ if (adapter->bit_au1x00gpio_data.getsda(adapter)==1) ++ { ++ printk ("i2c-au1x00gpio: sda line should read 0 but reads 1\n"); ++ state = -1; ++ } ++ if (adapter->bit_au1x00gpio_data.getscl(adapter)==1) ++ { ++ printk ("i2c-au1x00gpio: scl line should read 0 but reads 1\n"); ++ state = -1; ++ } ++ ++ if (state==0) ++ printk ("i2c-au1x00gpio: adapter with scl=GPIO%d,sda=GPIO%d is working\n", ++ adapter->scl_gpio, adapter->sda_gpio ++ ); ++ return state; ++} ++ ++ ++ ++#if 0 ++static int bit_au1x00gpio_reg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static int bit_au1x00gpio_unreg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static void bit_au1x00gpio_inc_use(struct i2c_adapter *adap) ++{ ++ MOD_INC_USE_COUNT; ++} ++ ++static void bit_au1x00gpio_dec_use(struct i2c_adapter *adap) ++{ ++ MOD_DEC_USE_COUNT; ++} ++#endif ++ ++ ++ ++static struct i2c_algo_bit_data bit_au1x00gpio_data = { ++ .setsda = bit_au1x00gpio_setsda, ++ .setscl = bit_au1x00gpio_setscl, ++ .getsda = bit_au1x00gpio_getsda, ++ .getscl = bit_au1x00gpio_getscl, ++ .udelay = 80, ++ .mdelay = 80, ++ .timeout = HZ ++}; ++ ++ ++static struct i2c_adapter bit_au1x00gpio_ops = { ++ .owner = THIS_MODULE, ++ .name = "Au1x00 GPIO I2C adapter", ++ .id = I2C_HW_B_AU1x00GPIO, ++}; ++ ++ ++ ++/* ++ * scl_gpio: ++ * 0..31 for primary GPIO's ++ * 200..215 for secondary GPIO's ++ * ++ * sda_gpio: ++ * 0..31 for primary GPIO's ++ * 200..215 for secondary GPIO's ++ * ++ * You can even mix primary and secondary GPIO's. ++ * E.g.: i2c_au1x00gpio_create(4,206); ++ */ ++ ++static int i2c_au1x00gpio_create (int scl_gpio, int sda_gpio) ++{ ++ if ((scl_gpio < 32 || (scl_gpio >= 200 && scl_gpio <= 215)) && ++ (scl_gpio < 32 || (scl_gpio >= 200 && scl_gpio <= 215))) ++ { ++ struct i2c_au1x00gpio *adapter = kmalloc(sizeof(struct i2c_au1x00gpio), ++ GFP_KERNEL); ++ if (!adapter) { ++ printk(KERN_ERR "i2c-au1x00-gpio: Unable to malloc.\n"); ++ return -1; ++ } ++ ++ printk(KERN_DEBUG "i2c-au1x00-gpio.o: attaching to SCL=GPIO%d, SDA=GPIO%d\n", ++ scl_gpio, sda_gpio); ++ ++ memset (adapter, 0, sizeof(struct i2c_au1x00gpio)); ++ ++ adapter->adapter = bit_au1x00gpio_ops; ++ ++ adapter->adapter.algo_data = &adapter->bit_au1x00gpio_data; ++ adapter->bit_au1x00gpio_data = bit_au1x00gpio_data; ++ adapter->bit_au1x00gpio_data.data = adapter; ++ ++ adapter->bit_au1x00gpio_data.data = adapter; ++ ++ adapter->scl_gpio = scl_gpio; ++ adapter->sda_gpio = sda_gpio; ++ ++ if (sda_gpio < 32) ++ { ++ adapter->bit_au1x00gpio_data.setsda = bit_au1x00gpio_setsda; ++ adapter->bit_au1x00gpio_data.getsda = bit_au1x00gpio_getsda; ++ adapter->sda_mask = 1<= 200 && sda_gpio <= 215) ++ { ++ adapter->bit_au1x00gpio_data.setsda = bit_au1x00gpio2_setsda; ++ adapter->bit_au1x00gpio_data.getsda = bit_au1x00gpio2_getsda; ++ adapter->sda_mask = 1<<(sda_gpio-200); ++ } ++ ++ ++ if (scl_gpio < 32) ++ { ++ adapter->bit_au1x00gpio_data.setscl = bit_au1x00gpio_setscl; ++ adapter->bit_au1x00gpio_data.getscl = bit_au1x00gpio_getscl; ++ adapter->scl_mask = 1<= 200 && scl_gpio <= 215) ++ { ++ adapter->bit_au1x00gpio_data.setscl = bit_au1x00gpio2_setscl; ++ adapter->bit_au1x00gpio_data.getscl = bit_au1x00gpio2_getscl; ++ adapter->scl_mask = 1<<(scl_gpio-200); ++ } ++ ++ au_writel(0L, SYS_PININPUTEN); ++ if (check_i2c_au1x00gpio_adapter(adapter)==0) ++ { ++ adapter->bit_au1x00gpio_data.setsda (adapter, 1); ++ adapter->bit_au1x00gpio_data.setscl (adapter, 1); ++ ++ if (i2c_bit_add_bus(&adapter->adapter) < 0) ++ { ++ printk(KERN_ERR "i2c-au1x00-gpio: Unable to register with I2C.\n"); ++ kfree(adapter); ++ return -1; /* No good */ ++ } ++ ++ adapter->next = adapter_list; ++ adapter_list = adapter; ++ return 0; ++ } ++ } ++ else ++ printk(KERN_ERR "i2c-au1x00-gpio: Invalid argument scl_gpio=%d, sda_gpio=%d.\n", scl_gpio, sda_gpio); ++ return -1; ++} ++ ++ ++ ++static void i2c_au1x00gpio_delete (int scl_gpio, int sda_gpio) ++{ ++ struct i2c_au1x00gpio *adapter, *prev = NULL; ++ ++ for (adapter = adapter_list; adapter; adapter = adapter->next) ++ { ++ if (adapter->scl_gpio == scl_gpio && ++ adapter->sda_gpio == sda_gpio) ++ { ++ i2c_bit_del_bus(&adapter->adapter); ++ if (prev) ++ prev->next = adapter->next; ++ else ++ adapter_list = adapter->next; ++ kfree(adapter); ++ return; ++ } ++ prev = adapter; ++ } ++} ++ ++ ++ ++ ++ ++#ifndef CONFIG_I2C_AU1X00GPIO_SCL ++#define CONFIG_I2C_AU1X00GPIO_SCL (216) ++#endif ++ ++#ifndef CONFIG_I2C_AU1X00GPIO_SDA ++#define CONFIG_I2C_AU1X00GPIO_SDA (217) ++#endif ++ ++static int au1x00gpiopin_scl = CONFIG_I2C_AU1X00GPIO_SCL; ++static int au1x00gpiopin_sda = CONFIG_I2C_AU1X00GPIO_SDA; ++ ++ ++ ++int __init i2c_bit_au1x00gpio_init(void) ++{ ++ printk(KERN_INFO "i2c-au1x00gpio.o: i2c Au1x00 GPIO adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); ++ ++ if (i2c_au1x00gpio_create (au1x00gpiopin_scl, au1x00gpiopin_sda) == 0) ++ { ++ printk(KERN_INFO "i2c-au1x00gpio.o: registered I2C-Bus for GPIO%d,GPIO%d\n", ++ au1x00gpiopin_scl, au1x00gpiopin_sda ++ ); ++ return 0; ++ } ++ printk(KERN_INFO "i2c-au1x00gpio.o: failed to register I2C-Bus for GPIO%d,GPIO%d\n", ++ au1x00gpiopin_scl, au1x00gpiopin_sda ++ ); ++ return -1; ++} ++ ++ ++void __exit i2c_bit_au1x00gpio_exit(void) ++{ ++ i2c_au1x00gpio_delete (au1x00gpiopin_scl, au1x00gpiopin_sda); ++} ++ ++ ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_PARM(au1x00gpiopin_scl, "i"); ++MODULE_PARM_DESC(au1x00gpiopin_scl, "GPIO pin number used for SCL pin."); ++ ++MODULE_PARM(au1x00gpiopin_sda, "i"); ++MODULE_PARM_DESC(au1x00gpiopin_sda, "GPIO pin number used for SDA pin."); ++ ++MODULE_AUTHOR("Michael Stickel "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for Au1x00 GPIO adapter."); ++MODULE_LICENSE("GPL"); ++ ++ ++#ifdef MODULE ++int init_module(void) ++{ ++ return i2c_bit_au1x00gpio_init(); ++} ++ ++void cleanup_module(void) ++{ ++ i2c_bit_au1x00gpio_exit(); ++} ++#endif +diff -urN linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/Makefile linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/Makefile +--- linux-2.4.27-mtx1-lm_sensors-2.8.8/drivers/i2c/Makefile 2004-12-13 18:03:18.000000000 +0100 ++++ linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/drivers/i2c/Makefile 2004-12-13 18:11:59.000000000 +0100 +@@ -25,6 +25,7 @@ + obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o i2c-sibyte.o + obj-$(CONFIG_I2C_MAX1617) += i2c-max1617.o + obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o ++obj-$(CONFIG_I2C_AU1X00GPIO) += i2c-au1x00gpio.o + obj-$(CONFIG_I2C_ALGO_AU1550) += i2c-algo-au1550.o i2c-au1550.o + + # This is needed for automatic patch generation: sensors code starts here +--- linux-2.4.27-mtx1-lm_sensors-2.8.8/include/linux/i2c-id.h 2004-12-13 21:09:52.000000000 +0100 ++++ linux-2.4.27-mtx1-lm_sensors-2.8.8-i2cau1x00gpio/include/linux/i2c-id.h 2004-12-13 21:36:14.000000000 +0100 +@@ -242,6 +242,7 @@ + #define I2C_HW_B_S3VIA 0x18 /* S3Via ProSavage adapter */ + #define I2C_HW_B_ZR36067 0x19 /* Zoran-36057/36067 based boards */ + #define I2C_HW_B_PCILYNX 0x1a /* TI PCILynx I2C adapter */ ++#define I2C_HW_B_AU1x00GPIO 0x1b /* Au1x00 GPIO adapter */ + + /* --- PCF 8584 based algorithms */ + #define I2C_HW_P_LP 0x00 /* Parallel port interface */ diff --git a/packages/linux/linux-mtx-1-2.4.27/19-kernel-make-depend.diff b/packages/linux/linux-mtx-1-2.4.27/19-kernel-make-depend.diff index e69de29bb2..6fdfe423e8 100644 --- a/packages/linux/linux-mtx-1-2.4.27/19-kernel-make-depend.diff +++ b/packages/linux/linux-mtx-1-2.4.27/19-kernel-make-depend.diff @@ -0,0 +1,11 @@ +--- linux/Makefile~ 2004-02-09 10:35:08.000000000 -0500 ++++ linux/Makefile 2004-02-12 20:38:04.000000000 -0500 +@@ -538,7 +538,7 @@ + ifdef CONFIG_MODVERSIONS + $(MAKE) update-modverfile + endif +- scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend ++ (find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print | xargs -r scripts/mkdep -- ) > .hdepend + scripts/mkdep -- init/*.c > .depend + (find $(TOPDIR) \( -name .depend -o -name .hdepend \) -print | xargs $(AWK) -f scripts/include_deps) > tmp_include_depends + sed -ne 's/^\([^ ].*\):.*/ \1 \\/p' tmp_include_depends > tmp_include_depends_1 diff --git a/packages/linux/linux-mtx-1-2.4.27/20-au1x00_ethernet_tx_stats.diff b/packages/linux/linux-mtx-1-2.4.27/20-au1x00_ethernet_tx_stats.diff index e69de29bb2..5fefd991da 100644 --- a/packages/linux/linux-mtx-1-2.4.27/20-au1x00_ethernet_tx_stats.diff +++ b/packages/linux/linux-mtx-1-2.4.27/20-au1x00_ethernet_tx_stats.diff @@ -0,0 +1,64 @@ +diff -p -u -r1.5.2.33 linux/drivers/net/au1000_eth.c +--- linux/drivers/net/au1000_eth.c 26 Nov 2004 08:40:13 -0000 1.5.2.33 ++++ linux/drivers/net/au1000_eth.c 21 Jan 2005 14:33:47 -0000 +@@ -83,7 +83,7 @@ static void au1000_tx_timeout(struct net + static int au1000_set_config(struct net_device *dev, struct ifmap *map); + static void set_rx_mode(struct net_device *); + static struct net_device_stats *au1000_get_stats(struct net_device *); +-static inline void update_tx_stats(struct net_device *, u32, u32); ++static inline void update_tx_stats(struct net_device *, u32); + static inline void update_rx_stats(struct net_device *, u32); + static void au1000_timer(unsigned long); + static int au1000_ioctl(struct net_device *, struct ifreq *, int); +@@ -1542,14 +1542,11 @@ static void __exit au1000_cleanup_module + + + static inline void +-update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) ++update_tx_stats(struct net_device *dev, u32 status) + { + struct au1000_private *aup = (struct au1000_private *) dev->priv; + struct net_device_stats *ps = &aup->stats; + +- ps->tx_packets++; +- ps->tx_bytes += pkt_len; +- + if (status & TX_FRAME_ABORTED) { + if (dev->if_port == IF_PORT_100BASEFX) { + if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) { +@@ -1582,7 +1579,7 @@ static void au1000_tx_ack(struct net_dev + ptxd = aup->tx_dma_ring[aup->tx_tail]; + + while (ptxd->buff_stat & TX_T_DONE) { +- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); ++ update_tx_stats(dev, ptxd->status); + ptxd->buff_stat &= ~TX_T_DONE; + ptxd->len = 0; + au_sync(); +@@ -1604,6 +1601,7 @@ static void au1000_tx_ack(struct net_dev + static int au1000_tx(struct sk_buff *skb, struct net_device *dev) + { + struct au1000_private *aup = (struct au1000_private *) dev->priv; ++ struct net_device_stats *ps = &aup->stats; + volatile tx_dma_t *ptxd; + u32 buff_stat; + db_dest_t *pDB; +@@ -1623,7 +1621,7 @@ static int au1000_tx(struct sk_buff *skb + return 1; + } + else if (buff_stat & TX_T_DONE) { +- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); ++ update_tx_stats(dev, ptxd->status); + ptxd->len = 0; + } + +@@ -1643,6 +1641,9 @@ static int au1000_tx(struct sk_buff *skb + else + ptxd->len = skb->len; + ++ ps->tx_packets++; ++ ps->tx_bytes += ptxd->len; ++ + ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE; + au_sync(); + dev_kfree_skb(skb); diff --git a/packages/linux/linux-mtx-1-2.4.27/21-mtx-1-watchdog.diff b/packages/linux/linux-mtx-1-2.4.27/21-mtx-1-watchdog.diff index e69de29bb2..b58f2cdd6b 100644 --- a/packages/linux/linux-mtx-1-2.4.27/21-mtx-1-watchdog.diff +++ b/packages/linux/linux-mtx-1-2.4.27/21-mtx-1-watchdog.diff @@ -0,0 +1,231 @@ +diff -Nurb linux/arch/mips/au1000/mtx-1/Makefile linux.wd/arch/mips/au1000/mtx-1/Makefile +--- linux/arch/mips/au1000/mtx-1/Makefile 2004-11-24 12:11:54.000000000 +0100 ++++ linux.wd/arch/mips/au1000/mtx-1/Makefile 2005-03-09 17:58:40.000000000 +0100 +@@ -15,6 +15,6 @@ + + O_TARGET := mtx-1.o + +-obj-y := init.o board_setup.o irqmap.o ++obj-y := init.o board_setup.o irqmap.o mtx-1_watchdog.o + + include $(TOPDIR)/Rules.make +diff -Nurb linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c linux.wd/arch/mips/au1000/mtx-1/mtx-1_watchdog.c +--- linux/arch/mips/au1000/mtx-1/mtx-1_watchdog.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.wd/arch/mips/au1000/mtx-1/mtx-1_watchdog.c 2005-03-09 18:28:06.000000000 +0100 +@@ -0,0 +1,216 @@ ++/* ++ * Driver for the MTX-1 Watchdog. ++ * ++ * (c) Copyright 2005 4G Systems , All Rights Reserved. ++ * http://www.4g-systems.biz ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Neither Michael Stickel nor 4G Systems admit liability nor provide ++ * warranty for any of this software. This material is provided ++ * "AS-IS" and at no charge. ++ * ++ * (c) Copyright 2005 4G Systems ++ * ++ * Release 0.01. ++ * ++ * Author: Michael Stickel michael.stickel@4g-systems.biz ++ * ++ * ++ * The Watchdog is configured to reset the MTX-1 ++ * if it is not triggered for 100 seconds. ++ * It should not be triggered more often than 1.6 seconds. ++ * ++ * A timer triggers the watchdog every 5 seconds, until ++ * it is opened for the first time. After the first open ++ * it MUST be triggered every 2..95 seconds. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#ifndef FALSE ++# define FALSE (0) ++#endif ++ ++#ifndef TRUE ++# define TRUE (!FALSE) ++#endif ++ ++ ++//---------[ Hardware Functions ]----------------- ++ ++static void mtx1_trigger_wd (void) ++{ ++ /* ++ * toggle GPIO2_15 ++ */ ++ ++ u32 tmp = au_readl(GPIO2_DIR); ++ tmp = (tmp & ~(1<<15)) | ((~tmp) & (1<<15)); ++ au_writel (tmp, GPIO2_DIR); ++} ++ ++static void mtx1_enable_wd (void) ++{ ++ au_writel (au_readl(GPIO2_DIR) | (u32)(1<<15), GPIO2_DIR); ++} ++ ++static void mtx1_disable_wd (void) ++{ ++ au_writel (au_readl(GPIO2_DIR) & ~((u32)(1<<15)), GPIO2_DIR); ++} ++ ++ ++//---------[ Timer Functions ]----------------- ++ ++static struct timer_list wd_trigger_timer; ++static char timer_is_running = FALSE; ++ ++static void wd_timer_callback (unsigned long data) ++{ ++ if (timer_is_running) ++ mod_timer (&wd_trigger_timer, jiffies + 5 * HZ); ++ mtx1_trigger_wd (); ++} ++ ++static void start_wd_timer (void) ++{ ++ if (!timer_is_running) { ++ struct timer_list *t = &wd_trigger_timer; ++ ++ init_timer (t); ++ t->function = wd_timer_callback; ++ t->data = (unsigned long)0L; ++ t->expires = jiffies + 5 * HZ; // 5 seconds. ++ add_timer (t); ++ timer_is_running = TRUE; ++ } ++} ++ ++ ++ ++static void stop_wd_timer (void) ++{ ++ if (timer_is_running) { ++ del_timer(&wd_trigger_timer); ++ timer_is_running = FALSE; ++ } ++} ++ ++ ++//---------[ File Functions ]----------------- ++ ++static int mtx1wd_open (struct inode *inode, struct file *file) ++{ ++ if (MINOR(inode->i_rdev)!=WATCHDOG_MINOR) return -ENODEV; ++ MOD_INC_USE_COUNT; ++ ++ // stop the timer, if it is running. It will not be ++ // started again, until the module is loaded again. ++ stop_wd_timer(); ++ ++ // sleep for 2 seconds, to ensure, that the wd is ++ // not triggered more often than every 2 seconds. ++ schedule_timeout (2 * HZ); ++ ++ return 0; ++} ++ ++ ++static int mtx1wd_release (struct inode *inode, struct file *file) { ++ if (MINOR(inode->i_rdev)==WATCHDOG_MINOR) { ++ } ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++ ++static ssize_t mtx1wd_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { ++ if (ppos!=&file->f_pos) ++ return -ESPIPE; ++ ++ if (count) { ++ mtx1_trigger_wd (); ++ return 1; ++ } ++ return 0; ++} ++ ++ ++ ++static struct file_operations mtx1wd_fops = { ++ .owner = THIS_MODULE, ++ .llseek = NULL, ++ .read = NULL, ++ .write = mtx1wd_write, ++ .readdir = NULL, ++ .poll = NULL, ++ .ioctl = NULL, ++ .mmap = NULL, ++ .open = mtx1wd_open, ++ .flush = NULL, ++ .release = mtx1wd_release ++}; ++ ++ ++static struct miscdevice mtx1wd_miscdev = { ++ WATCHDOG_MINOR, ++ "watchdog", ++ &mtx1wd_fops ++}; ++ ++ ++ ++//---------[ Module Functions ]----------------- ++ ++ ++void cleanup_module (void) ++{ ++ // stop the timer, if it is running. ++ stop_wd_timer(); ++ ++ misc_deregister(&mtx1wd_miscdev); ++ ++ mtx1_disable_wd (); ++} ++ ++ ++int __init init_mtx1_watchdog (void) ++{ ++ printk("MTX-1 watchdog driver\n"); ++ ++ mtx1_enable_wd (); ++ ++ //-- trigger it for the first time. ++ //-- We do not exactly know how long it has not been triggered. ++ mtx1_trigger_wd (); ++ ++ misc_register (&mtx1wd_miscdev); ++ ++ // start a timer, that calls mtx1_trigger_wd every 5 seconds. ++ start_wd_timer(); ++ ++ return 0; ++} ++ ++__initcall(init_mtx1_watchdog); ++ ++MODULE_AUTHOR("Michael Stickel"); ++MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); ++MODULE_LICENSE("GPL"); ++EXPORT_NO_SYMBOLS; -- cgit v1.2.3