# vmalloc.patch
# usb-sl811.patch
# orinoco013e.patch
+# bluetooth.patch
# ramses.patch
# ramses-ac97.patch
# ramses-keyb.patch
# Patch managed by http://www.holgerschurig.de/patcher.html
#
+--- linux-2.4.21/CREDITS~bluetooth
++++ linux-2.4.21/CREDITS
+@@ -1348,7 +1348,11 @@
+ N: Marcel Holtmann
+ E: marcel@holtmann.org
+ W: http://www.holtmann.org
+-D: Author of the Linux Bluetooth Subsystem PC Card drivers
++D: Maintainer of the Linux Bluetooth Subsystem
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport protocol driver
++D: Author and maintainer of the Bluetooth HID protocol driver
++D: Various other Bluetooth related patches, cleanups and fixes
+ S: Germany
+
+ N: Rob W. W. Hooft
+@@ -2624,6 +2628,7 @@
+ N: Aristeu Sergio Rozanski Filho
+ E: aris@conectiva.com.br
+ D: Support for EtherExpress 10 ISA (i82595) in eepro driver
++D: User level driver support for input
+ S: Conectiva S.A.
+ S: R. Tocantins, 89 - Cristo Rei
+ S: 80050-430 - Curitiba - ParanĂ¡
+--- linux-2.4.21/Documentation/Configure.help~bluetooth
++++ linux-2.4.21/Documentation/Configure.help
+@@ -14071,6 +14071,15 @@
+ accessible under char device 13:64+ - /dev/input/eventX in a generic
+ way. This is the future ...
+
++CONFIG_INPUT_UINPUT
++ Say Y here if you want to support user level drivers for input
++ subsystem accessible under char device 10:223 - /dev/input/uinput.
++
++ This driver is also available as a module ( = code which can be
++ inserted in and removed from the running kernel whenever you want).
++ The module will be called uinput.o. If you want to compile it as a
++ module, say M here and read <file:Documentation/modules.txt>.
++
+ USB Scanner support
+ CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your computer's
+@@ -21670,21 +21679,21 @@
+
+ Linux Bluetooth subsystem consist of several layers:
+ BlueZ Core (HCI device and connection manager, scheduler)
+- HCI Device drivers (interface to the hardware)
+- L2CAP Module (L2CAP protocol)
+- SCO Module (SCO links)
+- RFCOMM Module (RFCOMM protocol)
+- BNEP Module (BNEP protocol)
++ HCI Device drivers (Interface to the hardware)
++ SCO Module (SCO audio links)
++ L2CAP Module (Logical Link Control and Adaptation Protocol)
++ RFCOMM Module (RFCOMM Protocol)
++ BNEP Module (Bluetooth Network Encapsulation Protocol)
++ CMTP Module (CAPI Message Transport Protocol)
++ HIDP Module (Human Interface Device Protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build BlueZ Core
+- layer.
++ Say Y here to compile Bluetooth support into the kernel or say M to
++ compile it as module (bluez.o).
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+ utilities like hciconfig and hcid. These utilities and updates to
+ Bluetooth kernel modules are provided in the BlueZ package.
+- For more information, see <http://bluez.sourceforge.net/>.
+-
+- If you want to compile BlueZ Core as module (bluez.o) say M here.
++ For more information, see <http://www.bluez.org/>.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -21697,7 +21706,7 @@
+
+ SCO links support
+ CONFIG_BLUEZ_SCO
+- SCO link provides voice transport over Bluetooth. SCO support is
++ SCO link provides voice transport over Bluetooth. SCO support is
+ required for voice applications like Headset and Audio.
+
+ Say Y here to compile SCO support into the kernel or say M to
+@@ -21705,7 +21714,7 @@
+
+ RFCOMM protocol support
+ CONFIG_BLUEZ_RFCOMM
+- RFCOMM provides connection oriented stream transport. RFCOMM
++ RFCOMM provides connection oriented stream transport. RFCOMM
+ support is required for Dialup Networking, OBEX and other Bluetooth
+ applications.
+
+@@ -21719,12 +21728,8 @@
+ BNEP protocol support
+ CONFIG_BLUEZ_BNEP
+ BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
+- emulation layer on top of Bluetooth. BNEP is required for Bluetooth
+- PAN (Personal Area Network).
+-
+- To use BNEP, you will need user-space utilities provided in the
+- BlueZ-PAN package.
+- For more information, see <http://bluez.sourceforge.net>.
++ emulation layer on top of Bluetooth. BNEP is required for
++ Bluetooth PAN (Personal Area Network).
+
+ Say Y here to compile BNEP support into the kernel or say M to
+ compile it as module (bnep.o).
+@@ -21737,6 +21742,24 @@
+ CONFIG_BLUEZ_BNEP_PROTO_FILTER
+ This option enables the protocol filter support for BNEP.
+
++CMTP protocol support
++CONFIG_BLUEZ_CMTP
++ CMTP (CAPI Message Transport Protocol) is a transport layer
++ for CAPI messages. CMTP is required for the Bluetooth Common
++ ISDN Access Profile.
++
++ Say Y here to compile CMTP support into the kernel or say M to
++ compile it as module (cmtp.o).
++
++HIDP protocol support
++CONFIG_BLUEZ_HIDP
++ HIDP (Human Interface Device Protocol) is a transport layer
++ for HID reports. HIDP is required for the Bluetooth Human
++ Interface Device Profile.
++
++ Say Y here to compile HIDP support into the kernel or say M to
++ compile it as module (hidp.o).
++
+ HCI UART driver
+ CONFIG_BLUEZ_HCIUART
+ Bluetooth HCI UART driver.
+@@ -21781,7 +21804,7 @@
+ kernel or say M to compile it as module (hci_usb.o).
+
+ HCI USB SCO (voice) support
+-CONFIG_BLUEZ_USB_SCO
++CONFIG_BLUEZ_HCIUSB_SCO
+ This option enables the SCO support in the HCI USB driver. You need this
+ to transmit voice data with your Bluetooth USB device. And your device
+ must also support sending SCO data over the HCI layer, because some of
+@@ -21789,14 +21812,6 @@
+
+ Say Y here to compile support for HCI SCO data.
+
+-HCI USB zero packet support
+-CONFIG_BLUEZ_USB_ZERO_PACKET
+- This option is provided only as a work around for buggy Bluetooth USB
+- devices. Do NOT enable it unless you know for sure that your device
+- requires zero packets.
+-
+- Most people should say N here.
+-
+ HCI VHCI Virtual HCI device driver
+ CONFIG_BLUEZ_HCIVHCI
+ Bluetooth Virtual HCI device driver.
+@@ -21805,6 +21820,16 @@
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci.o).
+
++HCI BFUSB device driver
++CONFIG_BLUEZ_HCIBFUSB
++ Bluetooth HCI BlueFRITZ! USB driver.
++ This driver provides support for Bluetooth USB devices with AVM
++ interface:
++ AVM BlueFRITZ! USB
++
++ Say Y here to compile support for HCI BFUSB devices into the
++ kernel or say M to compile it as module (bfusb.o).
++
+ HCI DTL1 (PC Card) device driver
+ CONFIG_BLUEZ_HCIDTL1
+ Bluetooth HCI DTL1 (PC Card) driver.
+@@ -21824,9 +21849,6 @@
+ 3Com Bluetooth Card (3CRWB6096)
+ HP Bluetooth Card
+
+- The HCI BT3C driver uses external firmware loader program provided in
+- the BlueFW package. For more information, see <http://bluez.sf.net>.
+-
+ Say Y here to compile support for HCI BT3C devices into the
+ kernel or say M to compile it as module (bt3c_cs.o).
+
+@@ -26815,6 +26837,12 @@
+
+ If unsure, say N.
+
++Hotplug firmware loading support (EXPERIMENTAL)
++CONFIG_FW_LOADER
++ This option is provided for the case where no in-kernel-tree modules require
++ hotplug firmware loading support, but a module built outside the kernel tree
++ does.
++
+ NatSemi SCx200 support
+ CONFIG_SCx200
+ This provides basic support for the National Semiconductor SCx200
--- /dev/null
+++ linux-2.4.21/Documentation/DocBook/librs.tmpl
@@ -0,0 +1,287 @@
+ </para>
+ </chapter>
+</book>
+--- linux-2.4.21/Documentation/devices.txt~bluetooth
++++ linux-2.4.21/Documentation/devices.txt
+@@ -419,6 +419,7 @@
+ 220 = /dev/mptctl Message passing technology (MPT) control
+ 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver
+ 222 = /dev/mvista/hasi Montavista PICMG high availability
++ 223 = /dev/input/uinput User level driver support for input
+ 240-255 Reserved for local use
+
+ 11 char Raw keyboard device
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/README
+@@ -0,0 +1,58 @@
++
++ request_firmware() hotplug interface:
++ ------------------------------------
++ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
++
++ Why:
++ ---
++
++ Today, the most extended way to use firmware in the Linux kernel is linking
++ it statically in a header file. Which has political and technical issues:
++
++ 1) Some firmware is not legal to redistribute.
++ 2) The firmware occupies memory permanently, even though it often is just
++ used once.
++ 3) Some people, like the Debian crowd, don't consider some firmware free
++ enough and remove entire drivers (e.g.: keyspan).
++
++ about in-kernel persistence:
++ ---------------------------
++ Under some circumstances, as explained below, it would be interesting to keep
++ firmware images in non-swappable kernel memory or even in the kernel image
++ (probably within initramfs).
++
++ Note that this functionality has not been implemented.
++
++ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
++
++ - If the device that needs the firmware is needed to access the
++ filesystem. When upon some error the device has to be reset and the
++ firmware reloaded, it won't be possible to get it from userspace.
++ e.g.:
++ - A diskless client with a network card that needs firmware.
++ - The filesystem is stored in a disk behind an scsi device
++ that needs firmware.
++ - Replacing buggy DSDT/SSDT ACPI tables on boot.
++ Note: this would require the persistent objects to be included
++ within the kernel image, probably within initramfs.
++
++ And the same device can be needed to access the filesystem or not depending
++ on the setup, so I think that the choice on what firmware to make
++ persistent should be left to userspace.
++
++ - Why register_firmware()+__init can be useful:
++ - For boot devices needing firmware.
++ - To make the transition easier:
++ The firmware can be declared __init and register_firmware()
++ called on module_init. Then the firmware is warranted to be
++ there even if "firmware hotplug userspace" is not there yet or
++ it doesn't yet provide the needed firmware.
++ Once the firmware is widely available in userspace, it can be
++ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
++
++ In either case, if firmware hotplug support is there, it can move the
++ firmware out of kernel memory into the real filesystem for later
++ usage.
++
++ Note: If persistence is implemented on top of initramfs,
++ register_firmware() may not be appropriate.
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/firmware_sample_driver.c
+@@ -0,0 +1,121 @@
++/*
++ * firmware_sample_driver.c -
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Sample code on how to use request_firmware() from drivers.
++ *
++ * Note that register_firmware() is currently useless.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include "linux/firmware.h"
++
++#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++char __init inkernel_firmware[] = "let's say that this is firmware\n";
++#endif
++
++static char ghost_device[] = "ghost0";
++
++static void sample_firmware_load(char *firmware, int size)
++{
++ u8 buf[size+1];
++ memcpy(buf, firmware, size);
++ buf[size] = '\0';
++ printk("firmware_sample_driver: firmware: %s\n", buf);
++}
++
++static void sample_probe_default(void)
++{
++ /* uses the default method to get the firmware */
++ const struct firmware *fw_entry;
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware not available\n");
++ return;
++ }
++
++ sample_firmware_load(fw_entry->data, fw_entry->size);
++
++ release_firmware(fw_entry);
++
++ /* finish setting up the device */
++}
++static void sample_probe_specific(void)
++{
++ /* Uses some specific hotplug support to get the firmware from
++ * userspace directly into the hardware, or via some sysfs file */
++
++ /* NOTE: This currently doesn't work */
++
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware load failed\n");
++ return;
++ }
++
++ /* request_firmware blocks until userspace finished, so at
++ * this point the firmware should be already in the device */
++
++ /* finish setting up the device */
++}
++static void sample_probe_async_cont(const struct firmware *fw, void *context)
++{
++ if(!fw){
++ printk(KERN_ERR
++ "firmware_sample_driver: firmware load failed\n");
++ return;
++ }
++
++ printk("firmware_sample_driver: device pointer \"%s\"\n",
++ (char *)context);
++ sample_firmware_load(fw->data, fw->size);
++}
++static void sample_probe_async(void)
++{
++ /* Let's say that I can't sleep */
++ int error;
++ error = request_firmware_nowait (THIS_MODULE,
++ "sample_driver_fw", ghost_device,
++ "my device pointer",
++ sample_probe_async_cont);
++ if(error){
++ printk(KERN_ERR
++ "firmware_sample_driver:"
++ " request_firmware_nowait failed\n");
++ }
++}
++
++static int sample_init(void)
++{
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++ register_firmware("sample_driver_fw", inkernel_firmware,
++ sizeof(inkernel_firmware));
++#endif
++ /* since there is no real hardware insertion I just call the
++ * sample probe functions here */
++ sample_probe_specific();
++ sample_probe_default();
++ sample_probe_async();
++ return 0;
++}
++static void __exit sample_exit(void)
++{
++}
++
++module_init (sample_init);
++module_exit (sample_exit);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/hotplug-script
+@@ -0,0 +1,16 @@
++#!/bin/sh
++
++# Simple hotplug script sample:
++#
++# Both $DEVPATH and $FIRMWARE are already provided in the environment.
++
++HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
++
++echo 1 > /sysfs/$DEVPATH/loading
++cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
++echo 0 > /sysfs/$DEVPATH/loading
++
++# To cancel the load in case of error:
++#
++# echo -1 > /sysfs/$DEVPATH/loading
++#
+--- linux-2.4.21/MAINTAINERS~bluetooth
++++ linux-2.4.21/MAINTAINERS
+@@ -302,16 +302,88 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Marcel Holtmann
++M: marcel@holtmann.org
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
++L: bluez-devel@lists.sf.net
+ W: http://bluez.sf.net
++W: http://www.bluez.org
++W: http://www.holtmann.org/linux/bluetooth/
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (PC Card Drivers)
++BLUETOOTH RFCOMM LAYER
+ P: Marcel Holtmann
+ M: marcel@holtmann.org
+-W: http://www.holtmann.org/linux/bluetooth/
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HIDP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI BCM203X DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
+ S: Maintained
+
+ BONDING DRIVER
--- linux-2.4.21/Makefile~linux-mkdep
+++ linux-2.4.21/Makefile
@@ -14,10 +14,11 @@
--- /dev/null
+++ linux-2.4.21/arch/arm/def-configs/ramses
-@@ -0,0 +1,1174 @@
+@@ -0,0 +1,1177 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_UINPUT=m
+# CONFIG_INPUT_MX1TS is not set
+
+#
+CONFIG_BLUEZ_L2CAP=m
+CONFIG_BLUEZ_SCO=m
+CONFIG_BLUEZ_RFCOMM=m
-+# CONFIG_BLUEZ_RFCOMM_TTY is not set
++CONFIG_BLUEZ_RFCOMM_TTY=y
+CONFIG_BLUEZ_BNEP=m
+CONFIG_BLUEZ_BNEP_MC_FILTER=y
-+# CONFIG_BLUEZ_BNEP_PROTO_FILTER is not set
++CONFIG_BLUEZ_BNEP_PROTO_FILTER=y
++CONFIG_BLUEZ_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BLUEZ_HCIUSB=m
-+CONFIG_BLUEZ_USB_SCO=y
-+CONFIG_BLUEZ_USB_ZERO_PACKET=y
++CONFIG_BLUEZ_HCIUSB_SCO=y
+CONFIG_BLUEZ_HCIUART=m
+CONFIG_BLUEZ_HCIUART_H4=y
+CONFIG_BLUEZ_HCIUART_BCSP=y
-+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC=y
++# CONFIG_BLUEZ_HCIUART_BCSP_TXCRC is not set
++CONFIG_BLUEZ_HCIBFUSB=m
+CONFIG_BLUEZ_HCIDTL1=m
+CONFIG_BLUEZ_HCIBT3C=m
+CONFIG_BLUEZ_HCIBLUECARD=m
+CONFIG_BLUEZ_HCIBTUART=m
-+# CONFIG_BLUEZ_HCIVHCI is not set
++CONFIG_BLUEZ_HCIVHCI=m
+
+#
+# Kernel hacking
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_REED_SOLOMON is not set
++CONFIG_FW_LOADER=m
--- linux-2.4.21/arch/arm/mach-pxa/Makefile~pm
+++ linux-2.4.21/arch/arm/mach-pxa/Makefile
@@ -14,8 +14,11 @@
+#endif
+#define USE_UCB
+//#define PFI_LED
-+//#define PFI_TURNOFF
++#define PFI_TURNOFF
+
+#include <asm/types.h>
+#include <asm/setup.h>
pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" );
if ( pstr ) {
pxa_usb_set_string_descriptor( 1, pstr );
---- linux-2.4.21/drivers/char/Config.in~i2c-ds1337
-+++ linux-2.4.21/drivers/char/Config.in
-@@ -164,6 +164,7 @@
-
- if [ "$CONFIG_I2C" != "n" ]; then
- dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
-+ dep_tristate ' DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C
+--- linux-2.4.21/drivers/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Config.in
+@@ -7,8 +7,7 @@
+
+ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
+ if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
+- bool ' SCO (voice) support' CONFIG_BLUEZ_USB_SCO
+- bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET
++ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO
fi
- source drivers/l3/Config.in
---- linux-2.4.21/drivers/char/Makefile~i2c-ds1337
-+++ linux-2.4.21/drivers/char/Makefile
-@@ -21,10 +21,11 @@
- # All of the (potential) objects that export symbols.
- # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
-
--export-objs := busmouse.o console.o keyboard.o sysrq.o \
-+export-objs := vt.o busmouse.o console.o keyboard.o sysrq.o \
- misc.o pty.o random.o selection.o serial.o \
- sonypi.o tty_io.o tty_ioctl.o generic_serial.o \
-- au1000_gpio.o hp_psaux.o nvram.o scx200.o
-+ au1000_gpio.o hp_psaux.o nvram.o scx200.o \
-+ input_keyb.o
-
- mod-subdirs := joystick ftape drm drm-4.0 pcmcia
-
-@@ -129,6 +130,11 @@
- ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
- KEYBD += cerf_keyb.o
- endif
-+ ifeq ($(CONFIG_ARCH_RAMSES),y)
-+ KEYMAP = german.o
-+ KEYBD += input_keyb.o
-+ obj-m += sysctl.o
-+ endif
- ifeq ($(CONFIG_ARCH_FORTUNET),y)
- KEYMAP := defkeymap.o
- endif
-@@ -337,6 +343,7 @@
-
- # I2C char devices
- obj-$(CONFIG_I2C_DS1307) += ds1307.o
-+obj-$(CONFIG_I2C_DS1337) += ds1337.o
-
- subdir-$(CONFIG_MWAVE) += mwave
- ifeq ($(CONFIG_MWAVE),y)
-@@ -373,3 +380,6 @@
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+@@ -18,6 +17,8 @@
+ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
+ fi
- qtronixmap.c: qtronixmap.map
- set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
+
-+german.c: german.map
-+ set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
---- linux-2.4.21/drivers/char/console.c~keyb-module
-+++ linux-2.4.21/drivers/char/console.c
-@@ -150,7 +150,7 @@
- static int con_open(struct tty_struct *, struct file *);
- static void vc_init(unsigned int console, unsigned int rows,
- unsigned int cols, int do_clear);
--static void blank_screen(unsigned long dummy);
-+//static void blank_screen(unsigned long dummy);
- static void gotoxy(int currcons, int new_x, int new_y);
- static void save_cur(int currcons);
- static void reset_terminal(int currcons, int do_clear);
-@@ -158,7 +158,7 @@
- static void set_vesa_blanking(unsigned long arg);
- static void set_cursor(int currcons);
- static void hide_cursor(int currcons);
--static void unblank_screen_t(unsigned long dummy);
-+//static void unblank_screen_t(unsigned long dummy);
- static void console_callback(void *ignored);
-
- static int printable; /* Is console ready for printing? */
-@@ -167,7 +167,7 @@
- int console_blanked;
-
- static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
--static int blankinterval = 10*60*HZ;
-+//static int blankinterval = 10*60*HZ;
- static int vesa_off_interval;
-
- static struct tq_struct console_callback_tq = {
-@@ -209,9 +209,9 @@
- * Hook so that the power management routines can (un)blank
- * the console on our behalf.
- */
--int (*console_blank_hook)(int);
-+//int (*console_blank_hook)(int);
-
--static struct timer_list console_timer;
-+//static struct timer_list console_timer;
-
- /*
- * Low-Level Functions
-@@ -543,7 +543,7 @@
-
- static void set_cursor(int currcons)
- {
-- if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
-+ if (!IS_FG || vcmode == KD_GRAPHICS)
- return;
- if (deccm) {
- if (currcons == sel_cons)
-@@ -1287,7 +1287,7 @@
- update_attr(currcons);
- break;
- case 9: /* set blanking interval */
-- blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
-+ //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
- poke_blanked_console();
- break;
- case 10: /* set bell frequency in Hz */
-@@ -2575,11 +2575,11 @@
- if (tty_register_driver(&console_driver))
- panic("Couldn't register console driver\n");
-
-- init_timer(&console_timer);
-- console_timer.function = blank_screen;
-- if (blankinterval) {
-- mod_timer(&console_timer, jiffies + blankinterval);
-- }
-+ //init_timer(&console_timer);
-+ //console_timer.function = blank_screen;
-+ //if (blankinterval) {
-+ // mod_timer(&console_timer, jiffies + blankinterval);
-+ //}
-
- /*
- * kmalloc is not running yet - we use the bootmem allocator.
-@@ -2744,11 +2744,12 @@
- */
- static void vesa_powerdown_screen(unsigned long dummy)
- {
-- console_timer.function = unblank_screen_t;
-+ //console_timer.function = unblank_screen_t;
-
- vesa_powerdown();
- }
-
-+#if 0
- static void timer_do_blank_screen(int entering_gfx, int from_timer_handler)
- {
- int currcons = fg_console;
-@@ -2797,12 +2798,14 @@
- if (vesa_blank_mode)
- sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
- }
-+#endif
-
- void do_blank_screen(int entering_gfx)
- {
-- timer_do_blank_screen(entering_gfx, 0);
-+ //timer_do_blank_screen(entering_gfx, 0);
- }
-
-+#if 0
- /*
- * This is a timer handler
- */
-@@ -2810,12 +2813,14 @@
- {
- unblank_screen();
- }
-+#endif
-
- /*
- * Called by timer as well as from vt_console_driver
- */
- void unblank_screen(void)
- {
-+#if 0
- int currcons;
-
- if (!console_blanked)
-@@ -2842,6 +2847,7 @@
- /* Low-level driver cannot restore -> do it ourselves */
- update_screen(fg_console);
- set_cursor(fg_console);
-+#endif
- }
+ dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
- /*
-@@ -2849,11 +2855,12 @@
- */
- static void blank_screen(unsigned long dummy)
- {
-- timer_do_blank_screen(0, 1);
-+ //timer_do_blank_screen(0, 1);
- }
-
- void poke_blanked_console(void)
- {
-+#if 0
- del_timer(&console_timer);
- if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- return;
-@@ -2863,6 +2870,7 @@
- } else if (blankinterval) {
- mod_timer(&console_timer, jiffies + blankinterval);
- }
-+#endif
- }
+ dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
+--- linux-2.4.21/drivers/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Makefile
+@@ -14,6 +14,8 @@
+ uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o
+ uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o
- /*
-@@ -3088,7 +3096,7 @@
- unblank_screen();
- break;
- case PM_SUSPEND:
-- do_blank_screen(0);
-+ //do_blank_screen(0);
- break;
- }
- return 0;
-@@ -3106,7 +3114,8 @@
- EXPORT_SYMBOL(video_scan_lines);
- EXPORT_SYMBOL(vc_resize);
- EXPORT_SYMBOL(fg_console);
--EXPORT_SYMBOL(console_blank_hook);
-+//EXPORT_SYMBOL(console_blank_hook);
-+EXPORT_SYMBOL(console_driver);
- #ifdef CONFIG_VT
- EXPORT_SYMBOL(vt_cons);
- #endif
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o
++
+ obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
+ obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
+ obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
--- /dev/null
-+++ linux-2.4.21/drivers/char/ds1337.c
-@@ -0,0 +1,545 @@
++++ linux-2.4.21/drivers/bluetooth/Makefile.lib
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/bfusb.c
+@@ -0,0 +1,782 @@
+/*
-+ * ds1337.c
+ *
-+ * Device driver for Dallas Semiconductor's Real Time Controller DS1337.
++ * AVM BlueFRITZ! USB driver
+ *
-+ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ *
-+ * 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.
+ *
-+ * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
-+#include <linux/version.h>
+
+#include <linux/kernel.h>
-+#include <linux/poll.h>
-+#include <linux/i2c.h>
-+#include <linux/slab.h>
+#include <linux/init.h>
-+#include <linux/rtc.h>
-+#include <linux/string.h>
-+#include <linux/miscdevice.h>
-+#include <linux/proc_fs.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
+
-+#include "ds1337.h"
++#include <linux/firmware.h>
++#include <linux/usb.h>
+
-+//#define DEBUG 1
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
+
-+#if DEBUG
-+static unsigned int rtc_debug = DEBUG;
-+#else
-+#define rtc_debug 0 /* gcc will remove all the debug code for us */
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
+#endif
+
-+static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR;
-+struct i2c_driver ds1337_driver;
-+struct i2c_client *ds1337_i2c_client = 0;
-+static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED;
++#define VERSION "1.1"
+
-+static unsigned short ignore[] = { I2C_CLIENT_END };
-+static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++static struct usb_device_id bfusb_table[] = {
++ /* AVM BlueFRITZ! USB */
++ { USB_DEVICE(0x057c, 0x2200) },
+
-+static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-+static int ds1337_rtc_noop(struct inode *inode, struct file *file);
++ { } /* Terminating entry */
++};
+
-+static int ds1337_probe(struct i2c_adapter *adap);
-+static int ds1337_detach(struct i2c_client *client);
-+static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg);
++MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+
-+static struct i2c_client_address_data addr_data = {
-+ .normal_i2c = normal_addr,
-+ .normal_i2c_range = ignore,
-+ .probe = ignore,
-+ .probe_range = ignore,
-+ .ignore = ignore,
-+ .ignore_range = ignore,
-+ .force = ignore,
-+};
++#define BFUSB_MAX_BLOCK_SIZE 256
+
-+static struct file_operations rtc_fops = {
-+ .owner = THIS_MODULE,
-+ .ioctl = ds1337_rtc_ioctl,
-+ .open = ds1337_rtc_noop,
-+ .release = ds1337_rtc_noop,
-+};
++#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
+
-+static struct miscdevice ds1337_rtc_miscdev = {
-+ RTC_MINOR,
-+ "rtc",
-+ &rtc_fops
-+};
++#define BFUSB_TX_PROCESS 1
++#define BFUSB_TX_WAKEUP 2
+
++#define BFUSB_MAX_BULK_TX 1
++#define BFUSB_MAX_BULK_RX 1
+
-+struct i2c_driver ds1337_driver = {
-+ .name = "DS1337",
-+ .id = I2C_DRIVERID_DS1337,
-+ .flags = I2C_DF_NOTIFY,
-+ .attach_adapter = ds1337_probe,
-+ .detach_client = ds1337_detach,
-+ .command = ds1337_command
++struct bfusb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ unsigned int bulk_in_ep;
++ unsigned int bulk_out_ep;
++ unsigned int bulk_pkt_size;
++
++ rwlock_t lock;
++
++ struct sk_buff_head transmit_q;
++
++ struct sk_buff *reassembly;
++
++ atomic_t pending_tx;
++ struct sk_buff_head pending_q;
++ struct sk_buff_head completed_q;
+};
+
-+#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */
++struct bfusb_scb {
++ struct urb *urb;
++};
+
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
+
-+static int ds1337_readram(char *buf, int len)
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
+{
-+ unsigned long flags;
-+ unsigned char ad[1] = { 0 };
-+ int ret;
-+ struct i2c_msg msgs[2] = {
-+ {ds1337_i2c_client->addr, 0, 1, ad},
-+ {ds1337_i2c_client->addr, I2C_M_RD, len, buf}
-+ };
++ struct sk_buff *skb;
++ struct urb *urb = NULL;
+
-+ spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+ ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2);
-+ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++ BT_DBG("bfusb %p", bfusb);
+
-+ return ret;
-+}
++ skb = skb_dequeue(&bfusb->completed_q);
++ if (skb) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ kfree_skb(skb);
++ }
+
++ return urb;
++}
+
-+static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val)
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
+{
-+ unsigned char buf[2];
-+ buf[0] = reg;
-+ buf[1] = val;
-+ i2c_master_send(c, (char *) buf, 2);
++ struct sk_buff *skb;
++ struct urb *urb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ while ((skb = skb_dequeue(&bfusb->pending_q))) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ usb_unlink_urb(urb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++ }
++
++ while ((urb = bfusb_get_completed(bfusb)))
++ usb_free_urb(urb);
+}
+
-+static int ds1337_attach(struct i2c_adapter *adap, int addr,
-+ unsigned short flags, int kind)
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
+{
-+ struct i2c_client *c;
-+ unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 };
-+ struct i2c_msg msgs[2] = {
-+ {addr, 0, 1, ad},
-+ {addr, I2C_M_RD, 1, buf}
-+ };
-+ int ret;
++ struct bfusb_scb *scb = (void *) skb->cb;
++ struct urb *urb = bfusb_get_completed(bfusb);
++ int err, pipe;
+
-+ if (rtc_debug>1)
-+ printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind);
++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
+
-+ c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL);
-+ if (!c)
-+ return -ENOMEM;
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
+
-+ strcpy(c->name, "DS1337");
-+ c->id = ds1337_driver.id;
-+ c->flags = 0;
-+ c->addr = addr;
-+ c->adapter = adap;
-+ c->driver = &ds1337_driver;
-+ c->data = NULL;
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
-+ ret = i2c_transfer(c->adapter, msgs, 2);
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++ bfusb_tx_complete, skb);
+
-+ if (ret == 2) {
-+ DAT(c) = buf[0];
-+ } else
-+ printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret);
++ urb->transfer_flags = USB_QUEUE_BULK;
+
-+ ds1337_i2c_client = c;
++ scb->urb = urb;
+
-+ ds1337_readram(buf, DS1337_MEM_SIZE);
++ skb_queue_tail(&bfusb->pending_q, skb);
+
-+ // set 24 hour mode
-+ ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24);
-+ // INTCN sets INTB to alarm2 (disables SQW)
-+ ds1337_setreg(c, 0x5, buf[5] & 0x7f); // clear century
-+ ds1337_setreg(c, 0x7, 0x00); // clear Alarm 1 seconds
-+ ds1337_setreg(c, 0x8, 0x00); // clear Alarm 1 minutes
-+ ds1337_setreg(c, 0x9, 0x40); // clear Alarm 1 hours, 24 hour on
-+ ds1337_setreg(c, 0xA, 0x00); // clear Alarm 1 date
-+ ds1337_setreg(c, 0xB, 0x00); // clear Alarm 2 minutes
-+ ds1337_setreg(c, 0xC, 0x40); // clear Alarm 2 hours, 24 hour on
-+ ds1337_setreg(c, 0xD, 0x00); // clear Alarm 2 date
-+ ds1337_setreg(c, 0xe, 4); // nEOSC enabled
-+ ds1337_setreg(c, 0xf, 0); // clear OSF, A2F, A1F
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk tx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ usb_free_urb(urb);
++ } else
++ atomic_inc(&bfusb->pending_tx);
+
-+ return i2c_attach_client(c);
++ return err;
+}
+
-+
-+static int ds1337_probe(struct i2c_adapter *adap)
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
+{
-+ if (rtc_debug>1)
-+ printk("%s()\n", __FUNCTION__);
++ struct sk_buff *skb;
+
-+ return i2c_probe(adap, &addr_data, ds1337_attach);
-+}
++ BT_DBG("bfusb %p", bfusb);
+
++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++ return;
++ }
+
-+static int ds1337_detach(struct i2c_client *client)
-+{
-+ if (rtc_debug>1)
-+ printk("%s()\n", __FUNCTION__);
++ do {
++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+
-+ i2c_detach_client(client);
++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
++ (skb = skb_dequeue(&bfusb->transmit_q))) {
++ if (bfusb_send_bulk(bfusb, skb) < 0) {
++ skb_queue_head(&bfusb->transmit_q, skb);
++ break;
++ }
++ }
+
-+ return 0;
-+}
++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
+
++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
+
-+static void ds1337_convert_to_time(struct rtc_time *dt, char *buf)
++static void bfusb_tx_complete(struct urb *urb)
+{
-+ if (rtc_debug>1)
-+ printk("%s()\n", __FUNCTION__);
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
+
-+ dt->tm_sec = BCD_TO_BIN(buf[0]);
-+ dt->tm_min = BCD_TO_BIN(buf[1]);
-+ dt->tm_hour = DS1337_HOURS_24(buf[2]);
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
-+ dt->tm_mday = BCD_TO_BIN(buf[4]);
-+ /* dt->tm_mon is zero-based */
-+ dt->tm_mon = BCD_TO_BIN(buf[5]) - 1;
-+ /* year is 1900 + dt->tm_year */
-+ dt->tm_year = BCD_TO_BIN(buf[6]) + 100;
++ atomic_dec(&bfusb->pending_tx);
+
-+ if (rtc_debug > 2) {
-+ printk("ds1337_get_datetime: year = %d\n", dt->tm_year);
-+ printk("ds1337_get_datetime: mon = %d\n", dt->tm_mon);
-+ printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday);
-+ printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour);
-+ printk("ds1337_get_datetime: min = %d\n", dt->tm_min);
-+ printk("ds1337_get_datetime: sec = %d\n", dt->tm_sec);
-+ }
-+}
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
+
++ if (!urb->status)
++ bfusb->hdev.stat.byte_tx += skb->len;
++ else
++ bfusb->hdev.stat.err_tx++;
+
-+static int ds1337_get_datetime(struct i2c_client *client,
-+ struct rtc_time *dt)
-+{
-+ unsigned char buf[7], addr[1] = { 0 };
-+ struct i2c_msg msgs[2] = {
-+ {client->addr, 0, 1, addr},
-+ {client->addr, I2C_M_RD, 7, buf}
-+ };
-+ int ret = -EIO;
++ read_lock(&bfusb->lock);
+
-+ if (rtc_debug)
-+ printk("%s()\n", __FUNCTION__);
++ skb_unlink(skb);
++ skb_queue_tail(&bfusb->completed_q, skb);
+
-+ memset(buf, 0, sizeof(buf));
++ bfusb_tx_wakeup(bfusb);
+
-+ ret = i2c_transfer(client->adapter, msgs, 2);
++ read_unlock(&bfusb->lock);
++}
+
-+ if (ret == 2) {
-+ ds1337_convert_to_time(dt, buf);
-+ ret = 0;
-+ } else
-+ printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret);
+
-+ return ret;
-+}
++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
++{
++ struct bfusb_scb *scb;
++ struct sk_buff *skb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
++ BT_DBG("bfusb %p urb %p", bfusb, urb);
+
-+static int ds1337_set_datetime(struct i2c_client *client,
-+ struct rtc_time *dt, int datetoo)
-+{
-+ unsigned char buf[8];
-+ int ret, len = 4;
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
+
-+ if (rtc_debug)
-+ printk("%s()\n", __FUNCTION__);
++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
+
-+ if (rtc_debug > 2) {
-+ printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year);
-+ printk("ds1337_set_datetime: tm_mon = %d\n", dt->tm_mon);
-+ printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday);
-+ printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour);
-+ printk("ds1337_set_datetime: tm_min = %d\n", dt->tm_min);
-+ printk("ds1337_set_datetime: tm_sec = %d\n", dt->tm_sec);
-+ }
++ skb->dev = (void *) bfusb;
+
-+ buf[0] = 0; /* register address on DS1337 */
-+ buf[1] = (BIN_TO_BCD(dt->tm_sec));
-+ buf[2] = (BIN_TO_BCD(dt->tm_min));
-+ buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24;
++ scb = (struct bfusb_scb *) skb->cb;
++ scb->urb = urb;
+
-+ if (datetoo) {
-+ len = 8;
-+ /* we skip buf[4] as we don't use day-of-week. */
-+ buf[5] = (BIN_TO_BCD(dt->tm_mday));
-+ buf[6] = (BIN_TO_BCD(dt->tm_mon + 1));
-+ /* The year only ranges from 0-99, we are being passed an offset from 1900,
-+ * and the chip calulates leap years based on 2000, thus we adjust by 100.
-+ */
-+ buf[7] = (BIN_TO_BCD(dt->tm_year - 100));
-+ }
-+ ret = i2c_master_send(client, (char *) buf, len);
-+ if (ret == len)
-+ ret = 0;
-+ else
-+ printk("ds1337_set_datetime(), i2c_master_send() returned %d\n",
-+ ret);
++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
+
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++ bfusb_rx_complete, skb);
+
-+ return ret;
-+}
++ urb->transfer_flags = USB_QUEUE_BULK;
+
++ skb_queue_tail(&bfusb->pending_q, skb);
+
-+#if 0
-+static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ kfree_skb(skb);
++ usb_free_urb(urb);
++ }
++
++ return err;
++}
++
++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
+{
-+ *ctrl = DAT(client);
++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
+
-+ if (rtc_debug)
-+ printk("%s():%d\n", __FUNCTION__, *ctrl);
++ if (hdr & 0x10) {
++ BT_ERR("%s error in block", bfusb->hdev.name);
++ if (bfusb->reassembly)
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ return -EIO;
++ }
+
-+ return 0;
-+}
++ if (hdr & 0x04) {
++ struct sk_buff *skb;
++ unsigned char pkt_type;
++ int pkt_len = 0;
+
++ if (bfusb->reassembly) {
++ BT_ERR("%s unexpected start block", bfusb->hdev.name);
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
+
-+static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo)
++ if (len < 1) {
++ BT_ERR("%s no packet type found", bfusb->hdev.name);
++ return -EPROTO;
++ }
++
++ pkt_type = *data++; len--;
++
++ switch (pkt_type) {
++ case HCI_EVENT_PKT:
++ if (len >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *hdr = (hci_event_hdr *) data;
++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
++ } else {
++ BT_ERR("%s event block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (len >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *hdr = (hci_acl_hdr *) data;
++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
++ } else {
++ BT_ERR("%s data block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (len >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *hdr = (hci_sco_hdr *) data;
++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
++ } else {
++ BT_ERR("%s audio block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++ }
++
++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) &bfusb->hdev;
++ skb->pkt_type = pkt_type;
++
++ bfusb->reassembly = skb;
++ } else {
++ if (!bfusb->reassembly) {
++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
++ return -EIO;
++ }
++ }
++
++ if (len > 0)
++ memcpy(skb_put(bfusb->reassembly, len), data, len);
++
++ if (hdr & 0x08) {
++ hci_recv_frame(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ return 0;
++}
++
++static void bfusb_rx_complete(struct urb *urb)
+{
-+ unsigned char buf[2];
-+ int ret;
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++ unsigned char *buf = urb->transfer_buffer;
++ int count = urb->actual_length;
++ int err, hdr, len;
+
-+ if (rtc_debug)
-+ printk("%s(%d)\n", __FUNCTION__, *cinfo);
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
-+ buf[0] = 7; /* control register address on DS1337 */
-+ buf[1] = *cinfo;
-+ /* save the control reg info in the client data field so that get_ctrl
-+ * function doesn't have to do an I2C transfer to get it.
-+ */
-+ DAT(client) = buf[1];
++ read_lock(&bfusb->lock);
+
-+ ret = i2c_master_send(client, (char *) buf, 2);
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ goto unlock;
+
-+ return ret;
++ if (urb->status || !count)
++ goto resubmit;
++
++ bfusb->hdev.stat.byte_rx += count;
++
++ skb_put(skb, count);
++
++ while (count) {
++ hdr = buf[0] | (buf[1] << 8);
++
++ if (hdr & 0x4000) {
++ len = 0;
++ count -= 2;
++ buf += 2;
++ } else {
++ len = (buf[2] == 0) ? 256 : buf[2];
++ count -= 3;
++ buf += 3;
++ }
++
++ if (count < len) {
++ BT_ERR("%s block extends over URB buffer ranges",
++ bfusb->hdev.name);
++ }
++
++ if ((hdr & 0xe1) == 0xc1)
++ bfusb_recv_block(bfusb, hdr, buf, len);
++
++ count -= len;
++ buf += len;
++ }
++
++ skb_unlink(skb);
++ kfree_skb(skb);
++
++ bfusb_rx_submit(bfusb, urb);
++
++ read_unlock(&bfusb->lock);
++
++ return;
++
++resubmit:
++ urb->dev = bfusb->udev;
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk resubmit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ }
++
++unlock:
++ read_unlock(&bfusb->lock);
+}
-+#endif
+
+
-+static int ds1337_command(struct i2c_client *client, unsigned int cmd,
-+ void *arg)
++static int bfusb_open(struct hci_dev *hdev)
+{
-+ if (rtc_debug)
-+ printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd);
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++ int i, err;
+
-+ switch (cmd) {
-+ case DS1337_GETDATETIME:
-+ return ds1337_get_datetime(client, arg);
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
-+ case DS1337_SETTIME:
-+ return ds1337_set_datetime(client, arg, 0);
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
-+ case DS1337_SETDATETIME:
-+ return ds1337_set_datetime(client, arg, 1);
++ MOD_INC_USE_COUNT;
+
-+ default:
-+ return -EINVAL;
-+ }
-+}
++ write_lock_irqsave(&bfusb->lock, flags);
+
++ err = bfusb_rx_submit(bfusb, NULL);
++ if (!err) {
++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
++ bfusb_rx_submit(bfusb, NULL);
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
+
-+static int ds1337_rtc_noop(struct inode *inode, struct file *file)
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
+{
-+ return 0;
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ skb_queue_purge(&bfusb->transmit_q);
++
++ return 0;
+}
+
++static int bfusb_close(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
+
-+static int ds1337_rtc_ioctl(struct inode *inode, struct file *file,
-+ unsigned int cmd, unsigned long arg)
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ bfusb_unlink_urbs(bfusb);
++ bfusb_flush(hdev);
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++static int bfusb_send_frame(struct sk_buff *skb)
+{
-+ unsigned long flags;
-+ struct rtc_time wtime;
-+ int status = 0;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct bfusb *bfusb;
++ struct sk_buff *nskb;
++ unsigned char buf[3];
++ int sent = 0, size, count;
+
-+ if (rtc_debug)
-+ printk("%s()\n", __FUNCTION__);
++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
+
-+ switch (cmd) {
-+ default:
-+ case RTC_UIE_ON: // mask ints from RTC updates
-+ case RTC_UIE_OFF:
-+ case RTC_PIE_ON: // allow periodic interrupts
-+ case RTC_PIE_OFF:
-+ case RTC_AIE_ON: // mask alarm int enable bit
-+ case RTC_AIE_OFF:
-+ case RTC_ALM_SET:
-+ /*
-+ * This expects a struct rtc_time. Writing 0xff means
-+ * "don't care" or "match all". Only the tm_hour,
-+ * tm_min and tm_sec are used.
-+ */
-+ case RTC_ALM_READ:
-+ // get_rtc_alm_time(&wtime);
-+ case RTC_IRQP_READ: // Read the periodic IRQ rate
-+ case RTC_IRQP_SET: // Set periodic IRQ rate
-+ case RTC_EPOCH_READ:
-+ // return put_user (epoch, (unsigned long *)arg);
-+ case RTC_EPOCH_SET:
-+ case RTC_WKALM_SET:
-+ case RTC_WKALM_RD:
-+ status = -EINVAL;
-+ break;
++ if (!hdev) {
++ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++ return -ENODEV;
++ }
+
-+ case RTC_RD_TIME:
-+ spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+ ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime);
-+ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
+
-+ if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time)))
-+ status = -EFAULT;
-+ break;
++ bfusb = (struct bfusb *) hdev->driver_data;
+
-+ case RTC_SET_TIME:
-+ if (!capable(CAP_SYS_TIME)) {
-+ status = -EACCES;
-+ break;
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++
++ count = skb->len;
++
++ /* Max HCI frame size seems to be 1511 + 1 */
++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new packet");
++ return -ENOMEM;
+ }
+
-+ if (copy_from_user
-+ (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) {
-+ status = -EFAULT;
-+ break;
++ nskb->dev = (void *) bfusb;
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
++
++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
++ buf[1] = 0x00;
++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
++
++ memcpy(skb_put(nskb, 3), buf, 3);
++ memcpy(skb_put(nskb, size), skb->data + sent, size);
++
++ sent += size;
++ count -= size;
+ }
+
-+ spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+ ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime);
-+ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
-+ break;
-+ }
++ /* Don't send frame with multiple size of bulk max packet */
++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
++ buf[0] = 0xdd;
++ buf[1] = 0x00;
++ memcpy(skb_put(nskb, 2), buf, 2);
++ }
+
-+ return status;
++ read_lock(&bfusb->lock);
++
++ skb_queue_tail(&bfusb->transmit_q, nskb);
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++
++ kfree_skb(skb);
++
++ return 0;
+}
+
++static void bfusb_destruct(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
-+static char *ds1337_mon2str(unsigned int mon)
++ kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
-+ char *mon2str[12] = {
-+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-+ };
-+ if (mon > 11)
-+ return "error";
-+ else
-+ return mon2str[mon];
++ return -ENOIOCTLCMD;
+}
+
+
-+static int ds1337_rtc_proc_output(char *buf)
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
+{
-+#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++ unsigned char *buf;
++ int err, pipe, len, size, sent = 0;
+
-+ unsigned char ram[DS1337_MEM_SIZE];
-+ int ret;
++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
+
-+ char *p = buf;
++ BT_INFO("BlueFRITZ! USB loading firmware");
+
-+ ret = ds1337_readram(ram, DS1337_MEM_SIZE);
-+ if (ret > 0) {
-+#ifdef DEBUG
-+ int i;
-+ char text[9];
-+#endif
-+ struct rtc_time dt;
++ if (usb_set_configuration(bfusb->udev, 1) < 0) {
++ BT_ERR("Can't change to loading configuration");
++ return -EBUSY;
++ }
+
-+ p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n");
++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++ if (!buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
+
-+ ds1337_convert_to_time(&dt, ram);
-+ p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n",
-+ dt.tm_mday, ds1337_mon2str(dt.tm_mon),
-+ dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec);
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
-+#ifdef DEBUG
-+ p += sprintf(p, "RAM dump:\n");
-+ text[8] = '\0';
-+ for (i = 0; i < DS1337_MEM_SIZE; i++) {
-+ if ((i % 8) == 0)
-+ p += sprintf(p, "%02X: ", i);
-+ p += sprintf(p, "%02X ", ram[i]);
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
-+ if ((ram[i] < 32) || (ram[i] > 126))
-+ ram[i] = '.';
-+ text[i % 8] = ram[i];
-+ if ((i % 8) == 7)
-+ p += sprintf(p, "%s\n", text);
++ memcpy(buf, firmware + sent, size);
++
++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
++ &len, BFUSB_BLOCK_TIMEOUT);
++
++ if (err || (len != size)) {
++ BT_ERR("Error in firmware loading");
++ goto error;
++ }
++
++ sent += size;
++ count -= size;
+ }
-+ p += sprintf(p, "\n");
-+#endif
-+ } else {
-+ p += sprintf(p, "Failed to read RTC memory!\n");
-+ }
+
-+ return p - buf;
-+}
++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
++ BT_ERR("Error in null packet request");
++ goto error;
++ }
+
++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
++ BT_ERR("Can't change to running configuration");
++ goto error;
++ }
+
-+static int ds1337_rtc_read_proc(char *page, char **start, off_t off,
-+ int count, int *eof, void *data)
-+{
-+ int len = ds1337_rtc_proc_output(page);
++ BT_INFO("BlueFRITZ! USB device ready");
+
-+ if (len <= off + count)
-+ *eof = 1;
-+ *start = page + off;
-+ len -= off;
-+ if (len > count)
-+ len = count;
-+ if (len < 0)
-+ len = 0;
-+ return len;
-+}
++ kfree(buf);
++ return 0;
+
++error:
++ kfree(buf);
+
-+static __init int ds1337_init(void)
++ pipe = usb_sndctrlpipe(bfusb->udev, 0);
++
++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
++
++ return err;
++}
++
++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+{
-+ int retval = 0;
++ const struct firmware *firmware;
++ char device[16];
++ struct usb_interface *iface;
++ struct usb_interface_descriptor *iface_desc;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct hci_dev *hdev;
++ struct bfusb *bfusb;
+
-+ if (rtc_debug>1)
-+ printk("%s()\n", __FUNCTION__);
++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
+
-+ if (slave_address != 0xffff) {
-+ normal_addr[0] = slave_address;
-+ }
++ /* Check number of endpoints */
++ iface = &udev->actconfig->interface[0];
++ iface_desc = &iface->altsetting[0];
+
-+ if (normal_addr[0] == 0xffff) {
-+ printk(KERN_ERR
-+ "I2C: Invalid slave address for DS1337 RTC (%#x)\n",
-+ normal_addr[0]);
-+ return -EINVAL;
-+ }
++ if (iface_desc->bNumEndpoints < 2)
++ return NULL;
+
-+ retval = i2c_add_driver(&ds1337_driver);
++ bulk_out_ep = &iface_desc->endpoint[0];
++ bulk_in_ep = &iface_desc->endpoint[1];
+
-+ if (retval == 0) {
-+ misc_register(&ds1337_rtc_miscdev);
-+ create_proc_read_entry(DS1337_PROC_NAME, 0, 0,
-+ ds1337_rtc_read_proc, NULL);
-+ printk("I2C: DS1337 RTC driver loaded\n");
-+ }
-+ return retval;
-+}
++ if (!bulk_out_ep || !bulk_in_ep) {
++ BT_ERR("Bulk endpoints not found");
++ goto done;
++ }
+
++ /* Initialize control structure and load firmware */
++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
++ BT_ERR("Can't allocate memory for control structure");
++ goto done;
++ }
+
-+static __exit void ds1337_exit(void)
++ memset(bfusb, 0, sizeof(struct bfusb));
++
++ bfusb->udev = udev;
++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress;
++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress;
++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
++
++ bfusb->lock = RW_LOCK_UNLOCKED;
++
++ bfusb->reassembly = NULL;
++
++ skb_queue_head_init(&bfusb->transmit_q);
++ skb_queue_head_init(&bfusb->pending_q);
++ skb_queue_head_init(&bfusb->completed_q);
++
++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
++
++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
++ BT_ERR("Firmware request failed");
++ goto error;
++ }
++
++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
++ BT_ERR("Firmware loading failed");
++ goto release;
++ }
++
++ release_firmware(firmware);
++
++ /* Initialize and register HCI device */
++ hdev = &bfusb->hdev;
++
++ hdev->type = HCI_USB;
++ hdev->driver_data = bfusb;
++
++ hdev->open = bfusb_open;
++ hdev->close = bfusb_close;
++ hdev->flush = bfusb_flush;
++ hdev->send = bfusb_send_frame;
++ hdev->destruct = bfusb_destruct;
++ hdev->ioctl = bfusb_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device");
++ goto error;
++ }
++
++ return bfusb;
++
++release:
++ release_firmware(firmware);
++
++error:
++ kfree(bfusb);
++
++done:
++ return NULL;
++}
++
++static void bfusb_disconnect(struct usb_device *udev, void *ptr)
+{
-+ if (rtc_debug>1)
-+ printk("%s()\n", __FUNCTION__);
++ struct bfusb *bfusb = (struct bfusb *) ptr;
++ struct hci_dev *hdev = &bfusb->hdev;
+
-+ remove_proc_entry(DS1337_PROC_NAME, NULL);
-+ misc_deregister(&ds1337_rtc_miscdev);
-+ i2c_del_driver(&ds1337_driver);
++ BT_DBG("udev %p ptr %p", udev, ptr);
++
++ if (!hdev)
++ return;
++
++ bfusb_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
+}
+
++static struct usb_driver bfusb_driver = {
++ name: "bfusb",
++ probe: bfusb_probe,
++ disconnect: bfusb_disconnect,
++ id_table: bfusb_table,
++};
+
-+module_init(ds1337_init);
-+module_exit(ds1337_exit);
++static int __init bfusb_init(void)
++{
++ int err;
+
-+MODULE_PARM(slave_address, "i");
-+MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC");
++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
+
-+MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH");
-+MODULE_LICENSE("GPL");
---- /dev/null
-+++ linux-2.4.21/drivers/char/ds1337.h
-@@ -0,0 +1,43 @@
-+/*
-+ * ds1337.h
-+ *
-+ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
-+ *
-+ * 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.
-+ *
-+ */
-+#ifndef DS1337_H
-+#define DS1337_H
++ if ((err = usb_register(&bfusb_driver)) < 0)
++ BT_ERR("Failed to register BlueFRITZ! USB driver");
+
-+#define DS1337_I2C_SLAVE_ADDR 0x68
-+//#define DS1337_RAM_ADDR_START 0x10
-+//#define DS1337_RAM_ADDR_END 0x10
-+#define DS1337_MEM_SIZE 0x10
++ return err;
++}
+
-+#define DS1337_PROC_NAME "driver/ds1337"
++static void __exit bfusb_cleanup(void)
++{
++ usb_deregister(&bfusb_driver);
++}
+
-+struct rtc_mem {
-+ unsigned int loc;
-+ unsigned int nr;
-+ unsigned char *data;
-+};
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
+
-+#define DS1337_GETDATETIME 0
-+#define DS1337_SETTIME 1
-+#define DS1337_SETDATETIME 2
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/bluecard_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bluecard_cs.c
+@@ -803,6 +803,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
+
-+#define DS1337_RATE_1HZ 0x00 /* Rate Select 1 Hz */
-+#define DS1337_RATE_4096HZ 0x01 /* Rate Select 4096 kHz */
-+#define DS1337_RATE_8192HZ 0x02 /* Rate Select 8192 kHz */
-+#define DS1337_RATE_32768HZ 0x03 /* Rate Select 32768 kHz */
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+--- linux-2.4.21/drivers/bluetooth/bt3c_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bt3c_cs.c
+@@ -24,8 +24,6 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+-#define __KERNEL_SYSCALLS__
+-
+ #include <linux/kernel.h>
+ #include <linux/kmod.h>
+ #include <linux/init.h>
+@@ -48,6 +46,8 @@
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+
++#include <linux/firmware.h>
+
-+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
-+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+ #include <pcmcia/version.h>
+ #include <pcmcia/cs_types.h>
+ #include <pcmcia/cs.h>
+@@ -485,78 +485,101 @@
+
+
+
+-/* ======================== User mode firmware loader ======================== */
++/* ======================== Card services HCI interaction ======================== */
+
+
+-#define FW_LOADER "/sbin/bluefw"
+-static int errno;
++static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
++{
++ char *ptr = (char *) firmware;
++ char b[9];
++ unsigned int iobase, size, addr, fcs, tmp;
++ int i, err = 0;
+
++ iobase = info->link.io.BasePort1;
+
+-static int bt3c_fw_loader_exec(void *dev)
+-{
+- char *argv[] = { FW_LOADER, "pccard", dev, NULL };
+- char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+- int err;
++ /* Reset */
+
+- err = exec_usermodehelper(FW_LOADER, argv, envp);
+- if (err)
+- printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev);
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++ bt3c_io_write(iobase, 0x8040, 0x0400);
+
+- return err;
+-}
++ udelay(1);
+
++ bt3c_io_write(iobase, 0x8040, 0x0404);
+
+-static int bt3c_firmware_load(bt3c_info_t *info)
+-{
+- sigset_t tmpsig;
+- char dev[16];
+- pid_t pid;
+- int result;
++ udelay(17);
+
+- /* Check if root fs is mounted */
+- if (!current->fs->root) {
+- printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
+- return -EPERM;
+- }
++ /* Load */
+
+- sprintf(dev, "%04x", info->link.io.BasePort1);
++ while (count) {
++ if (ptr[0] != 'S') {
++ printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++ err = -EFAULT;
++ goto error;
++ }
+
+- pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0);
+- if (pid < 0) {
+- printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid);
+- return pid;
+- }
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 2, 2);
++ size = simple_strtol(b, NULL, 16);
+
+- /* Block signals, everything but SIGKILL/SIGSTOP */
+- spin_lock_irq(¤t->sigmask_lock);
+- tmpsig = current->blocked;
+- siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+- recalc_sigpending(current);
+- spin_unlock_irq(¤t->sigmask_lock);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 4, 8);
++ addr = simple_strtol(b, NULL, 16);
+
+- result = waitpid(pid, NULL, __WCLONE);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + (size * 2) + 2, 2);
++ fcs = simple_strtol(b, NULL, 16);
+
+- /* Allow signals again */
+- spin_lock_irq(¤t->sigmask_lock);
+- current->blocked = tmpsig;
+- recalc_sigpending(current);
+- spin_unlock_irq(¤t->sigmask_lock);
++ memset(b, 0, sizeof(b));
++ for (tmp = 0, i = 0; i < size; i++) {
++ memcpy(b, ptr + (i * 2) + 2, 2);
++ tmp += simple_strtol(b, NULL, 16);
++ }
+
+- if (result != pid) {
+- printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
+- return -result;
++ if (((tmp + fcs) & 0xff) != 0xff) {
++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
++ err = -EILSEQ;
++ goto error;
++ }
++
++ if (ptr[1] == '3') {
++ bt3c_address(iobase, addr);
++
++ memset(b, 0, sizeof(b));
++ for (i = 0; i < (size - 4) / 2; i++) {
++ memcpy(b, ptr + (i * 4) + 12, 4);
++ tmp = simple_strtol(b, NULL, 16);
++ bt3c_put(iobase, tmp);
++ }
++ }
+
-+#define DS1337_HOUR12 0x40
-+#define DS1337_HOUR24 0x00
-+#define DS1337_HOURS_24(val) BCD_TO_BIN((val & 0x3f))
++ ptr += (size * 2) + 6;
++ count -= (size * 2) + 6;
+ }
+
+- return 0;
+-}
++ udelay(17);
+
++ /* Boot */
+
++ bt3c_address(iobase, 0x3000);
++ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+-/* ======================== Card services HCI interaction ======================== */
++error:
++ udelay(17);
+
-+#endif
---- /dev/null
-+++ linux-2.4.21/drivers/char/german.map
-@@ -0,0 +1,528 @@
-+keymaps 0-2,4-6,8-10,12
-+keycode 1 = Escape Escape
-+ alt keycode 1 = Meta_Escape
-+ shift alt keycode 1 = Meta_Escape
-+keycode 2 = one exclam
-+ alt keycode 2 = Meta_one
-+ shift alt keycode 2 = Meta_exclam
++ /* Clear */
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++ bt3c_io_write(iobase, 0x7005, 0x0000);
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ return err;
++}
+
+
+ int bt3c_open(bt3c_info_t *info)
+ {
++ const struct firmware *firmware;
++ char device[16];
+ struct hci_dev *hdev;
+ int err;
+
+@@ -570,8 +593,22 @@
+
+ /* Load firmware */
+
+- if ((err = bt3c_firmware_load(info)) < 0)
++ snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
++
++ err = request_firmware(&firmware, "BT3CPCC.bin", device);
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
+ return err;
++ }
++
++ err = bt3c_load_firmware(info, firmware->data, firmware->size);
++
++ release_firmware(firmware);
++
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
++ return err;
++ }
+
+ /* Timeout before it is safe to send the first HCI packet */
+
+@@ -606,6 +643,9 @@
+ {
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ bt3c_hci_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+--- linux-2.4.21/drivers/bluetooth/btuart_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/btuart_cs.c
+@@ -556,6 +556,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ btuart_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/dtl1_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/dtl1_cs.c
+@@ -535,6 +535,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ dtl1_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/hci_bcsp.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_bcsp.c
+@@ -34,7 +34,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+@@ -635,7 +634,8 @@
+ struct sk_buff *skb;
+ unsigned long flags;
+
+- BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
++
+ spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+--- linux-2.4.21/drivers/bluetooth/hci_ldisc.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_ldisc.c
+@@ -33,7 +33,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+--- linux-2.4.21/drivers/bluetooth/hci_uart.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_uart.h
+@@ -35,11 +35,12 @@
+ #define HCIUARTGETPROTO _IOR('U', 201, int)
+
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO 3
++#define HCI_UART_MAX_PROTO 4
+
+ #define HCI_UART_H4 0
+ #define HCI_UART_BCSP 1
+-#define HCI_UART_NCSP 2
++#define HCI_UART_3WIRE 2
++#define HCI_UART_H4DS 3
+
+ #ifdef __KERNEL__
+ struct hci_uart;
+--- linux-2.4.21/drivers/bluetooth/hci_usb.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.c
+@@ -30,7 +30,7 @@
+ *
+ * $Id$
+ */
+-#define VERSION "2.4"
++#define VERSION "2.7"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -62,7 +62,7 @@
+ #define BT_DMP( A... )
+ #endif
+
+-#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
+ #undef USB_ZERO_PACKET
+ #define USB_ZERO_PACKET 0
+ #endif
+@@ -73,20 +73,39 @@
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+
+- /* Ericsson with non-standard id */
+- { USB_DEVICE(0x0bdb, 0x1002) },
++ /* AVM BlueFRITZ! USB v2.0 */
++ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
++ /* ALPS Modules with non-standard id */
++ { USB_DEVICE(0x044e, 0x3001) },
++ { USB_DEVICE(0x044e, 0x3002) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
+ { } /* Terminating entry */
+ };
+
+ MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+-static struct usb_device_id ignore_ids[] = {
++static struct usb_device_id blacklist_ids[] = {
+ /* Broadcom BCM2033 without firmware */
+- { USB_DEVICE(0x0a5c, 0x2033) },
++ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
++
++ /* Broadcom BCM2035 */
++ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
++
++ /* ISSC Bluetooth Adapter v3.1 */
++ { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
++
++ /* Digianswer device */
++ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
++
++ /* RTX Telecom based adapter with buggy SCO support */
++ { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
+
+ { } /* Terminating entry */
+ };
+@@ -133,6 +152,7 @@
+ return _urb_dequeue(__completed_q(husb, type));
+ }
+
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+ {
+ int offset = 0, i;
+@@ -152,6 +172,7 @@
+ }
+ urb->number_of_packets = i;
+ }
++#endif
+
+ static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+ {
+@@ -229,7 +250,7 @@
+ return err;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+ {
+ struct _urb *_urb;
+@@ -300,8 +321,10 @@
+ for (i = 0; i < HCI_MAX_BULK_RX; i++)
+ hci_usb_bulk_rx_submit(husb);
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- hci_usb_isoc_rx_submit(husb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (husb->isoc_iface)
++ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
++ hci_usb_isoc_rx_submit(husb);
+ #endif
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+@@ -426,7 +449,7 @@
+ } else
+ dr = (void *) _urb->urb.setup_packet;
+
+- dr->bRequestType = HCI_CTRL_REQ;
++ dr->bRequestType = husb->ctrl_req;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+@@ -467,7 +490,7 @@
+ return __tx_submit(husb, _urb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+@@ -518,10 +541,10 @@
+ skb_queue_head(q, skb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ /* Process SCO queue */
+ q = __transmit_q(husb, HCI_SCODATA_PKT);
+- if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) &&
++ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
+ (skb = skb_dequeue(q))) {
+ if (hci_usb_send_isoc(husb, skb) < 0)
+ skb_queue_head(q, skb);
+@@ -577,7 +600,7 @@
+ hdev->stat.acl_tx++;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+@@ -627,7 +650,7 @@
+ } else
+ return -EILSEQ;
+ break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ if (count >= HCI_SCO_HDR_SIZE) {
+ hci_sco_hdr *h = data;
+@@ -638,7 +661,7 @@
+ #endif
+ }
+ BT_DBG("new packet len %d", len);
+-
++
+ skb = bluez_skb_alloc(len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for the packet", husb->hdev.name);
+@@ -683,16 +706,16 @@
+ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
+ _urb->type, urb->status, count, urb->transfer_flags);
+
+- if (!test_bit(HCI_RUNNING, &hdev->flags))
+- return;
+-
+ read_lock(&husb->completion_lock);
+
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto unlock;
++
+ if (urb->status || !count)
+ goto resubmit;
+
+ if (_urb->type == HCI_SCODATA_PKT) {
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ int i;
+ for (i=0; i < urb->number_of_packets; i++) {
+ BT_DBG("desc %d status %d offset %d len %d", i,
+@@ -724,6 +747,8 @@
+ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+ _urb->type, err);
+ }
++
++unlock:
+ read_unlock(&husb->completion_lock);
+ }
+
+@@ -786,8 +811,14 @@
+
+ iface = &udev->actconfig->interface[0];
+
+- /* Check our black list */
+- if (usb_match_id(udev, iface, ignore_ids))
++ if (!id->driver_info) {
++ const struct usb_device_id *match;
++ match = usb_match_id(udev, iface, blacklist_ids);
++ if (match)
++ id = match;
++ }
++
++ if (id->driver_info & HCI_IGNORE)
+ return NULL;
+
+ /* Check number of endpoints */
+@@ -827,9 +858,9 @@
+ bulk_out_ep[i] = ep;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case USB_ENDPOINT_XFER_ISOC:
+- if (ep->wMaxPacketSize < size)
++ if (ep->wMaxPacketSize < size || a > 2)
+ break;
+ size = ep->wMaxPacketSize;
+
+@@ -853,8 +884,8 @@
+ goto done;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
+ BT_DBG("Isoc endpoints not found");
+ isoc_iface = NULL;
+ }
+@@ -872,7 +903,12 @@
+ husb->bulk_in_ep = bulk_in_ep[0];
+ husb->intr_in_ep = intr_in_ep[0];
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++ if (id->driver_info & HCI_DIGIANSWER)
++ husb->ctrl_req = HCI_DIGI_REQ;
++ else
++ husb->ctrl_req = HCI_CTRL_REQ;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ if (isoc_iface) {
+ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
+ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+@@ -906,6 +942,9 @@
+ hdev->send = hci_usb_send_frame;
+ hdev->destruct = hci_usb_destruct;
+
++ if (id->driver_info & HCI_RESET)
++ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
++
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ goto probe_error;
+@@ -968,6 +1007,6 @@
+ module_init(hci_usb_init);
+ module_exit(hci_usb_cleanup);
+
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+ MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
+ MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/hci_usb.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.h
+@@ -35,12 +35,21 @@
+ #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+
+ #define HCI_CTRL_REQ 0x20
++#define HCI_DIGI_REQ 0x40
++
++#define HCI_IGNORE 0x01
++#define HCI_RESET 0x02
++#define HCI_DIGIANSWER 0x04
++#define HCI_BROKEN_ISOC 0x08
+
+ #define HCI_MAX_IFACE_NUM 3
+
+ #define HCI_MAX_BULK_TX 4
+ #define HCI_MAX_BULK_RX 1
+
++#define HCI_MAX_ISOC_RX 2
++#define HCI_MAX_ISOC_TX 2
++
+ #define HCI_MAX_ISOC_FRAMES 10
+
+ struct _urb_queue {
+@@ -119,6 +128,8 @@
+ struct usb_endpoint_descriptor *isoc_out_ep;
+ struct usb_endpoint_descriptor *isoc_in_ep;
+
++ __u8 ctrl_req;
++
+ struct sk_buff_head transmit_q[4];
+ struct sk_buff *reassembly[4]; // Reassembly buffers
+
+--- linux-2.4.21/drivers/char/Config.in~i2c-ds1337
++++ linux-2.4.21/drivers/char/Config.in
+@@ -164,6 +164,7 @@
+
+ if [ "$CONFIG_I2C" != "n" ]; then
+ dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
++ dep_tristate ' DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C
+ fi
+
+ source drivers/l3/Config.in
+--- linux-2.4.21/drivers/char/Makefile~i2c-ds1337
++++ linux-2.4.21/drivers/char/Makefile
+@@ -21,10 +21,11 @@
+ # All of the (potential) objects that export symbols.
+ # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+
+-export-objs := busmouse.o console.o keyboard.o sysrq.o \
++export-objs := vt.o busmouse.o console.o keyboard.o sysrq.o \
+ misc.o pty.o random.o selection.o serial.o \
+ sonypi.o tty_io.o tty_ioctl.o generic_serial.o \
+- au1000_gpio.o hp_psaux.o nvram.o scx200.o
++ au1000_gpio.o hp_psaux.o nvram.o scx200.o \
++ input_keyb.o
+
+ mod-subdirs := joystick ftape drm drm-4.0 pcmcia
+
+@@ -129,6 +130,11 @@
+ ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
+ KEYBD += cerf_keyb.o
+ endif
++ ifeq ($(CONFIG_ARCH_RAMSES),y)
++ KEYMAP = german.o
++ KEYBD += input_keyb.o
++ obj-m += sysctl.o
++ endif
+ ifeq ($(CONFIG_ARCH_FORTUNET),y)
+ KEYMAP := defkeymap.o
+ endif
+@@ -337,6 +343,7 @@
+
+ # I2C char devices
+ obj-$(CONFIG_I2C_DS1307) += ds1307.o
++obj-$(CONFIG_I2C_DS1337) += ds1337.o
+
+ subdir-$(CONFIG_MWAVE) += mwave
+ ifeq ($(CONFIG_MWAVE),y)
+@@ -373,3 +380,6 @@
+
+ qtronixmap.c: qtronixmap.map
+ set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
++
++german.c: german.map
++ set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
+--- linux-2.4.21/drivers/char/console.c~keyb-module
++++ linux-2.4.21/drivers/char/console.c
+@@ -150,7 +150,7 @@
+ static int con_open(struct tty_struct *, struct file *);
+ static void vc_init(unsigned int console, unsigned int rows,
+ unsigned int cols, int do_clear);
+-static void blank_screen(unsigned long dummy);
++//static void blank_screen(unsigned long dummy);
+ static void gotoxy(int currcons, int new_x, int new_y);
+ static void save_cur(int currcons);
+ static void reset_terminal(int currcons, int do_clear);
+@@ -158,7 +158,7 @@
+ static void set_vesa_blanking(unsigned long arg);
+ static void set_cursor(int currcons);
+ static void hide_cursor(int currcons);
+-static void unblank_screen_t(unsigned long dummy);
++//static void unblank_screen_t(unsigned long dummy);
+ static void console_callback(void *ignored);
+
+ static int printable; /* Is console ready for printing? */
+@@ -167,7 +167,7 @@
+ int console_blanked;
+
+ static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
+-static int blankinterval = 10*60*HZ;
++//static int blankinterval = 10*60*HZ;
+ static int vesa_off_interval;
+
+ static struct tq_struct console_callback_tq = {
+@@ -209,9 +209,9 @@
+ * Hook so that the power management routines can (un)blank
+ * the console on our behalf.
+ */
+-int (*console_blank_hook)(int);
++//int (*console_blank_hook)(int);
+
+-static struct timer_list console_timer;
++//static struct timer_list console_timer;
+
+ /*
+ * Low-Level Functions
+@@ -543,7 +543,7 @@
+
+ static void set_cursor(int currcons)
+ {
+- if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
++ if (!IS_FG || vcmode == KD_GRAPHICS)
+ return;
+ if (deccm) {
+ if (currcons == sel_cons)
+@@ -1287,7 +1287,7 @@
+ update_attr(currcons);
+ break;
+ case 9: /* set blanking interval */
+- blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
++ //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
+ poke_blanked_console();
+ break;
+ case 10: /* set bell frequency in Hz */
+@@ -2575,11 +2575,11 @@
+ if (tty_register_driver(&console_driver))
+ panic("Couldn't register console driver\n");
+
+- init_timer(&console_timer);
+- console_timer.function = blank_screen;
+- if (blankinterval) {
+- mod_timer(&console_timer, jiffies + blankinterval);
+- }
++ //init_timer(&console_timer);
++ //console_timer.function = blank_screen;
++ //if (blankinterval) {
++ // mod_timer(&console_timer, jiffies + blankinterval);
++ //}
+
+ /*
+ * kmalloc is not running yet - we use the bootmem allocator.
+@@ -2744,11 +2744,12 @@
+ */
+ static void vesa_powerdown_screen(unsigned long dummy)
+ {
+- console_timer.function = unblank_screen_t;
++ //console_timer.function = unblank_screen_t;
+
+ vesa_powerdown();
+ }
+
++#if 0
+ static void timer_do_blank_screen(int entering_gfx, int from_timer_handler)
+ {
+ int currcons = fg_console;
+@@ -2797,12 +2798,14 @@
+ if (vesa_blank_mode)
+ sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
+ }
++#endif
+
+ void do_blank_screen(int entering_gfx)
+ {
+- timer_do_blank_screen(entering_gfx, 0);
++ //timer_do_blank_screen(entering_gfx, 0);
+ }
+
++#if 0
+ /*
+ * This is a timer handler
+ */
+@@ -2810,12 +2813,14 @@
+ {
+ unblank_screen();
+ }
++#endif
+
+ /*
+ * Called by timer as well as from vt_console_driver
+ */
+ void unblank_screen(void)
+ {
++#if 0
+ int currcons;
+
+ if (!console_blanked)
+@@ -2842,6 +2847,7 @@
+ /* Low-level driver cannot restore -> do it ourselves */
+ update_screen(fg_console);
+ set_cursor(fg_console);
++#endif
+ }
+
+ /*
+@@ -2849,11 +2855,12 @@
+ */
+ static void blank_screen(unsigned long dummy)
+ {
+- timer_do_blank_screen(0, 1);
++ //timer_do_blank_screen(0, 1);
+ }
+
+ void poke_blanked_console(void)
+ {
++#if 0
+ del_timer(&console_timer);
+ if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return;
+@@ -2863,6 +2870,7 @@
+ } else if (blankinterval) {
+ mod_timer(&console_timer, jiffies + blankinterval);
+ }
++#endif
+ }
+
+ /*
+@@ -3088,7 +3096,7 @@
+ unblank_screen();
+ break;
+ case PM_SUSPEND:
+- do_blank_screen(0);
++ //do_blank_screen(0);
+ break;
+ }
+ return 0;
+@@ -3106,7 +3114,8 @@
+ EXPORT_SYMBOL(video_scan_lines);
+ EXPORT_SYMBOL(vc_resize);
+ EXPORT_SYMBOL(fg_console);
+-EXPORT_SYMBOL(console_blank_hook);
++//EXPORT_SYMBOL(console_blank_hook);
++EXPORT_SYMBOL(console_driver);
+ #ifdef CONFIG_VT
+ EXPORT_SYMBOL(vt_cons);
+ #endif
+--- /dev/null
++++ linux-2.4.21/drivers/char/ds1337.c
+@@ -0,0 +1,545 @@
++/*
++ * ds1337.c
++ *
++ * Device driver for Dallas Semiconductor's Real Time Controller DS1337.
++ *
++ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ *
++ * 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.
++ *
++ * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.pdf
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtc.h>
++#include <linux/string.h>
++#include <linux/miscdevice.h>
++#include <linux/proc_fs.h>
++
++#include "ds1337.h"
++
++//#define DEBUG 1
++
++#if DEBUG
++static unsigned int rtc_debug = DEBUG;
++#else
++#define rtc_debug 0 /* gcc will remove all the debug code for us */
++#endif
++
++static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR;
++struct i2c_driver ds1337_driver;
++struct i2c_client *ds1337_i2c_client = 0;
++static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED;
++
++static unsigned short ignore[] = { I2C_CLIENT_END };
++static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++
++static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++static int ds1337_rtc_noop(struct inode *inode, struct file *file);
++
++static int ds1337_probe(struct i2c_adapter *adap);
++static int ds1337_detach(struct i2c_client *client);
++static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++
++static struct i2c_client_address_data addr_data = {
++ .normal_i2c = normal_addr,
++ .normal_i2c_range = ignore,
++ .probe = ignore,
++ .probe_range = ignore,
++ .ignore = ignore,
++ .ignore_range = ignore,
++ .force = ignore,
++};
++
++static struct file_operations rtc_fops = {
++ .owner = THIS_MODULE,
++ .ioctl = ds1337_rtc_ioctl,
++ .open = ds1337_rtc_noop,
++ .release = ds1337_rtc_noop,
++};
++
++static struct miscdevice ds1337_rtc_miscdev = {
++ RTC_MINOR,
++ "rtc",
++ &rtc_fops
++};
++
++
++struct i2c_driver ds1337_driver = {
++ .name = "DS1337",
++ .id = I2C_DRIVERID_DS1337,
++ .flags = I2C_DF_NOTIFY,
++ .attach_adapter = ds1337_probe,
++ .detach_client = ds1337_detach,
++ .command = ds1337_command
++};
++
++#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */
++
++
++static int ds1337_readram(char *buf, int len)
++{
++ unsigned long flags;
++ unsigned char ad[1] = { 0 };
++ int ret;
++ struct i2c_msg msgs[2] = {
++ {ds1337_i2c_client->addr, 0, 1, ad},
++ {ds1337_i2c_client->addr, I2C_M_RD, len, buf}
++ };
++
++ spin_lock_irqsave(&ds1337_rtc_lock, flags);
++ ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2);
++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++
++ return ret;
++}
++
++
++static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val)
++{
++ unsigned char buf[2];
++ buf[0] = reg;
++ buf[1] = val;
++ i2c_master_send(c, (char *) buf, 2);
++}
++
++static int ds1337_attach(struct i2c_adapter *adap, int addr,
++ unsigned short flags, int kind)
++{
++ struct i2c_client *c;
++ unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 };
++ struct i2c_msg msgs[2] = {
++ {addr, 0, 1, ad},
++ {addr, I2C_M_RD, 1, buf}
++ };
++ int ret;
++
++ if (rtc_debug>1)
++ printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind);
++
++ c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL);
++ if (!c)
++ return -ENOMEM;
++
++ strcpy(c->name, "DS1337");
++ c->id = ds1337_driver.id;
++ c->flags = 0;
++ c->addr = addr;
++ c->adapter = adap;
++ c->driver = &ds1337_driver;
++ c->data = NULL;
++
++ ret = i2c_transfer(c->adapter, msgs, 2);
++
++ if (ret == 2) {
++ DAT(c) = buf[0];
++ } else
++ printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret);
++
++ ds1337_i2c_client = c;
++
++ ds1337_readram(buf, DS1337_MEM_SIZE);
++
++ // set 24 hour mode
++ ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24);
++ // INTCN sets INTB to alarm2 (disables SQW)
++ ds1337_setreg(c, 0x5, buf[5] & 0x7f); // clear century
++ ds1337_setreg(c, 0x7, 0x00); // clear Alarm 1 seconds
++ ds1337_setreg(c, 0x8, 0x00); // clear Alarm 1 minutes
++ ds1337_setreg(c, 0x9, 0x40); // clear Alarm 1 hours, 24 hour on
++ ds1337_setreg(c, 0xA, 0x00); // clear Alarm 1 date
++ ds1337_setreg(c, 0xB, 0x00); // clear Alarm 2 minutes
++ ds1337_setreg(c, 0xC, 0x40); // clear Alarm 2 hours, 24 hour on
++ ds1337_setreg(c, 0xD, 0x00); // clear Alarm 2 date
++ ds1337_setreg(c, 0xe, 4); // nEOSC enabled
++ ds1337_setreg(c, 0xf, 0); // clear OSF, A2F, A1F
++
++ return i2c_attach_client(c);
++}
++
++
++static int ds1337_probe(struct i2c_adapter *adap)
++{
++ if (rtc_debug>1)
++ printk("%s()\n", __FUNCTION__);
++
++ return i2c_probe(adap, &addr_data, ds1337_attach);
++}
++
++
++static int ds1337_detach(struct i2c_client *client)
++{
++ if (rtc_debug>1)
++ printk("%s()\n", __FUNCTION__);
++
++ i2c_detach_client(client);
++
++ return 0;
++}
++
++
++static void ds1337_convert_to_time(struct rtc_time *dt, char *buf)
++{
++ if (rtc_debug>1)
++ printk("%s()\n", __FUNCTION__);
++
++ dt->tm_sec = BCD_TO_BIN(buf[0]);
++ dt->tm_min = BCD_TO_BIN(buf[1]);
++ dt->tm_hour = DS1337_HOURS_24(buf[2]);
++
++ dt->tm_mday = BCD_TO_BIN(buf[4]);
++ /* dt->tm_mon is zero-based */
++ dt->tm_mon = BCD_TO_BIN(buf[5]) - 1;
++ /* year is 1900 + dt->tm_year */
++ dt->tm_year = BCD_TO_BIN(buf[6]) + 100;
++
++ if (rtc_debug > 2) {
++ printk("ds1337_get_datetime: year = %d\n", dt->tm_year);
++ printk("ds1337_get_datetime: mon = %d\n", dt->tm_mon);
++ printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday);
++ printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour);
++ printk("ds1337_get_datetime: min = %d\n", dt->tm_min);
++ printk("ds1337_get_datetime: sec = %d\n", dt->tm_sec);
++ }
++}
++
++
++static int ds1337_get_datetime(struct i2c_client *client,
++ struct rtc_time *dt)
++{
++ unsigned char buf[7], addr[1] = { 0 };
++ struct i2c_msg msgs[2] = {
++ {client->addr, 0, 1, addr},
++ {client->addr, I2C_M_RD, 7, buf}
++ };
++ int ret = -EIO;
++
++ if (rtc_debug)
++ printk("%s()\n", __FUNCTION__);
++
++ memset(buf, 0, sizeof(buf));
++
++ ret = i2c_transfer(client->adapter, msgs, 2);
++
++ if (ret == 2) {
++ ds1337_convert_to_time(dt, buf);
++ ret = 0;
++ } else
++ printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret);
++
++ return ret;
++}
++
++
++static int ds1337_set_datetime(struct i2c_client *client,
++ struct rtc_time *dt, int datetoo)
++{
++ unsigned char buf[8];
++ int ret, len = 4;
++
++ if (rtc_debug)
++ printk("%s()\n", __FUNCTION__);
++
++ if (rtc_debug > 2) {
++ printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year);
++ printk("ds1337_set_datetime: tm_mon = %d\n", dt->tm_mon);
++ printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday);
++ printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour);
++ printk("ds1337_set_datetime: tm_min = %d\n", dt->tm_min);
++ printk("ds1337_set_datetime: tm_sec = %d\n", dt->tm_sec);
++ }
++
++ buf[0] = 0; /* register address on DS1337 */
++ buf[1] = (BIN_TO_BCD(dt->tm_sec));
++ buf[2] = (BIN_TO_BCD(dt->tm_min));
++ buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24;
++
++ if (datetoo) {
++ len = 8;
++ /* we skip buf[4] as we don't use day-of-week. */
++ buf[5] = (BIN_TO_BCD(dt->tm_mday));
++ buf[6] = (BIN_TO_BCD(dt->tm_mon + 1));
++ /* The year only ranges from 0-99, we are being passed an offset from 1900,
++ * and the chip calulates leap years based on 2000, thus we adjust by 100.
++ */
++ buf[7] = (BIN_TO_BCD(dt->tm_year - 100));
++ }
++ ret = i2c_master_send(client, (char *) buf, len);
++ if (ret == len)
++ ret = 0;
++ else
++ printk("ds1337_set_datetime(), i2c_master_send() returned %d\n",
++ ret);
++
++
++ return ret;
++}
++
++
++#if 0
++static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
++{
++ *ctrl = DAT(client);
++
++ if (rtc_debug)
++ printk("%s():%d\n", __FUNCTION__, *ctrl);
++
++ return 0;
++}
++
++
++static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo)
++{
++ unsigned char buf[2];
++ int ret;
++
++ if (rtc_debug)
++ printk("%s(%d)\n", __FUNCTION__, *cinfo);
++
++ buf[0] = 7; /* control register address on DS1337 */
++ buf[1] = *cinfo;
++ /* save the control reg info in the client data field so that get_ctrl
++ * function doesn't have to do an I2C transfer to get it.
++ */
++ DAT(client) = buf[1];
++
++ ret = i2c_master_send(client, (char *) buf, 2);
++
++ return ret;
++}
++#endif
++
++
++static int ds1337_command(struct i2c_client *client, unsigned int cmd,
++ void *arg)
++{
++ if (rtc_debug)
++ printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd);
++
++ switch (cmd) {
++ case DS1337_GETDATETIME:
++ return ds1337_get_datetime(client, arg);
++
++ case DS1337_SETTIME:
++ return ds1337_set_datetime(client, arg, 0);
++
++ case DS1337_SETDATETIME:
++ return ds1337_set_datetime(client, arg, 1);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++
++static int ds1337_rtc_noop(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++
++static int ds1337_rtc_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ unsigned long flags;
++ struct rtc_time wtime;
++ int status = 0;
++
++ if (rtc_debug)
++ printk("%s()\n", __FUNCTION__);
++
++ switch (cmd) {
++ default:
++ case RTC_UIE_ON: // mask ints from RTC updates
++ case RTC_UIE_OFF:
++ case RTC_PIE_ON: // allow periodic interrupts
++ case RTC_PIE_OFF:
++ case RTC_AIE_ON: // mask alarm int enable bit
++ case RTC_AIE_OFF:
++ case RTC_ALM_SET:
++ /*
++ * This expects a struct rtc_time. Writing 0xff means
++ * "don't care" or "match all". Only the tm_hour,
++ * tm_min and tm_sec are used.
++ */
++ case RTC_ALM_READ:
++ // get_rtc_alm_time(&wtime);
++ case RTC_IRQP_READ: // Read the periodic IRQ rate
++ case RTC_IRQP_SET: // Set periodic IRQ rate
++ case RTC_EPOCH_READ:
++ // return put_user (epoch, (unsigned long *)arg);
++ case RTC_EPOCH_SET:
++ case RTC_WKALM_SET:
++ case RTC_WKALM_RD:
++ status = -EINVAL;
++ break;
++
++ case RTC_RD_TIME:
++ spin_lock_irqsave(&ds1337_rtc_lock, flags);
++ ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime);
++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++
++ if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time)))
++ status = -EFAULT;
++ break;
++
++ case RTC_SET_TIME:
++ if (!capable(CAP_SYS_TIME)) {
++ status = -EACCES;
++ break;
++ }
++
++ if (copy_from_user
++ (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) {
++ status = -EFAULT;
++ break;
++ }
++
++ spin_lock_irqsave(&ds1337_rtc_lock, flags);
++ ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime);
++ spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++ break;
++ }
++
++ return status;
++}
++
++
++static char *ds1337_mon2str(unsigned int mon)
++{
++ char *mon2str[12] = {
++ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
++ };
++ if (mon > 11)
++ return "error";
++ else
++ return mon2str[mon];
++}
++
++
++static int ds1337_rtc_proc_output(char *buf)
++{
++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++
++ unsigned char ram[DS1337_MEM_SIZE];
++ int ret;
++
++ char *p = buf;
++
++ ret = ds1337_readram(ram, DS1337_MEM_SIZE);
++ if (ret > 0) {
++#ifdef DEBUG
++ int i;
++ char text[9];
++#endif
++ struct rtc_time dt;
++
++ p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n");
++
++ ds1337_convert_to_time(&dt, ram);
++ p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n",
++ dt.tm_mday, ds1337_mon2str(dt.tm_mon),
++ dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec);
++
++#ifdef DEBUG
++ p += sprintf(p, "RAM dump:\n");
++ text[8] = '\0';
++ for (i = 0; i < DS1337_MEM_SIZE; i++) {
++ if ((i % 8) == 0)
++ p += sprintf(p, "%02X: ", i);
++ p += sprintf(p, "%02X ", ram[i]);
++
++ if ((ram[i] < 32) || (ram[i] > 126))
++ ram[i] = '.';
++ text[i % 8] = ram[i];
++ if ((i % 8) == 7)
++ p += sprintf(p, "%s\n", text);
++ }
++ p += sprintf(p, "\n");
++#endif
++ } else {
++ p += sprintf(p, "Failed to read RTC memory!\n");
++ }
++
++ return p - buf;
++}
++
++
++static int ds1337_rtc_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = ds1337_rtc_proc_output(page);
++
++ if (len <= off + count)
++ *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++ return len;
++}
++
++
++static __init int ds1337_init(void)
++{
++ int retval = 0;
++
++ if (rtc_debug>1)
++ printk("%s()\n", __FUNCTION__);
++
++ if (slave_address != 0xffff) {
++ normal_addr[0] = slave_address;
++ }
++
++ if (normal_addr[0] == 0xffff) {
++ printk(KERN_ERR
++ "I2C: Invalid slave address for DS1337 RTC (%#x)\n",
++ normal_addr[0]);
++ return -EINVAL;
++ }
++
++ retval = i2c_add_driver(&ds1337_driver);
++
++ if (retval == 0) {
++ misc_register(&ds1337_rtc_miscdev);
++ create_proc_read_entry(DS1337_PROC_NAME, 0, 0,
++ ds1337_rtc_read_proc, NULL);
++ printk("I2C: DS1337 RTC driver loaded\n");
++ }
++ return retval;
++}
++
++
++static __exit void ds1337_exit(void)
++{
++ if (rtc_debug>1)
++ printk("%s()\n", __FUNCTION__);
++
++ remove_proc_entry(DS1337_PROC_NAME, NULL);
++ misc_deregister(&ds1337_rtc_miscdev);
++ i2c_del_driver(&ds1337_driver);
++}
++
++
++module_init(ds1337_init);
++module_exit(ds1337_exit);
++
++MODULE_PARM(slave_address, "i");
++MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC");
++
++MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/drivers/char/ds1337.h
+@@ -0,0 +1,43 @@
++/*
++ * ds1337.h
++ *
++ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ *
++ * 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.
++ *
++ */
++#ifndef DS1337_H
++#define DS1337_H
++
++#define DS1337_I2C_SLAVE_ADDR 0x68
++//#define DS1337_RAM_ADDR_START 0x10
++//#define DS1337_RAM_ADDR_END 0x10
++#define DS1337_MEM_SIZE 0x10
++
++#define DS1337_PROC_NAME "driver/ds1337"
++
++struct rtc_mem {
++ unsigned int loc;
++ unsigned int nr;
++ unsigned char *data;
++};
++
++#define DS1337_GETDATETIME 0
++#define DS1337_SETTIME 1
++#define DS1337_SETDATETIME 2
++
++#define DS1337_RATE_1HZ 0x00 /* Rate Select 1 Hz */
++#define DS1337_RATE_4096HZ 0x01 /* Rate Select 4096 kHz */
++#define DS1337_RATE_8192HZ 0x02 /* Rate Select 8192 kHz */
++#define DS1337_RATE_32768HZ 0x03 /* Rate Select 32768 kHz */
++
++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
++
++#define DS1337_HOUR12 0x40
++#define DS1337_HOUR24 0x00
++#define DS1337_HOURS_24(val) BCD_TO_BIN((val & 0x3f))
++
++#endif
+--- /dev/null
++++ linux-2.4.21/drivers/char/german.map
+@@ -0,0 +1,528 @@
++keymaps 0-2,4-6,8-10,12
++keycode 1 = Escape Escape
++ alt keycode 1 = Meta_Escape
++ shift alt keycode 1 = Meta_Escape
++keycode 2 = one exclam
++ alt keycode 2 = Meta_one
++ shift alt keycode 2 = Meta_exclam
+keycode 3 = two quotedbl twosuperior nul
+ alt keycode 3 = Meta_two
+ shift alt keycode 3 = Meta_quotedbl
static void change_speed(struct async_struct *info, struct termios *old);
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-@@ -325,46 +165,82 @@
+@@ -325,46 +165,86 @@
{ 0, 0}
};
+ flags: ASYNC_SKIP_TEST,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }
};
#ifndef PREPARE_FUNC
#define PREPARE_FUNC(dev) (dev->prepare)
-@@ -403,39 +279,21 @@
+@@ -403,39 +283,21 @@
#endif
return readl((unsigned long) info->iomem_base +
(offset<<info->iomem_reg_shift));
default:
-@@ -447,17 +305,14 @@
+@@ -447,17 +309,14 @@
int value)
{
switch (info->io_type) {
writel(value, (unsigned long) info->iomem_base +
(offset<<info->iomem_reg_shift));
break;
-@@ -509,9 +364,6 @@
+@@ -509,9 +368,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
save_flags(flags); cli();
if (info->IER & UART_IER_THRI) {
info->IER &= ~UART_IER_THRI;
-@@ -529,9 +381,6 @@
+@@ -529,9 +385,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
save_flags(flags); cli();
if (info->xmit.head != info->xmit.tail
&& info->xmit.buf
-@@ -689,11 +538,7 @@
+@@ -689,11 +542,7 @@
#endif
*status = serial_inp(info, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
-@@ -758,11 +603,6 @@
+@@ -758,11 +607,6 @@
icount->dsr++;
if (status & UART_MSR_DDCD) {
icount->dcd++;
}
if (status & UART_MSR_DCTS)
icount->cts++;
-@@ -810,120 +650,23 @@
+@@ -810,120 +654,23 @@
}
}
do {
status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
-@@ -932,120 +675,23 @@
+@@ -932,120 +679,23 @@
if (status & UART_LSR_DR)
receive_chars(info, &status, regs);
check_modem_status(info);
/*
* -------------------------------------------------------------------
-@@ -1107,22 +753,6 @@
+@@ -1107,22 +757,6 @@
if (!info)
continue;
save_flags(flags); cli();
rs_interrupt_single(i, NULL, NULL);
restore_flags(flags);
}
-@@ -1132,11 +762,7 @@
+@@ -1132,11 +766,7 @@
if (IRQ_ports[0]) {
save_flags(flags); cli();
restore_flags(flags);
mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
-@@ -1177,50 +803,6 @@
+@@ -1177,50 +807,6 @@
IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
}
static int startup(struct async_struct * info)
{
unsigned long flags;
-@@ -1228,9 +810,6 @@
+@@ -1228,9 +814,6 @@
void (*handler)(int, void *, struct pt_regs *);
struct serial_state *state= info->state;
unsigned long page;
page = get_zeroed_page(GFP_KERNEL);
if (!page)
-@@ -1258,6 +837,22 @@
+@@ -1258,6 +841,22 @@
printk("starting up ttys%d (irq %d)...", info->line, state->irq);
#endif
if (uart_config[state->type].flags & UART_STARTECH) {
/* Wake up UART */
serial_outp(info, UART_LCR, 0xBF);
-@@ -1305,25 +900,12 @@
+@@ -1305,25 +904,12 @@
serial_outp(info, UART_LCR, 0);
}
case (long)&STUART: CKEN |= CKEN5_STUART; break;
}
}
-@@ -1344,6 +926,7 @@
+@@ -1344,6 +930,7 @@
/*
* Clear the interrupt registers.
*/
(void) serial_inp(info, UART_LSR);
(void) serial_inp(info, UART_RX);
(void) serial_inp(info, UART_IIR);
-@@ -1371,18 +954,8 @@
+@@ -1371,18 +958,8 @@
if (state->irq && (!IRQ_ports[state->irq] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
} else
handler = rs_interrupt_single;
-@@ -1417,12 +990,6 @@
+@@ -1417,12 +994,6 @@
info->MCR = 0;
if (info->tty->termios->c_cflag & CBAUD)
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
{
if (state->irq != 0)
info->MCR |= UART_MCR_OUT2;
-@@ -1437,18 +1004,9 @@
+@@ -1437,18 +1008,9 @@
*/
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
if (pxa_port(state->type))
/*
* And clear the interrupt registers again for luck.
*/
-@@ -1469,7 +1027,6 @@
+@@ -1469,7 +1031,6 @@
/*
* Set up the tty->alt_speed kludge
*/
if (info->tty) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
info->tty->alt_speed = 57600;
-@@ -1480,7 +1037,6 @@
+@@ -1480,7 +1041,6 @@
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
}
/*
* and set the speed of the serial port
-@@ -1516,6 +1072,30 @@
+@@ -1516,6 +1076,30 @@
state->irq);
#endif
save_flags(flags); cli(); /* Disable interrupts */
/*
-@@ -1561,13 +1141,6 @@
+@@ -1561,13 +1145,6 @@
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
info->MCR &= ~UART_MCR_OUT2;
if (pxa_buggy_port(state->type))
info->MCR ^= UART_MCR_OUT2;
-@@ -1586,16 +1159,6 @@
+@@ -1586,16 +1163,6 @@
UART_FCR_CLEAR_XMIT));
serial_outp(info, UART_FCR, 0);
#ifdef CONFIG_ARCH_PXA
if (state->type == PORT_PXA
#ifdef CONFIG_SERIAL_CONSOLE
-@@ -1634,37 +1197,6 @@
+@@ -1634,37 +1201,6 @@
restore_flags(flags);
}
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
-@@ -1711,12 +1243,6 @@
+@@ -1711,12 +1247,6 @@
baud = tty_get_baud_rate(info->tty);
if (!baud)
baud = 9600; /* B0 transition handled in rs_set_termios */
baud_base = info->state->baud_base;
if (info->state->type == PORT_16C950) {
if (baud <= baud_base)
-@@ -1778,10 +1304,6 @@
+@@ -1778,10 +1308,6 @@
if (uart_config[info->state->type].flags & UART_USE_FIFO) {
if ((info->state->baud_base / quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
}
-@@ -1864,9 +1386,6 @@
+@@ -1864,9 +1390,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (!tty || !info->xmit.buf)
return;
-@@ -1888,9 +1407,6 @@
+@@ -1888,9 +1411,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (info->xmit.head == info->xmit.tail
|| tty->stopped
|| tty->hw_stopped
-@@ -1900,8 +1416,6 @@
+@@ -1900,8 +1420,6 @@
save_flags(flags); cli();
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
restore_flags(flags);
}
-@@ -1912,9 +1426,6 @@
+@@ -1912,9 +1430,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (!tty || !info->xmit.buf || !tmp_buf)
return 0;
-@@ -1978,11 +1489,6 @@
+@@ -1978,11 +1493,6 @@
&& !(info->IER & UART_IER_THRI)) {
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
return ret;
}
-@@ -1991,8 +1497,6 @@
+@@ -1991,8 +1501,6 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
-@@ -2000,8 +1504,6 @@
+@@ -2000,8 +1508,6 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
-@@ -2010,8 +1512,6 @@
+@@ -2010,8 +1516,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
save_flags(flags); cli();
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
-@@ -2032,16 +1532,11 @@
+@@ -2032,16 +1536,11 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
}
}
-@@ -2064,9 +1559,6 @@
+@@ -2064,9 +1563,6 @@
tty->ldisc.chars_in_buffer(tty));
#endif
if (I_IXOFF(tty))
rs_send_xchar(tty, STOP_CHAR(tty));
-@@ -2089,9 +1581,6 @@
+@@ -2089,9 +1585,6 @@
tty->ldisc.chars_in_buffer(tty));
#endif
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
-@@ -2134,7 +1623,6 @@
+@@ -2134,7 +1627,6 @@
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
tmp.io_type = state->io_type;
if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
return -EFAULT;
-@@ -2160,8 +1648,7 @@
+@@ -2160,8 +1652,7 @@
new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
change_irq = new_serial.irq != state->irq;
if (!capable(CAP_SYS_ADMIN)) {
if (change_irq || change_port ||
-@@ -2198,7 +1685,6 @@
+@@ -2198,7 +1689,6 @@
if (new_serial.type) {
for (i = 0 ; i < NR_PORTS; i++)
if ((state != &rs_table[i]) &&
(rs_table[i].port == new_port) &&
rs_table[i].type)
return -EADDRINUSE;
-@@ -2220,18 +1706,11 @@
+@@ -2220,18 +1710,11 @@
state->custom_divisor = new_serial.custom_divisor;
state->close_delay = new_serial.close_delay * HZ/100;
state->closing_wait = new_serial.closing_wait * HZ/100;
release_region(state->port,8);
}
state->type = new_serial.type;
-@@ -2243,31 +1722,19 @@
+@@ -2243,31 +1726,19 @@
shutdown(info);
state->irq = new_serial.irq;
info->port = state->port = new_port;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
info->tty->alt_speed = 57600;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-@@ -2276,7 +1743,6 @@
+@@ -2276,7 +1747,6 @@
info->tty->alt_speed = 230400;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
change_speed(info, 0);
}
} else
-@@ -2414,60 +1880,14 @@
+@@ -2414,60 +1884,14 @@
return 0;
}
if (!CONFIGURED_SERIAL_PORT(info))
return;
save_flags(flags); cli();
-@@ -2478,121 +1898,6 @@
+@@ -2478,121 +1902,6 @@
serial_out(info, UART_LCR, info->LCR);
restore_flags(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
-@@ -2601,12 +1906,6 @@
+@@ -2601,12 +1910,6 @@
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct icount;
unsigned long flags;
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-@@ -2616,45 +1915,6 @@
+@@ -2616,45 +1919,6 @@
}
switch (cmd) {
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
-@@ -2667,9 +1927,6 @@
+@@ -2667,9 +1931,6 @@
case TIOCSSERIAL:
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
return get_lsr_info(info, (unsigned int *) arg);
-@@ -2679,15 +1936,6 @@
+@@ -2679,15 +1940,6 @@
return -EFAULT;
return 0;
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
-@@ -2754,6 +2002,39 @@
+@@ -2754,6 +2006,39 @@
printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
return 0;
default:
return -ENOIOCTLCMD;
}
-@@ -2801,18 +2082,6 @@
+@@ -2801,18 +2086,6 @@
tty->hw_stopped = 0;
rs_start(tty);
}
}
/*
-@@ -2831,9 +2100,6 @@
+@@ -2831,9 +2104,6 @@
struct serial_state *state;
unsigned long flags;
state = info->state;
save_flags(flags); cli();
-@@ -2933,10 +2199,7 @@
+@@ -2933,10 +2203,7 @@
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
unsigned long orig_jiffies, char_time;
if (info->state->type == PORT_UNKNOWN)
return;
-@@ -2974,9 +2237,11 @@
+@@ -2974,9 +2241,11 @@
printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
printk("jiff=%lu...", jiffies);
#endif
#endif
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(char_time);
-@@ -2986,8 +2251,9 @@
+@@ -2986,8 +2255,9 @@
break;
}
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
}
/*
-@@ -2998,9 +2264,6 @@
+@@ -2998,9 +2268,6 @@
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct serial_state *state = info->state;
state = info->state;
rs_flush_buffer(tty);
-@@ -3036,12 +2299,8 @@
+@@ -3036,12 +2303,8 @@
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
}
/*
-@@ -3114,14 +2373,10 @@
+@@ -3114,14 +2377,10 @@
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-@@ -3223,16 +2478,12 @@
+@@ -3223,16 +2482,12 @@
}
tty->driver_data = info;
info->tty = tty;
/*
* This relies on lock_kernel() stuff so wants tidying for 2.5
-@@ -3254,12 +2505,8 @@
+@@ -3254,12 +2509,8 @@
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
}
/*
-@@ -3313,17 +2560,14 @@
+@@ -3313,17 +2564,14 @@
int ret;
unsigned long flags;
/*
* Figure out the current RS-232 lines
-@@ -3334,7 +2578,6 @@
+@@ -3334,7 +2582,6 @@
info->magic = SERIAL_MAGIC;
info->port = state->port;
info->flags = state->flags;
info->io_type = state->io_type;
info->iomem_base = state->iomem_base;
info->iomem_reg_shift = state->iomem_reg_shift;
-@@ -3389,13 +2632,13 @@
+@@ -3389,13 +2636,13 @@
}
static int rs_read_proc(char *page, char **start, off_t off, int count,
for (i = 0; i < NR_PORTS && len < 4000; i++) {
l = line_info(page + len, &rs_table[i]);
len += l;
-@@ -3423,125 +2666,212 @@
+@@ -3423,125 +2670,212 @@
*/
/*
}
/*
-@@ -3894,1760 +3224,8 @@
+@@ -3894,1762 +3228,10 @@
restore_flags(flags);
}
- p = ioremap(pci_resource_start(dev, 0), 0x80);
- writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c);
- iounmap(p);
--
+
- if (!enable)
- pci_write_config_byte(dev, PCI_COMMAND,
- data & ~pci_config);
- return 0;
-}
--
--
--/*
+
+
+ /*
- * SIIG serial cards have an PCI interface chip which also controls
- * the UART clocking frequency. Each UART can be clocked independently
- * (except cards equiped with 4 UARTs) and initial clocking settings
-#endif
- { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */
- 0, 0, pci_xircom_fn },
-
+-
- { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */
- 0, 0, pci_siig10x_fn },
- { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */
- 0, 0, pci_siig20x_fn },
- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */
- 0, 0, pci_siig20x_fn },
-
+-
- { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */
- 0x40, 2, NULL, 0x200 },
- { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */
- rs_table[i].closing_wait = req->closing_wait;
- return(0);
-}
-
- /*
+-
+-/*
* register_serial and unregister_serial allows for 16x50 serial ports to be
-@@ -5734,11 +3312,8 @@
+ * configured at run-time, to support PCMCIA modems.
+ */
+@@ -5734,11 +3316,8 @@
}
restore_flags(flags);
state->iomem_base ? "iomem" : "port",
state->iomem_base ? (unsigned long)state->iomem_base :
state->port, state->irq, uart_config[state->type].name);
-@@ -5746,7 +3321,7 @@
+@@ -5746,7 +3325,7 @@
serial_driver.minor_start + state->line);
tty_register_devfs(&callout_driver, 0,
callout_driver.minor_start + state->line);
}
/**
-@@ -5785,7 +3360,6 @@
+@@ -5785,7 +3364,6 @@
int i;
struct async_struct *info;
del_timer_sync(&serial_timer);
save_flags(flags); cli();
remove_bh(SERIAL_BH);
-@@ -5803,41 +3377,31 @@
+@@ -5803,41 +3381,31 @@
kfree(info);
}
if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) {
}
module_init(rs_init);
-@@ -5946,7 +3510,7 @@
+@@ -5946,7 +3514,7 @@
static struct async_struct *info;
struct serial_state *state;
unsigned cval;
int bits = 8;
int parity = 'n';
int doflow = 0;
-@@ -5954,6 +3518,8 @@
+@@ -5954,6 +3522,8 @@
int quot = 0;
char *s;
if (options) {
baud = simple_strtoul(options, NULL, 10);
s = options;
-@@ -6028,19 +3594,12 @@
+@@ -6028,19 +3598,12 @@
info->state = state;
info->port = state->port;
info->flags = state->flags;
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
-@@ -6082,10 +3641,15 @@
+@@ -6082,10 +3645,15 @@
*/
void __init serial_console_init(void)
{
dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT
if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
---- linux-2.4.21/drivers/input/Makefile~ramses-keyb
+@@ -14,6 +16,7 @@
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
+ dep_tristate ' MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS
+
+ endmenu
+--- linux-2.4.21/drivers/input/Makefile~bluetooth
+++ linux-2.4.21/drivers/input/Makefile
@@ -8,7 +8,7 @@
# Object file lists.
-@@ -21,10 +21,12 @@
+@@ -21,10 +21,13 @@
obj-$(CONFIG_INPUT) += input.o
obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
++obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o
+obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o
# The global Rules.make.
+--- linux-2.4.21/drivers/input/keybdev.c~bluetooth
++++ linux-2.4.21/drivers/input/keybdev.c
+@@ -154,16 +154,18 @@
+
+ static struct input_handler keybdev_handler;
+
++static unsigned int ledstate = 0xff;
++
+ void keybdev_ledfunc(unsigned int led)
+ {
+ struct input_handle *handle;
+
+- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
++ ledstate = led;
+
++ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+-
+ }
+ }
+
+@@ -203,6 +205,12 @@
+ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+ kbd_refresh_leds();
+
++ if (ledstate != 0xff) {
++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02));
++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04));
++ }
++
+ return handle;
+ }
+
--- /dev/null
+++ linux-2.4.21/drivers/input/ramses_cellmap.h
@@ -0,0 +1,34 @@
+
+#endif
--- /dev/null
++++ linux-2.4.21/drivers/input/uinput.c
+@@ -0,0 +1,428 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/smp_lock.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/uinput.h>
++
++static int uinput_dev_open(struct input_dev *dev)
++{
++ return 0;
++}
++
++static void uinput_dev_close(struct input_dev *dev)
++{
++}
++
++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)dev->private;
++
++ udev->buff[udev->head].type = type;
++ udev->buff[udev->head].code = code;
++ udev->buff[udev->head].value = value;
++ do_gettimeofday(&udev->buff[udev->head].time);
++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
++
++ wake_up_interruptible(&udev->waitq);
++
++ return 0;
++}
++
++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
++{
++ return 0;
++}
++
++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
++{
++ return 0;
++}
++
++static int uinput_create_device(struct uinput_device *udev)
++{
++ if (!udev->dev->name) {
++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ udev->dev->open = uinput_dev_open;
++ udev->dev->close = uinput_dev_close;
++ udev->dev->event = uinput_dev_event;
++ udev->dev->upload_effect = uinput_dev_upload_effect;
++ udev->dev->erase_effect = uinput_dev_erase_effect;
++ udev->dev->private = udev;
++
++ init_waitqueue_head(&(udev->waitq));
++
++ input_register_device(udev->dev);
++
++ set_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_destroy_device(struct uinput_device *udev)
++{
++ if (!test_bit(UIST_CREATED, &(udev->state))) {
++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ input_unregister_device(udev->dev);
++
++ clear_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_open(struct inode *inode, struct file *file)
++{
++ struct uinput_device *newdev;
++ struct input_dev *newinput;
++
++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
++ if (!newdev)
++ goto error;
++ memset(newdev, 0, sizeof(struct uinput_device));
++
++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!newinput)
++ goto cleanup;
++ memset(newinput, 0, sizeof(struct input_dev));
++
++ newdev->dev = newinput;
++
++ file->private_data = newdev;
++
++ return 0;
++cleanup:
++ kfree(newdev);
++error:
++ return -ENOMEM;
++}
++
++static int uinput_validate_absbits(struct input_dev *dev)
++{
++ unsigned int cnt;
++ int retval = 0;
++
++ for (cnt = 0; cnt < ABS_MAX; cnt++) {
++ if (!test_bit(cnt, dev->absbit))
++ continue;
++
++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
++ (dev->absmax[cnt] <= dev->absmin[cnt])) {
++ printk(KERN_DEBUG
++ "%s: invalid abs[%02x] min:%d max:%d\n",
++ UINPUT_NAME, cnt,
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++
++ if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
++ (dev->absflat[cnt] > dev->absmax[cnt])) {
++ printk(KERN_DEBUG
++ "%s: absflat[%02x] out of range: %d "
++ "(min:%d/max:%d)\n",
++ UINPUT_NAME, cnt, dev->absflat[cnt],
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++ }
++ return retval;
++}
++
++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
++{
++ struct uinput_user_dev *user_dev;
++ struct input_dev *dev;
++ struct uinput_device *udev;
++ int size,
++ retval;
++
++ retval = count;
++
++ udev = (struct uinput_device *)file->private_data;
++ dev = udev->dev;
++
++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
++ if (!user_dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ if (NULL != dev->name)
++ kfree(dev->name);
++
++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
++ dev->name = kmalloc(size, GFP_KERNEL);
++ if (!dev->name) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ strncpy(dev->name, user_dev->name, size);
++ dev->idbus = user_dev->idbus;
++ dev->idvendor = user_dev->idvendor;
++ dev->idproduct = user_dev->idproduct;
++ dev->idversion = user_dev->idversion;
++ dev->ff_effects_max = user_dev->ff_effects_max;
++
++ size = sizeof(int) * (ABS_MAX + 1);
++ memcpy(dev->absmax, user_dev->absmax, size);
++ memcpy(dev->absmin, user_dev->absmin, size);
++ memcpy(dev->absfuzz, user_dev->absfuzz, size);
++ memcpy(dev->absflat, user_dev->absflat, size);
++
++ /* check if absmin/absmax/absfuzz/absflat are filled as
++ * told in Documentation/input/input-programming.txt */
++ if (test_bit(EV_ABS, dev->evbit)) {
++ retval = uinput_validate_absbits(dev);
++ if (retval < 0)
++ kfree(dev->name);
++ }
++
++exit:
++ kfree(user_dev);
++ return retval;
++}
++
++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++
++ if (test_bit(UIST_CREATED, &(udev->state))) {
++ struct input_event ev;
++
++ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
++ return -EFAULT;
++ input_event(udev->dev, ev.type, ev.code, ev.value);
++ }
++ else
++ count = uinput_alloc_device(file, buffer, count);
++
++ return count;
++}
++
++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++ int retval = 0;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
++ return -EAGAIN;
++
++ retval = wait_event_interruptible(udev->waitq,
++ (udev->head != udev->tail) ||
++ !test_bit(UIST_CREATED, &(udev->state)));
++
++ if (retval)
++ return retval;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ while ((udev->head != udev->tail) &&
++ (retval + sizeof(struct input_event) <= count)) {
++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
++ sizeof(struct input_event))) return -EFAULT;
++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
++ retval += sizeof(struct input_event);
++ }
++
++ return retval;
++}
++
++static unsigned int uinput_poll(struct file *file, poll_table *wait)
++{
++ struct uinput_device *udev = file->private_data;
++
++ poll_wait(file, &udev->waitq, wait);
++
++ if (udev->head != udev->tail)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++static int uinput_burn_device(struct uinput_device *udev)
++{
++ if (test_bit(UIST_CREATED, &(udev->state)))
++ uinput_destroy_device(udev);
++
++ kfree(udev->dev);
++ kfree(udev);
++
++ return 0;
++}
++
++static int uinput_close(struct inode *inode, struct file *file)
++{
++ return uinput_burn_device((struct uinput_device *)file->private_data);
++}
++
++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)file->private_data;
++
++ /* device attributes can not be changed after the device is created */
++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
++ return -EINVAL;
++
++ switch (cmd) {
++ case UI_DEV_CREATE:
++ retval = uinput_create_device(udev);
++ break;
++
++ case UI_DEV_DESTROY:
++ retval = uinput_destroy_device(udev);
++ break;
++
++ case UI_SET_EVBIT:
++ if (arg > EV_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->evbit);
++ break;
++
++ case UI_SET_KEYBIT:
++ if (arg > KEY_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->keybit);
++ break;
++
++ case UI_SET_RELBIT:
++ if (arg > REL_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->relbit);
++ break;
++
++ case UI_SET_ABSBIT:
++ if (arg > ABS_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->absbit);
++ break;
++
++ case UI_SET_MSCBIT:
++ if (arg > MSC_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->mscbit);
++ break;
++
++ case UI_SET_LEDBIT:
++ if (arg > LED_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ledbit);
++ break;
++
++ case UI_SET_SNDBIT:
++ if (arg > SND_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->sndbit);
++ break;
++
++ case UI_SET_FFBIT:
++ if (arg > FF_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ffbit);
++ break;
++
++ default:
++ retval = -EFAULT;
++ }
++ return retval;
++}
++
++struct file_operations uinput_fops = {
++ owner: THIS_MODULE,
++ open: uinput_open,
++ release: uinput_close,
++ read: uinput_read,
++ write: uinput_write,
++ poll: uinput_poll,
++ ioctl: uinput_ioctl,
++};
++
++static struct miscdevice uinput_misc = {
++ fops: &uinput_fops,
++ minor: UINPUT_MINOR,
++ name: UINPUT_NAME,
++};
++
++static int __init uinput_init(void)
++{
++ return misc_register(&uinput_misc);
++}
++
++static void __exit uinput_exit(void)
++{
++ misc_deregister(&uinput_misc);
++}
++
++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
++MODULE_DESCRIPTION("User level driver support for input subsystem");
++MODULE_LICENSE("GPL");
++
++module_init(uinput_init);
++module_exit(uinput_exit);
++
+--- /dev/null
+++ linux-2.4.21/drivers/input/wedge.c
@@ -0,0 +1,241 @@
+/*
+
+MODULE_DESCRIPTION("virtual keyboard wedge");
+MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/isdn/avmb1/capidrv.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/capidrv.c
+@@ -523,13 +523,25 @@
+
+ static void send_message(capidrv_contr * card, _cmsg * cmsg)
+ {
+- struct sk_buff *skb;
+- size_t len;
++ struct sk_buff *skb;
++ size_t len;
++ u16 err;
++
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
++ if(!skb) {
++ printk(KERN_ERR "no skb len(%d) memory\n", len);
++ return;
++ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+- (*capifuncs->capi_put_message) (global.appid, skb);
++ err = (*capifuncs->capi_put_message) (global.appid, skb);
++ if (err) {
++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
++ __FUNCTION__, err);
++ kfree_skb(skb);
++ return;
++ }
+ global.nsentctlpkt++;
+ }
+
+@@ -2188,10 +2200,10 @@
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+- if (card->plci_list)
+- printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
++ if (card->plci_list)
++ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ kfree(card->bchans);
+ card->bchans = 0;
+
+--- linux-2.4.21/drivers/isdn/avmb1/kcapi.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/kcapi.c
+@@ -546,7 +546,13 @@
+ static void notify_up(__u32 contr)
+ {
+ struct capi_interface_user *p;
++ __u16 appl;
+
++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
++ if (!VALID_APPLID(appl)) continue;
++ if (APPL(appl)->releasing) continue;
++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
++ }
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ spin_lock(&capi_users_lock);
+ for (p = capi_users; p; p = p->next) {
+@@ -714,12 +720,16 @@
+ nextpp = &(*pp)->next;
+ }
+ }
+- APPL(appl)->releasing--;
+- if (APPL(appl)->releasing <= 0) {
+- APPL(appl)->signal = 0;
+- APPL_MARK_FREE(appl);
+- printk(KERN_INFO "kcapi: appl %d down\n", appl);
+- }
++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */
++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
++ APPL(appl)->releasing--;
++ if (APPL(appl)->releasing <= 0) {
++ APPL(appl)->signal = 0;
++ APPL_MARK_FREE(appl);
++ printk(KERN_INFO "kcapi: appl %d down\n", appl);
++ }
++ } else
++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
+ }
+ /*
+ * ncci management
+@@ -872,16 +882,7 @@
+
+ static void controllercb_ready(struct capi_ctr * card)
+ {
+- __u16 appl;
+-
+ card->cardstate = CARD_RUNNING;
+-
+- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+- if (!VALID_APPLID(appl)) continue;
+- if (APPL(appl)->releasing) continue;
+- card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+- }
+-
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+
--- linux-2.4.21/drivers/misc/Makefile~wedge
+++ linux-2.4.21/drivers/misc/Makefile
@@ -12,7 +12,7 @@
/* pass ownership to the completion handler */
urb->complete (urb);
+--- linux-2.4.21/drivers/usb/hid-core.c~bluetooth
++++ linux-2.4.21/drivers/usb/hid-core.c
+@@ -211,6 +211,8 @@
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
++ if (usages < parser->global.report_count)
++ usages = parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
--- linux-2.4.21/drivers/usb/host/Config.in~usb-sl811
+++ linux-2.4.21/drivers/usb/host/Config.in
@@ -13,6 +13,9 @@
+ NEED_OP (t + 3 - 1);
+ goto copy_match;
+
-+ }
-+ else if (t >= 32)
++ }
++ else if (t >= 32)
++ {
++ t &= 31;
++ if (t == 0)
++ {
++ NEED_IP (1);
++ while (*ip == 0)
++ {
++ t += 255;
++ ip++;
++ NEED_IP (1);
++ }
++ t += 31 + *ip++;
++ }
++
++ m_pos = op - 1;
++ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++ ip += 2;
++ }
++ else if (t >= 16)
++ {
++ m_pos = op;
++ m_pos -= (t & 8) << 11;
++
++ t &= 7;
++ if (t == 0)
++ {
++ NEED_IP (1);
++ while (*ip == 0)
++ {
++ t += 255;
++ ip++;
++ NEED_IP (1);
++ }
++ t += 7 + *ip++;
++ }
++
++ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++ ip += 2;
++ if (m_pos == op)
++ goto eof_found;
++ m_pos -= 0x4000;
++ }
++ else
++ {
++
++ m_pos = op - 1;
++ m_pos -= t >> 2;
++ m_pos -= *ip++ << 2;
++ TEST_LOOKBEHIND (m_pos, out);
++ NEED_OP (2);
++ *op++ = *m_pos++;
++ *op++ = *m_pos;
++
++ goto match_done;
++ }
++
++ TEST_LOOKBEHIND (m_pos, out);
++ NEED_OP (t + 3 - 1);
++ if (t >= 2 * 4 - (3 - 1)
++ && PTR_ALIGNED2_4 (op, m_pos))
++ {
++ COPY4 (op, m_pos);
++ op += 4;
++ m_pos += 4;
++ t -= 4 - (3 - 1);
++ do
++ {
++ COPY4 (op, m_pos);
++ op += 4;
++ m_pos += 4;
++ t -= 4;
++ }
++ while (t >= 4);
++ if (t > 0)
++ do
++ *op++ = *m_pos++;
++ while (--t > 0);
++ }
++ else
++
++ {
++ copy_match:
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ do
++ *op++ = *m_pos++;
++ while (--t > 0);
++ }
++
++ match_done:
++ t = ip[-2] & 3;
++
++ if (t == 0)
++ break;
++
++ match_next:
++ NEED_OP (t);
++ NEED_IP (t + 1);
++ do
++ *op++ = *ip++;
++ while (--t > 0);
++ t = *ip++;
++ }
++ }
++ *out_len = op - out;
++ return LZO_E_EOF_NOT_FOUND;
++
++ eof_found:
++ *out_len = op - out;
++ return (ip == ip_end ? LZO_E_OK :
++ (ip <
++ ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++
++ input_overrun:
++ *out_len = op - out;
++ return LZO_E_INPUT_OVERRUN;
++
++ output_overrun:
++ *out_len = op - out;
++ return LZO_E_OUTPUT_OVERRUN;
++
++ lookbehind_overrun:
++ *out_len = op - out;
++ return LZO_E_LOOKBEHIND_OVERRUN;
++}
++
++/* lzo1x_oo.ch */
++
++#define NO_LIT LZO_UINT_MAX
++
++static void
++copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++ ip[0] = m_pos[0];
++ if (off == 1)
++ ip[1] = m_pos[0];
++ else
++ ip[1] = m_pos[1];
++}
++
++static void
++copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++ ip[0] = m_pos[0];
++ if (off == 1)
++ {
++ ip[2] = ip[1] = m_pos[0];
++ }
++ else if (off == 2)
++ {
++ ip[1] = m_pos[1];
++ ip[2] = m_pos[0];
++ }
++ else
++ {
++ ip[1] = m_pos[1];
++ ip[2] = m_pos[2];
++ }
++}
++
++static int
++lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
++ lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++ register lzo_byte *op;
++ register lzo_byte *ip;
++ register lzo_uint t;
++ register lzo_byte *m_pos;
++ lzo_uint nl;
++ const lzo_byte *const ip_end = in + in_len;
++ const lzo_byte *const op_end = out + *out_len;
++ lzo_byte *litp = NULL;
++ lzo_uint lit = 0;
++ lzo_uint next_lit = NO_LIT;
++ long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
++
++ *out_len = 0;
++
++ op = out;
++ ip = in;
++
++ if (*ip > 17)
++ {
++ t = *ip++ - 17;
++ if (t < 4)
++ goto match_next;
++ goto first_literal_run;
++ }
++
++ while (TEST_IP && TEST_OP)
++ {
++ t = *ip++;
++ if (t >= 16)
++ goto match;
++ litp = ip - 1;
++ if (t == 0)
++ {
++ t = 15;
++ while (*ip == 0)
++ t += 255, ip++;
++ t += *ip++;
++ }
++ lit = t + 3;
++ copy_literal_run:
++ *op++ = *ip++;
++ *op++ = *ip++;
++ *op++ = *ip++;
++ first_literal_run:
++ do
++ *op++ = *ip++;
++ while (--t > 0);
++
++ t = *ip++;
++
++ if (t >= 16)
++ goto match;
++ m_pos = op - 1 - 0x800;
++ m_pos -= t >> 2;
++ m_pos -= *ip++ << 2;
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ lit = 0;
++ goto match_done;
++
++ while (TEST_IP && TEST_OP)
++ {
++ if (t < 16)
+ {
-+ t &= 31;
-+ if (t == 0)
-+ {
-+ NEED_IP (1);
-+ while (*ip == 0)
-+ {
-+ t += 255;
-+ ip++;
-+ NEED_IP (1);
-+ }
-+ t += 31 + *ip++;
-+ }
-+
+ m_pos = op - 1;
-+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++ m_pos -= t >> 2;
++ m_pos -= *ip++ << 2;
+
-+ ip += 2;
-+ }
-+ else if (t >= 16)
-+ {
-+ m_pos = op;
-+ m_pos -= (t & 8) << 11;
++ if (litp == NULL)
++ goto copy_m1;
+
-+ t &= 7;
-+ if (t == 0)
++ nl = ip[-2] & 3;
++ if (nl == 0 && lit == 1 && ip[0] >= 16)
+ {
-+ NEED_IP (1);
-+ while (*ip == 0)
-+ {
-+ t += 255;
-+ ip++;
-+ NEED_IP (1);
-+ }
-+ t += 7 + *ip++;
++ next_lit = nl;
++ lit += 2;
++ *litp = LZO_BYTE ((*litp & ~3) | lit);
++ copy2 (ip - 2, m_pos, op - m_pos);
++ o_m1_a++;
+ }
++ else if (nl == 0 && ip[0] < 16 && ip[0] != 0
++ && (lit + 2 + ip[0] < 16))
++ {
++ t = *ip++;
++ *litp &= ~3;
++ copy2 (ip - 3 + 1, m_pos, op - m_pos);
++ litp += 2;
++ if (lit > 0)
++ memmove (litp + 1, litp, lit);
++ lit += 2 + t + 3;
++ *litp = LZO_BYTE (lit - 3);
+
-+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
-+
-+ ip += 2;
-+ if (m_pos == op)
-+ goto eof_found;
-+ m_pos -= 0x4000;
++ o_m1_b++;
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ goto copy_literal_run;
++ }
++ copy_m1:
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
+ }
+ else
+ {
++ match:
++ if (t >= 64)
++ {
++ m_pos = op - 1;
++ m_pos -= (t >> 2) & 7;
++ m_pos -= *ip++ << 3;
++ t = (t >> 5) - 1;
++ if (litp == NULL)
++ goto copy_m;
+
-+ m_pos = op - 1;
-+ m_pos -= t >> 2;
-+ m_pos -= *ip++ << 2;
-+ TEST_LOOKBEHIND (m_pos, out);
-+ NEED_OP (2);
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos;
++ nl = ip[-2] & 3;
++ if (t == 1 && lit > 3 && nl == 0 &&
++ ip[0] < 16 && ip[0] != 0
++ && (lit + 3 + ip[0] < 16))
++ {
++ t = *ip++;
++ copy3 (ip - 1 - 2, m_pos,
++ op - m_pos);
++ lit += 3 + t + 3;
++ *litp = LZO_BYTE (lit - 3);
++ o_m2++;
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ goto copy_literal_run;
++ }
++ }
++ else
++ {
++ if (t >= 32)
++ {
++ t &= 31;
++ if (t == 0)
++ {
++ t = 31;
++ while (*ip == 0)
++ t += 255,
++ ip++;
++ t += *ip++;
++ }
++ m_pos = op - 1;
++ m_pos -= *ip++ >> 2;
++ m_pos -= *ip++ << 6;
++ }
++ else
++ {
++ m_pos = op;
++ m_pos -= (t & 8) << 11;
++ t &= 7;
++ if (t == 0)
++ {
++ t = 7;
++ while (*ip == 0)
++ t += 255,
++ ip++;
++ t += *ip++;
++ }
++ m_pos -= *ip++ >> 2;
++ m_pos -= *ip++ << 6;
++ if (m_pos == op)
++ goto eof_found;
++ m_pos -= 0x4000;
++ }
++ if (litp == NULL)
++ goto copy_m;
+
-+ goto match_done;
-+ }
++ nl = ip[-2] & 3;
++ if (t == 1 && lit == 0 && nl == 0
++ && ip[0] >= 16)
++ {
++ next_lit = nl;
++ lit += 3;
++ *litp = LZO_BYTE ((*litp & ~3)
++ | lit);
++ copy3 (ip - 3, m_pos,
++ op - m_pos);
++ o_m3_a++;
++ }
++ else if (t == 1 && lit <= 3 && nl == 0
++ && ip[0] < 16 && ip[0] != 0
++ && (lit + 3 + ip[0] < 16))
++ {
++ t = *ip++;
++ *litp &= ~3;
++ copy3 (ip - 4 + 1, m_pos,
++ op - m_pos);
++ litp += 2;
++ if (lit > 0)
++ memmove (litp + 1,
++ litp, lit);
++ lit += 3 + t + 3;
++ *litp = LZO_BYTE (lit - 3);
+
-+ TEST_LOOKBEHIND (m_pos, out);
-+ NEED_OP (t + 3 - 1);
-+ if (t >= 2 * 4 - (3 - 1)
-+ && PTR_ALIGNED2_4 (op, m_pos))
-+ {
-+ COPY4 (op, m_pos);
-+ op += 4;
-+ m_pos += 4;
-+ t -= 4 - (3 - 1);
-+ do
-+ {
-+ COPY4 (op, m_pos);
-+ op += 4;
-+ m_pos += 4;
-+ t -= 4;
-+ }
-+ while (t >= 4);
-+ if (t > 0)
-+ do
++ o_m3_b++;
+ *op++ = *m_pos++;
-+ while (--t > 0);
-+ }
-+ else
-+
-+ {
-+ copy_match:
++ *op++ = *m_pos++;
++ *op++ = *m_pos++;
++ goto copy_literal_run;
++ }
++ }
++ copy_m:
+ *op++ = *m_pos++;
+ *op++ = *m_pos++;
+ do
+ }
+
+ match_done:
-+ t = ip[-2] & 3;
-+
++ if (next_lit == NO_LIT)
++ {
++ t = ip[-2] & 3;
++ lit = t;
++ litp = ip - 2;
++ }
++ else
++ t = next_lit;
++ next_lit = NO_LIT;
+ if (t == 0)
+ break;
-+
+ match_next:
-+ NEED_OP (t);
-+ NEED_IP (t + 1);
+ do
+ *op++ = *ip++;
+ while (--t > 0);
+ t = *ip++;
+ }
+ }
++
+ *out_len = op - out;
+ return LZO_E_EOF_NOT_FOUND;
+
+ return (ip == ip_end ? LZO_E_OK :
+ (ip <
+ ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++}
+
-+ input_overrun:
-+ *out_len = op - out;
-+ return LZO_E_INPUT_OVERRUN;
++/* interface to jffs2 follows */
+
-+ output_overrun:
-+ *out_len = op - out;
-+ return LZO_E_OUTPUT_OVERRUN;
++#include "compr.h"
++#include <linux/jffs2.h>
+
-+ lookbehind_overrun:
-+ *out_len = op - out;
-+ return LZO_E_LOOKBEHIND_OVERRUN;
++/*#define BLOCKSIZE JFFS2_PAGE_SIZE
++#define OUTBLOCKSIZE (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
++
++int jffs2_lzo_compress (unsigned char *input,
++ unsigned char *output, uint32_t *sourcelen,
++ uint32_t *dstlen, void *model);
++
++int jffs2_lzo_decompress (unsigned char *input,
++ unsigned char *output, uint32_t sourcelen,
++ uint32_t dstlen, void *model);
++
++static struct jffs2_compressor jffs2_lzo_comp = {
++ .priority = JFFS2_LZO_PRIORITY,
++ .name = "lzo",
++ .compr = JFFS2_COMPR_LZO,
++ .compress = &jffs2_lzo_compress,
++ .decompress = &jffs2_lzo_decompress,
++#ifdef JFFS2_LZO_DISABLED
++ .disabled = 1,
++#else
++ .disabled = 0,
++#endif
++};
++
++#ifdef JFFS2_LZO_1
++static int
++no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
++ lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
++{
++ return 0;
++}
++#endif
++
++static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
++static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
++#ifdef JFFS2_LZO_1
++static int lzo1x_compressor_type = 999;
++static int lzo1x_optimize_type = 1;
++#endif
++static unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
++
++static lzo_bytep wrkmem = NULL; /* temporary buffer for compression, used by lzo */
++static lzo_bytep cmprssmem = NULL; /* temporary buffer for compression, used by interface */
++static int cmprssmem_size = 0;
++
++static int prepare_out_buf(uint32_t ssize, uint32_t dsize)
++{
++ uint32_t msize,req;
++
++ msize = (ssize>dsize)? ssize : dsize;
++ req = (msize<<1) + 20;
++ if ((!cmprssmem)||(cmprssmem_size<req)) {
++ if (!cmprssmem) {
++ vfree(cmprssmem);
++ cmprssmem = NULL;
++ cmprssmem_size = 0;
++ }
++ cmprssmem = vmalloc(req);
++ if (!cmprssmem) {
++ return -1;
++ }
++ cmprssmem_size = req;
++ }
++ return 0;
++}
++
++int jffs2_lzo_compress (unsigned char *input,
++ unsigned char *output, uint32_t *sourcelen,
++ uint32_t *dstlen, void *model)
++{
++ lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
++ lzo_uint isize = *sourcelen;
++ int retval;
++
++ if (prepare_out_buf(*sourcelen,*dstlen)) {
++ return -1;
++ }
++ if ((retval =
++ lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
++ wrkmem)) != LZO_E_OK)
++ {
++ return retval;
++ }
++ else
++ {
++ retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
++ NULL);
++ if (csize <= *dstlen) {
++ *dstlen = csize;
++ memcpy (output, cmprssmem, csize);
++ return retval;
++ } else {
++ return -1;
++ }
++ }
++}
++
++int jffs2_lzo_decompress (unsigned char *input,
++ unsigned char *output, uint32_t sourcelen,
++ uint32_t dstlen, void *model)
++{
++ lzo_uint outlen = dstlen;
++ return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
++}
++
++int jffs2_lzo_init (void)
++{
++ wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
++ if (!wrkmem) return -1;
++ jffs2_register_compressor(&jffs2_lzo_comp);
++ return 0;
++}
++
++void jffs2_lzo_exit (void)
++{
++ jffs2_unregister_compressor (&jffs2_lzo_comp);
++ if (cmprssmem) vfree(cmprssmem);
++ vfree(wrkmem);
++}
+--- linux-2.4.21/fs/jffs2/compr_rtime.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rtime.c
+@@ -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 <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ *
+ * 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
+ *
+@@ -49,12 +25,14 @@
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
++#include <linux/jffs2.h>
++#include "compr.h"
+
+ /* _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, void *model)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+@@ -91,10 +69,10 @@
+ }
+
+
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 srclen, __u32 destlen)
++int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen, void *model)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+@@ -123,6 +101,28 @@
+ }
+ }
+ }
++ return 0;
+ }
+
++static struct jffs2_compressor jffs2_rtime_comp = {
++ .priority = JFFS2_RTIME_PRIORITY,
++ .name = "rtime",
++ .compr = JFFS2_COMPR_RTIME,
++ .compress = &jffs2_rtime_compress,
++ .decompress = &jffs2_rtime_decompress,
++#ifdef JFFS2_RTIME_DISABLED
++ .disabled = 1,
++#else
++ .disabled = 0,
++#endif
++};
++
++int jffs2_rtime_init(void)
++{
++ return jffs2_register_compressor(&jffs2_rtime_comp);
++}
+
++void jffs2_rtime_exit(void)
++{
++ jffs2_unregister_compressor(&jffs2_rtime_comp);
++}
+--- linux-2.4.21/fs/jffs2/compr_rubin.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.c
+@@ -1,49 +1,25 @@
+ /*
+ * 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 <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+
+ #include <linux/string.h>
+ #include <linux/types.h>
++#include <linux/jffs2.h>
+ #include "compr_rubin.h"
+ #include "histo_mips.h"
++#include "compr.h"
+
+-
+-
+-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, void *model)
+ {
+ 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, void *model)
+ {
+ 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,15 @@
+ }
+
+
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+ rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
++ return 0;
+ }
+
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+ int bits[8];
+ int c;
+@@ -346,4 +323,51 @@
+ bits[c] = data_in[c];
+
+ rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
++ return 0;
+}
+
-+/* lzo1x_oo.ch */
-+
-+#define NO_LIT LZO_UINT_MAX
++static struct jffs2_compressor jffs2_rubinmips_comp = {
++ .priority = JFFS2_RUBINMIPS_PRIORITY,
++ .name = "rubinmips",
++ .compr = JFFS2_COMPR_DYNRUBIN,
++ .compress = NULL, /*&jffs2_rubinmips_compress,*/
++ .decompress = &jffs2_rubinmips_decompress,
++#ifdef JFFS2_RUBINMIPS_DISABLED
++ .disabled = 1,
++#else
++ .disabled = 0,
++#endif
++};
+
-+static void
-+copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++int jffs2_rubinmips_init(void)
+{
-+ ip[0] = m_pos[0];
-+ if (off == 1)
-+ ip[1] = m_pos[0];
-+ else
-+ ip[1] = m_pos[1];
++ return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
-+static void
-+copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++void jffs2_rubinmips_exit(void)
+{
-+ ip[0] = m_pos[0];
-+ if (off == 1)
-+ {
-+ ip[2] = ip[1] = m_pos[0];
-+ }
-+ else if (off == 2)
-+ {
-+ ip[1] = m_pos[1];
-+ ip[2] = m_pos[0];
-+ }
-+ else
-+ {
-+ ip[1] = m_pos[1];
-+ ip[2] = m_pos[2];
-+ }
++ jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
-+static int
-+lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
-+ lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
-+{
-+ register lzo_byte *op;
-+ register lzo_byte *ip;
-+ register lzo_uint t;
-+ register lzo_byte *m_pos;
-+ lzo_uint nl;
-+ const lzo_byte *const ip_end = in + in_len;
-+ const lzo_byte *const op_end = out + *out_len;
-+ lzo_byte *litp = NULL;
-+ lzo_uint lit = 0;
-+ lzo_uint next_lit = NO_LIT;
-+ long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
-+
-+ *out_len = 0;
-+
-+ op = out;
-+ ip = in;
-+
-+ if (*ip > 17)
-+ {
-+ t = *ip++ - 17;
-+ if (t < 4)
-+ goto match_next;
-+ goto first_literal_run;
-+ }
-+
-+ while (TEST_IP && TEST_OP)
-+ {
-+ t = *ip++;
-+ if (t >= 16)
-+ goto match;
-+ litp = ip - 1;
-+ if (t == 0)
-+ {
-+ t = 15;
-+ while (*ip == 0)
-+ t += 255, ip++;
-+ t += *ip++;
-+ }
-+ lit = t + 3;
-+ copy_literal_run:
-+ *op++ = *ip++;
-+ *op++ = *ip++;
-+ *op++ = *ip++;
-+ first_literal_run:
-+ do
-+ *op++ = *ip++;
-+ while (--t > 0);
-+
-+ t = *ip++;
-+
-+ if (t >= 16)
-+ goto match;
-+ m_pos = op - 1 - 0x800;
-+ m_pos -= t >> 2;
-+ m_pos -= *ip++ << 2;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ lit = 0;
-+ goto match_done;
-+
-+ while (TEST_IP && TEST_OP)
-+ {
-+ if (t < 16)
-+ {
-+ m_pos = op - 1;
-+ m_pos -= t >> 2;
-+ m_pos -= *ip++ << 2;
-+
-+ if (litp == NULL)
-+ goto copy_m1;
++static struct jffs2_compressor jffs2_dynrubin_comp = {
++ .priority = JFFS2_DYNRUBIN_PRIORITY,
++ .name = "dynrubin",
++ .compr = JFFS2_COMPR_RUBINMIPS,
++ .compress = jffs2_dynrubin_compress,
++ .decompress = &jffs2_dynrubin_decompress,
++#ifdef JFFS2_DYNRUBIN_DISABLED
++ .disabled = 1,
++#else
++ .disabled = 0,
++#endif
++};
+
-+ nl = ip[-2] & 3;
-+ if (nl == 0 && lit == 1 && ip[0] >= 16)
-+ {
-+ next_lit = nl;
-+ lit += 2;
-+ *litp = LZO_BYTE ((*litp & ~3) | lit);
-+ copy2 (ip - 2, m_pos, op - m_pos);
-+ o_m1_a++;
-+ }
-+ else if (nl == 0 && ip[0] < 16 && ip[0] != 0
-+ && (lit + 2 + ip[0] < 16))
-+ {
-+ t = *ip++;
-+ *litp &= ~3;
-+ copy2 (ip - 3 + 1, m_pos, op - m_pos);
-+ litp += 2;
-+ if (lit > 0)
-+ memmove (litp + 1, litp, lit);
-+ lit += 2 + t + 3;
-+ *litp = LZO_BYTE (lit - 3);
++int jffs2_dynrubin_init(void)
++{
++ return jffs2_register_compressor(&jffs2_dynrubin_comp);
++}
+
-+ o_m1_b++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ goto copy_literal_run;
-+ }
-+ copy_m1:
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ }
-+ else
-+ {
-+ match:
-+ if (t >= 64)
-+ {
-+ m_pos = op - 1;
-+ m_pos -= (t >> 2) & 7;
-+ m_pos -= *ip++ << 3;
-+ t = (t >> 5) - 1;
-+ if (litp == NULL)
-+ goto copy_m;
++void jffs2_dynrubin_exit(void)
++{
++ jffs2_unregister_compressor(&jffs2_dynrubin_comp);
+ }
+--- linux-2.4.21/fs/jffs2/compr_rubin.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.h
+@@ -1,7 +1,7 @@
+ /* Rubin encoder/decoder header */
+ /* work started at : aug 3, 1994 */
+ /* last modification : aug 15, 1994 */
+-/* $Id$ */
++/* $Id$ */
+
+ #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);
+--- linux-2.4.21/fs/jffs2/compr_zlib.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_zlib.c
+@@ -1,51 +1,28 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+-#ifndef __KERNEL__
++#if !defined(__KERNEL__) && !defined(__ECOS)
+ #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+ #endif
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/zlib.h>
++#include <linux/zutil.h>
++#include <asm/semaphore.h>
+ #include "nodelist.h"
++#include "compr.h"
+
+ /* Plan: call deflate() with avail_in == *sourcelen,
+ avail_out = *dstlen - 12 and flush == Z_FINISH.
+@@ -58,120 +35,184 @@
+
+ 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;
+
+-int __init jffs2_zlib_init(void)
++#ifdef __KERNEL__ /* Linux-only */
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+
-+ nl = ip[-2] & 3;
-+ if (t == 1 && lit > 3 && nl == 0 &&
-+ ip[0] < 16 && ip[0] != 0
-+ && (lit + 3 + ip[0] < 16))
-+ {
-+ t = *ip++;
-+ copy3 (ip - 1 - 2, m_pos,
-+ op - m_pos);
-+ lit += 3 + t + 3;
-+ *litp = LZO_BYTE (lit - 3);
-+ o_m2++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ goto copy_literal_run;
-+ }
-+ }
-+ else
-+ {
-+ if (t >= 32)
-+ {
-+ t &= 31;
-+ if (t == 0)
-+ {
-+ t = 31;
-+ while (*ip == 0)
-+ t += 255,
-+ ip++;
-+ t += *ip++;
-+ }
-+ m_pos = op - 1;
-+ m_pos -= *ip++ >> 2;
-+ m_pos -= *ip++ << 6;
-+ }
-+ else
-+ {
-+ m_pos = op;
-+ m_pos -= (t & 8) << 11;
-+ t &= 7;
-+ if (t == 0)
-+ {
-+ t = 7;
-+ while (*ip == 0)
-+ t += 255,
-+ ip++;
-+ t += *ip++;
-+ }
-+ m_pos -= *ip++ >> 2;
-+ m_pos -= *ip++ << 6;
-+ if (m_pos == op)
-+ goto eof_found;
-+ m_pos -= 0x4000;
-+ }
-+ if (litp == NULL)
-+ goto copy_m;
++static int __init alloc_workspaces(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()));
+ return 0;
+ }
+
+-void jffs2_zlib_exit(void)
++static void free_workspaces(void)
+ {
+- vfree(deflate_workspace);
+- vfree(inflate_workspace);
++ vfree(def_strm.workspace);
++ vfree(inf_strm.workspace);
+ }
++#else
++#define alloc_workspaces() (0)
++#define free_workspaces() do { } while(0)
++#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, void *model)
+ {
+- 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);
+
-+ nl = ip[-2] & 3;
-+ if (t == 1 && lit == 0 && nl == 0
-+ && ip[0] >= 16)
-+ {
-+ next_lit = nl;
-+ lit += 3;
-+ *litp = LZO_BYTE ((*litp & ~3)
-+ | lit);
-+ copy3 (ip - 3, m_pos,
-+ op - m_pos);
-+ o_m3_a++;
-+ }
-+ else if (t == 1 && lit <= 3 && nl == 0
-+ && ip[0] < 16 && ip[0] != 0
-+ && (lit + 3 + ip[0] < 16))
-+ {
-+ t = *ip++;
-+ *litp &= ~3;
-+ copy3 (ip - 4 + 1, m_pos,
-+ op - m_pos);
-+ litp += 2;
-+ if (lit > 0)
-+ memmove (litp + 1,
-+ litp, lit);
-+ lit += 3 + t + 3;
-+ *litp = LZO_BYTE (lit - 3);
+ 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)
++int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen, void *model)
+ {
+- 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;
+
-+ o_m3_b++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ goto copy_literal_run;
-+ }
-+ }
-+ copy_m:
-+ *op++ = *m_pos++;
-+ *op++ = *m_pos++;
-+ do
-+ *op++ = *m_pos++;
-+ while (--t > 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)) {
+
-+ match_done:
-+ if (next_lit == NO_LIT)
-+ {
-+ t = ip[-2] & 3;
-+ lit = t;
-+ litp = ip - 2;
-+ }
-+ else
-+ t = next_lit;
-+ next_lit = NO_LIT;
-+ if (t == 0)
-+ break;
-+ match_next:
-+ do
-+ *op++ = *ip++;
-+ while (--t > 0);
-+ t = *ip++;
-+ }
++ 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"));
+ }
+
-+ *out_len = op - out;
-+ return LZO_E_EOF_NOT_FOUND;
+
-+ eof_found:
-+ *out_len = op - out;
-+ return (ip == ip_end ? LZO_E_OK :
-+ (ip <
-+ ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+ printk(KERN_WARNING "inflateInit failed\n");
+ up(&inflate_sem);
+- return;
++ return 1;
+ }
+- 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);
++ return 0;
+}
+
-+/* interface to jffs2 follows */
-+
-+#include "compr.h"
-+#include <linux/jffs2.h>
-+
-+/*#define BLOCKSIZE JFFS2_PAGE_SIZE
-+#define OUTBLOCKSIZE (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
-+
-+int jffs2_lzo_compress (unsigned char *input,
-+ unsigned char *output, uint32_t *sourcelen,
-+ uint32_t *dstlen, void *model);
-+
-+int jffs2_lzo_decompress (unsigned char *input,
-+ unsigned char *output, uint32_t sourcelen,
-+ uint32_t dstlen, void *model);
-+
-+static struct jffs2_compressor jffs2_lzo_comp = {
-+ .priority = JFFS2_LZO_PRIORITY,
-+ .name = "lzo",
-+ .compr = JFFS2_COMPR_LZO,
-+ .compress = &jffs2_lzo_compress,
-+ .decompress = &jffs2_lzo_decompress,
-+#ifdef JFFS2_LZO_DISABLED
-+ .disabled = 1,
++static struct jffs2_compressor jffs2_zlib_comp = {
++ .priority = JFFS2_ZLIB_PRIORITY,
++ .name = "zlib",
++ .compr = JFFS2_COMPR_ZLIB,
++ .compress = &jffs2_zlib_compress,
++ .decompress = &jffs2_zlib_decompress,
++#ifdef JFFS2_ZLIB_DISABLED
++ .disabled = 1,
+#else
-+ .disabled = 0,
++ .disabled = 0,
+#endif
+};
+
-+#ifdef JFFS2_LZO_1
-+static int
-+no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
-+ lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
++int __init jffs2_zlib_init(void)
+{
-+ return 0;
++ int ret;
++
++ ret = alloc_workspaces();
++ if (ret)
++ return ret;
++
++ ret = jffs2_register_compressor(&jffs2_zlib_comp);
++ if (ret)
++ free_workspaces();
++
++ return ret;
+}
-+#endif
+
-+static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
-+static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
-+#ifdef JFFS2_LZO_1
-+static int lzo1x_compressor_type = 999;
-+static int lzo1x_optimize_type = 1;
++void jffs2_zlib_exit(void)
++{
++ jffs2_unregister_compressor(&jffs2_zlib_comp);
++ free_workspaces();
+ }
+--- linux-2.4.21/fs/jffs2/crc32.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.c
+@@ -37,11 +37,11 @@
+ * polynomial $edb88320
+ */
+
+-/* $Id$ */
++/* $Id$ */
+
+ #include "crc32.h"
+
+-const __u32 crc32_table[256] = {
++const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+--- linux-2.4.21/fs/jffs2/crc32.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.h
+@@ -1,16 +1,16 @@
+ #ifndef CRC32_H
+ #define CRC32_H
+
+-/* $Id$ */
++/* $Id$ */
+
+ #include <linux/types.h>
+
+-extern const __u32 crc32_table[256];
++extern const uint32_t crc32_table[256];
+
+ /* Return a 32-bit CRC of the contents of the buffer. */
+
+-static inline __u32
+-crc32(__u32 val, const void *ss, int len)
++static inline uint32_t
++crc32(uint32_t val, const void *ss, int len)
+ {
+ const unsigned char *s = ss;
+ while (--len >= 0)
+--- linux-2.4.21/fs/jffs2/dir.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/dir.c
+@@ -1,84 +1,73 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+-#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/time.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++
++/* Urgh. Please tell me there's a nicer way of doing these. */
++#include <linux/version.h>
++#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 unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
+
+ 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 lzo_bytep wrkmem = NULL; /* temporary buffer for compression, used by lzo */
-+static lzo_bytep cmprssmem = NULL; /* temporary buffer for compression, used by interface */
-+static int cmprssmem_size = 0;
++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,21 @@
+ 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 +235,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 prepare_out_buf(uint32_t ssize, uint32_t dsize)
-+{
-+ uint32_t msize,req;
+
+ 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;
+
-+ msize = (ssize>dsize)? ssize : dsize;
-+ req = (msize<<1) + 20;
-+ if ((!cmprssmem)||(cmprssmem_size<req)) {
-+ if (!cmprssmem) {
-+ vfree(cmprssmem);
-+ cmprssmem = NULL;
-+ cmprssmem_size = 0;
-+ }
-+ cmprssmem = vmalloc(req);
-+ if (!cmprssmem) {
-+ return -1;
-+ }
-+ cmprssmem_size = req;
-+ }
-+ return 0;
-+}
++ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
+
-+int jffs2_lzo_compress (unsigned char *input,
-+ unsigned char *output, uint32_t *sourcelen,
-+ uint32_t *dstlen, void *model)
-+{
-+ lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
-+ lzo_uint isize = *sourcelen;
-+ int retval;
+ 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,13 +295,12 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
+- int ret;
++ uint32_t alloclen, phys_ofs;
++ int ret, targetlen = strlen(target);
+
+ /* FIXME: If you care. We'd need to use frags for the target
+ if it grows much more than this */
+- if (strlen(target) > 254)
++ if (targetlen > 254)
+ return -EINVAL;
+
+ ri = jffs2_alloc_raw_inode();
+@@ -537,7 +314,7 @@
+ * Just the node will do for now, though
+ */
+ namelen = dentry->d_name.len;
+- ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
++ ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+@@ -556,15 +333,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 = targetlen;
++ 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, targetlen));
++ 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, targetlen, phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -575,19 +353,26 @@
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fn);
+ }
+
-+ if (prepare_out_buf(*sourcelen,*dstlen)) {
-+ return -1;
-+ }
-+ if ((retval =
-+ lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
-+ wrkmem)) != LZO_E_OK)
-+ {
-+ return retval;
-+ }
-+ else
-+ {
-+ retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
-+ NULL);
-+ if (csize <= *dstlen) {
-+ *dstlen = csize;
-+ memcpy (output, cmprssmem, csize);
-+ return retval;
-+ } else {
-+ return -1;
-+ }
++ /* We use f->dents field to store the target path. */
++ f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
++ if (!f->dents) {
++ printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ jffs2_clear_inode(inode);
++ return -ENOMEM;
+ }
-+}
-+
-+int jffs2_lzo_decompress (unsigned char *input,
-+ unsigned char *output, uint32_t sourcelen,
-+ uint32_t dstlen, void *model)
-+{
-+ lzo_uint outlen = dstlen;
-+ return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
-+}
+
-+int jffs2_lzo_init (void)
-+{
-+ wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
-+ if (!wrkmem) return -1;
-+ jffs2_register_compressor(&jffs2_lzo_comp);
-+ return 0;
-+}
++ memcpy(f->dents, target, targetlen + 1);
++ D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
+
-+void jffs2_lzo_exit (void)
-+{
-+ jffs2_unregister_compressor (&jffs2_lzo_comp);
-+ if (cmprssmem) vfree(cmprssmem);
-+ vfree(wrkmem);
-+}
---- linux-2.4.21/fs/jffs2/compr_rtime.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rtime.c
-@@ -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 <arjanv@redhat.com>
- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- *
- * 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
- *
-@@ -49,12 +25,14 @@
- #include <linux/types.h>
- #include <linux/errno.h>
- #include <linux/string.h>
-+#include <linux/jffs2.h>
-+#include "compr.h"
+ /* No data here. Only a metadata node, which will be
+ obsoleted by the first data write
+ */
+ 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 +380,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -608,41 +392,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->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 = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ 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);
+-
+- 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);
+ }
- /* _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, void *model)
- {
-- int positions[256];
-+ short positions[256];
- int outpos = 0;
- int pos=0;
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
-@@ -91,10 +69,10 @@
- }
+ 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);
--void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-- __u32 srclen, __u32 destlen)
-+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t srclen, uint32_t destlen, void *model)
- {
-- int positions[256];
-+ short positions[256];
- int outpos = 0;
- int pos=0;
-
-@@ -123,6 +101,28 @@
- }
- }
- }
-+ return 0;
- }
+ d_instantiate(dentry, inode);
+ return 0;
+@@ -659,8 +444,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;
-+static struct jffs2_compressor jffs2_rtime_comp = {
-+ .priority = JFFS2_RTIME_PRIORITY,
-+ .name = "rtime",
-+ .compr = JFFS2_COMPR_RTIME,
-+ .compress = &jffs2_rtime_compress,
-+ .decompress = &jffs2_rtime_decompress,
-+#ifdef JFFS2_RTIME_DISABLED
-+ .disabled = 1,
-+#else
-+ .disabled = 0,
-+#endif
-+};
-+
-+int jffs2_rtime_init(void)
-+{
-+ return jffs2_register_compressor(&jffs2_rtime_comp);
-+}
+ mode |= S_IFDIR;
+@@ -692,13 +476,15 @@
-+void jffs2_rtime_exit(void)
-+{
-+ jffs2_unregister_compressor(&jffs2_rtime_comp);
-+}
---- linux-2.4.21/fs/jffs2/compr_rubin.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rubin.c
-@@ -1,49 +1,25 @@
- /*
- * 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 <arjanv@redhat.com>
- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
++ /* Directories get nlink 2 at start */
++ inode->i_nlink = 2;
-
- #include <linux/string.h>
- #include <linux/types.h>
-+#include <linux/jffs2.h>
- #include "compr_rubin.h"
- #include "histo_mips.h"
-+#include "compr.h"
+ 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 +501,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 +508,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -742,41 +520,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->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 = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ 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);
-
--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;
+- 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);
+ }
-@@ -56,7 +32,7 @@
- }
+- 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);
--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)
+ /* 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 +566,19 @@
{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+ struct jffs2_full_dirent *fd;
++ int ret;
- long i0, i1;
-@@ -91,7 +67,7 @@
+ 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 +588,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 +603,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 +630,15 @@
--void end_rubin(struct rubin_state *rs)
-+static void end_rubin(struct rubin_state *rs)
- {
+ f = JFFS2_INODE_INFO(inode);
- int i;
-@@ -104,7 +80,7 @@
- }
+- 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);
--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);
+ jffs2_free_raw_inode(ri);
-@@ -151,7 +127,7 @@
- rs->rec_q = rec_q;
- }
+@@ -869,13 +655,6 @@
+ f->metadata = fn;
+ up(&f->sem);
--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 @@
+- /* 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 +662,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -896,44 +674,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->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));
--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, void *model)
- {
- 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, void *model)
- {
- int bits[8];
- unsigned char histo[256];
- int i;
- int ret;
-- __u32 mysrclen, mydstlen;
-+ uint32_t mysrclen, mydstlen;
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ 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;
- mysrclen = *sourcelen;
- mydstlen = *dstlen - 8;
-@@ -315,8 +291,8 @@
- return 0;
- }
+ /* XXX: This is ugly. */
+ rd->type = (mode & S_IFMT) >> 12;
--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,15 @@
- }
+- 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));
--void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
-- __u32 sourcelen, __u32 dstlen)
-+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t sourcelen, uint32_t dstlen, void *model)
- {
- rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
-+ return 0;
- }
+ jffs2_free_raw_dirent(rd);
--void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
-- __u32 sourcelen, __u32 dstlen)
-+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t sourcelen, uint32_t dstlen, void *model)
+ /* 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 +723,9 @@
+ struct inode *new_dir_i, struct dentry *new_dentry)
{
- int bits[8];
- int c;
-@@ -346,4 +323,51 @@
- bits[c] = data_in[c];
+ 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;
- rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
-+ return 0;
-+}
-+
-+static struct jffs2_compressor jffs2_rubinmips_comp = {
-+ .priority = JFFS2_RUBINMIPS_PRIORITY,
-+ .name = "rubinmips",
-+ .compr = JFFS2_COMPR_DYNRUBIN,
-+ .compress = NULL, /*&jffs2_rubinmips_compress,*/
-+ .decompress = &jffs2_rubinmips_decompress,
-+#ifdef JFFS2_RUBINMIPS_DISABLED
-+ .disabled = 1,
-+#else
-+ .disabled = 0,
-+#endif
-+};
+ /* 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 +754,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;
+
-+int jffs2_rubinmips_init(void)
-+{
-+ return jffs2_register_compressor(&jffs2_rubinmips_comp);
-+}
++ 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);
+
-+void jffs2_rubinmips_exit(void)
-+{
-+ jffs2_unregister_compressor(&jffs2_rubinmips_comp);
-+}
+ if (ret)
+ return ret;
+
+@@ -989,22 +778,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++;
+
-+static struct jffs2_compressor jffs2_dynrubin_comp = {
-+ .priority = JFFS2_DYNRUBIN_PRIORITY,
-+ .name = "dynrubin",
-+ .compr = JFFS2_COMPR_RUBINMIPS,
-+ .compress = jffs2_dynrubin_compress,
-+ .decompress = &jffs2_dynrubin_decompress,
-+#ifdef JFFS2_DYNRUBIN_DISABLED
-+ .disabled = 1,
-+#else
-+ .disabled = 0,
-+#endif
-+};
+ /* 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);
+
-+int jffs2_dynrubin_init(void)
-+{
-+ return jffs2_register_compressor(&jffs2_dynrubin_comp);
-+}
++ /* 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;
++ }
+
-+void jffs2_dynrubin_exit(void)
-+{
-+ jffs2_unregister_compressor(&jffs2_dynrubin_comp);
++ if (S_ISDIR(old_dentry->d_inode->i_mode))
++ old_dir_i->i_nlink--;
++
++ return 0;
}
---- linux-2.4.21/fs/jffs2/compr_rubin.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rubin.h
-@@ -1,7 +1,7 @@
- /* Rubin encoder/decoder header */
- /* work started at : aug 3, 1994 */
- /* last modification : aug 15, 1994 */
--/* $Id$ */
-+/* $Id$ */
-
- #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);
---- linux-2.4.21/fs/jffs2/compr_zlib.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_zlib.c
-@@ -1,51 +1,28 @@
+--- linux-2.4.21/fs/jffs2/erase.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/erase.c
+@@ -1,68 +1,63 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
-- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- * Copyright (C) 2001 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
+ * $Id$
*
*/
-
--#ifndef __KERNEL__
-+#if !defined(__KERNEL__) && !defined(__ECOS)
- #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
- #endif
-
- #include <linux/config.h>
++
#include <linux/kernel.h>
--#include <linux/mtd/compatmac.h> /* for min() */
#include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
-#include <linux/jffs2.h>
- #include <linux/zlib.h>
-+#include <linux/zutil.h>
-+#include <asm/semaphore.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/crc32.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
#include "nodelist.h"
-+#include "compr.h"
-
- /* Plan: call deflate() with avail_in == *sourcelen,
- avail_out = *dstlen - 12 and flush == Z_FINISH.
-@@ -58,120 +35,184 @@
+-#include "crc32.h"
- 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;
+ 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, uint32_t bad_offset);
++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);
--int __init jffs2_zlib_init(void)
-+#ifdef __KERNEL__ /* Linux-only */
-+#include <linux/vmalloc.h>
-+#include <linux/init.h>
-+
-+static int __init alloc_workspaces(void)
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
-- 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;
+- struct erase_info *instr;
+ int ret;
++ uint32_t bad_offset;
++#ifdef __ECOS
++ ret = jffs2_flash_erase(c, jeb);
++ if (!ret) {
++ jffs2_erase_succeeded(c, jeb);
++ return;
++ }
++ bad_offset = jeb->offset;
++#else /* Linux */
++ struct erase_info *instr;
+
++ D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
+ 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;
}
- 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;
+
+@@ -73,23 +68,29 @@
+ instr->len = c->sector_size;
+ instr->callback = jffs2_erase_callback;
+ instr->priv = (unsigned long)(&instr[1]);
++ instr->fail_addr = 0xffffffff;
+
+ ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+ ((struct erase_priv_struct *)instr->priv)->c = c;
+
+ ret = c->mtd->erase(c->mtd, instr);
+- if (!ret) {
++ if (!ret)
+ return;
+- }
++
++ bad_offset = instr->fail_addr;
++ 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;
}
- D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
- return 0;
- }
--void jffs2_zlib_exit(void)
-+static void free_workspaces(void)
- {
-- vfree(deflate_workspace);
-- vfree(inflate_workspace);
-+ vfree(def_strm.workspace);
-+ vfree(inf_strm.workspace);
+@@ -97,74 +98,119 @@
+ 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, bad_offset);
}
-+#else
-+#define alloc_workspaces() (0)
-+#define free_workspaces() do { } while(0)
-+#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, void *model)
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
{
-- z_stream strm;
- int ret;
-
- if (*dstlen <= STREAM_END_SPACE)
- return -1;
+ struct jffs2_eraseblock *jeb;
- down(&deflate_sem);
-- strm.workspace = deflate_workspace;
+- spin_lock_bh(&c->erase_completion_lock);
+- while (!list_empty(&c->erase_pending_list)) {
++ down(&c->erase_free_sem);
-- 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;
- }
+- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++ spin_lock(&c->erase_completion_lock);
-- 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;
+- 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;
++ }
-- 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);
++ } 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);
+
- if (ret != Z_STREAM_END) {
- D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
-- return -1;
-+ ret = -1;
-+ goto out;
++ } 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);
+ }
-- 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;
++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, uint32_t bad_offset)
++{
++ /* For NAND, if the failure did not occur at the device level for a
++ specific physical page, don't bother updating the bad block table. */
++ if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
++ /* We had a device-level failure to erase. Let's see if we've
++ failed too many times. */
++ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
++ /* We'd like to give this block another try. */
++ spin_lock(&c->erase_completion_lock);
++ list_del(&jeb->list);
++ list_add(&jeb->list, &c->erase_pending_list);
++ c->erasing_size -= c->sector_size;
++ c->dirty_size += c->sector_size;
++ jeb->dirty_size = c->sector_size;
++ spin_unlock(&c->erase_completion_lock);
++ return;
++ }
+ }
++
++ 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 (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));
+ 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, instr->fail_addr);
+ } 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 */
-- *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;
+ /* Hmmm. Maybe we should accept the extra space it takes and make
+ this a standard doubly-linked list? */
+@@ -187,7 +233,7 @@
+ continue;
+ }
+
+- if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
++ if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
+ /* It's in the block we're erasing */
+ struct jffs2_raw_node_ref *this;
+
+@@ -221,7 +267,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;
+@@ -231,11 +277,8 @@
+ printk("\n");
+ });
+
+- if (ic->nodes == (void *)ic) {
+- D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
++ if (ic->nodes == (void *)ic)
+ jffs2_del_ino_cache(c, ic);
+- jffs2_free_inode_cache(ic);
+- }
}
--void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
-- __u32 srclen, __u32 destlen)
-+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t srclen, uint32_t destlen, void *model)
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+@@ -256,118 +299,148 @@
+ jeb->last_node = NULL;
+ }
+
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+-{
+- OFNI_BS_2SFFJ(c)->s_dirt = 1;
+-}
+-
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
-- z_stream strm;
+- 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;
-+ int wbits = MAX_WBITS;
-
- down(&inflate_sem);
-- strm.workspace = inflate_workspace;
++ uint32_t bad_offset;
-- 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"));
+- 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)) && (c->cleanmarker_size > 0)) {
+ 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;
+ }
+-
+ }
-+
-+
-+ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
- printk(KERN_WARNING "inflateInit failed\n");
- up(&inflate_sem);
-- return;
-+ return 1;
- }
-- 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;
+ 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;
-- 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);
-+ return 0;
-+}
+ 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) {
++ bad_offset = ofs;
+
-+static struct jffs2_compressor jffs2_zlib_comp = {
-+ .priority = JFFS2_ZLIB_PRIORITY,
-+ .name = "zlib",
-+ .compr = JFFS2_COMPR_ZLIB,
-+ .compress = &jffs2_zlib_compress,
-+ .decompress = &jffs2_zlib_decompress,
-+#ifdef JFFS2_ZLIB_DISABLED
-+ .disabled = 1,
-+#else
-+ .disabled = 0,
-+#endif
-+};
++ 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; i<readlen; i += sizeof(unsigned long)) {
+ /* It's OK. We know it's properly aligned */
+ unsigned long datum = *(unsigned long *)(&ebuf[i]);
+ if (datum + 1) {
+- printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
++ bad_offset += i;
++ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
+ bad:
++ if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
+ jffs2_free_raw_node_ref(marker_ref);
+ kfree(ebuf);
+ bad2:
+- spin_lock_bh(&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);
+- wake_up(&c->erase_wait);
++ spin_lock(&c->erase_completion_lock);
++ /* Stick it on a list (any list) so
++ erase_failed can take it right off
++ again. Silly, but shouldn't happen
++ often. */
++ list_add(&jeb->list, &c->erasing_list);
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_erase_failed(c, jeb, bad_offset);
+ return;
+ }
+ }
+ ofs += readlen;
++ cond_resched();
+ }
+ kfree(ebuf);
+ }
+
++ bad_offset = jeb->offset;
+
-+int __init jffs2_zlib_init(void)
-+{
-+ int ret;
+ /* 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)) {
+
-+ ret = alloc_workspaces();
-+ if (ret)
-+ return ret;
++ if (jffs2_write_nand_cleanmarker(c, jeb))
++ goto bad2;
++
++ jeb->first_node = jeb->last_node = NULL;
+
-+ ret = jffs2_register_compressor(&jffs2_zlib_comp);
-+ if (ret)
-+ free_workspaces();
++ jeb->free_size = c->sector_size;
++ jeb->used_size = 0;
++ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ } else if (c->cleanmarker_size == 0) {
++ jeb->first_node = jeb->last_node = NULL;
+
-+ return ret;
-+}
++ jeb->free_size = c->sector_size;
++ jeb->used_size = 0;
++ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ } else {
++ struct kvec vecs[1];
++ 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)
++ };
+
-+void jffs2_zlib_exit(void)
-+{
-+ jffs2_unregister_compressor(&jffs2_zlib_comp);
-+ free_workspaces();
- }
---- linux-2.4.21/fs/jffs2/crc32.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/crc32.c
-@@ -37,11 +37,11 @@
- * polynomial $edb88320
- */
-
--/* $Id$ */
-+/* $Id$ */
-
- #include "crc32.h"
++ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++
++ vecs[0].iov_base = (unsigned char *) ▮
++ vecs[0].iov_len = sizeof(marker);
++ ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
++
+ 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 %zd, got %zd\n",
+ jeb->offset, sizeof(marker), retlen);
+ goto bad2;
+ }
--const __u32 crc32_table[256] = {
-+const uint32_t crc32_table[256] = {
- 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
- 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
- 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
---- linux-2.4.21/fs/jffs2/crc32.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/crc32.h
-@@ -1,16 +1,16 @@
- #ifndef CRC32_H
- #define CRC32_H
+ 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;
--/* $Id$ */
-+/* $Id$ */
+ jeb->first_node = jeb->last_node = marker_ref;
- #include <linux/types.h>
+- 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;
++ }
--extern const __u32 crc32_table[256];
-+extern const uint32_t crc32_table[256];
+- 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;
- /* Return a 32-bit CRC of the contents of the buffer. */
+ ACCT_SANITY_CHECK(c,jeb);
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
--static inline __u32
--crc32(__u32 val, const void *ss, int len)
-+static inline uint32_t
-+crc32(uint32_t val, const void *ss, int len)
- {
- const unsigned char *s = ss;
- while (--len >= 0)
---- linux-2.4.21/fs/jffs2/dir.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/dir.c
-@@ -1,84 +1,73 @@
+ 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);
+ }
++
+--- linux-2.4.21/fs/jffs2/file.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/file.c
+@@ -1,319 +1,106 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
*
*/
++#include <linux/version.h>
#include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
#include <linux/slab.h>
-+#include <linux/sched.h>
#include <linux/fs.h>
--#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/time.h>
+ #include <linux/pagemap.h>
++#include <linux/highmem.h>
+#include <linux/crc32.h>
#include <linux/jffs2.h>
- #include <linux/jffs2_fs_i.h>
- #include <linux/jffs2_fs_sb.h>
-+#include <linux/time.h>
#include "nodelist.h"
-#include "crc32.h"
-+
-+/* Urgh. Please tell me there's a nicer way of doing these. */
-+#include <linux/version.h>
-+#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);
+ 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));
--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 =
+-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
{
-- read: generic_read_dir,
-- readdir: jffs2_readdir,
+- /* 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
-+ .read = generic_read_dir,
-+ .readdir = jffs2_readdir,
++ .llseek = generic_file_llseek,
++ .open = generic_file_open,
++ .read = generic_file_read,
++ .write = generic_file_write,
+ .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,21 @@
- 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));
++ .mmap = generic_file_readonly_mmap,
++ .fsync = jffs2_fsync,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
++ .sendfile = generic_file_sendfile
++#endif
+ };
-+ jffs2_free_raw_inode(ri);
- d_instantiate(dentry, inode);
+ /* jffs2_file_inode_operations */
- D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
-@@ -332,173 +235,48 @@
+ 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
+ };
--static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
-{
-- 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_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;
-
-- c = JFFS2_SB_INFO(dir_i->i_sb);
+- /* 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));
+- }
-
-- rd = jffs2_alloc_raw_dirent();
-- if (!rd)
+- ri = jffs2_alloc_raw_inode();
+- if (!ri) {
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
- return -ENOMEM;
--
-- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+- }
+-
+- ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
- if (ret) {
-- jffs2_free_raw_dirent(rd);
+- 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);
-
-- dir_f = JFFS2_INODE_INFO(dir_i);
-- down(&dir_f->sem);
+- ri->ino = inode->i_ino;
+- ri->version = ++f->highest_version;
-
-- /* 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);
+- 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;
-
-- 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);
+- if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+- !in_group_p(ri->gid) && !capable(CAP_FSETID))
+- ri->mode &= ~S_ISGID;
-
-- 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);
+- 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;
-
-- if (IS_ERR(fd)) {
-- up(&dir_f->sem);
-- return PTR_ERR(fd);
+- 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;
-
-- /* 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);
+- new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
-
-- 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--;
+- 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;
-}
-
- 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 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;
-- 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;
+- 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;
- }
--
-- 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;
+ 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);
-
-- /* XXX: This is ugly. */
-- rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
-- if (!rd->type) rd->type = DT_REG;
+ 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.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 = 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.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);
+ }
+
-
-- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+ /* 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,13 @@
+ 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;
++ unsigned aligned_start = start & ~3;
+ 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 +223,53 @@
+ }
+
+ ri = jffs2_alloc_raw_inode();
+- if (!ri)
+- return -ENOMEM;
-
-- 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);
+- 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));
-
-- if (IS_ERR(fd)) {
-- up(&dir_f->sem);
-- return PTR_ERR(fd);
-- }
+- 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;
+- }
+- down(&f->sem);
+- datalen = writelen;
+- cdatalen = min(alloclen - sizeof(*ri), writelen);
-
-- /* File it. This will mark the old one obsolete. */
-- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-- up(&dir_f->sem);
+- 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;
++ if (!ri) {
++ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
++ return -ENOMEM;
+ }
+- /* 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);
-
-- if (!rename) {
-- f = JFFS2_INODE_INFO(old_dentry->d_inode);
-- down(&f->sem);
-- old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+- 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);
++ /* 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());
+
+- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
++ /* 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);
+
+- jffs2_complete_reservation(c);
++ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
++ (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
++ end - aligned_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;
++
++ /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
++ if (writtenlen < (start&3))
++ writtenlen = 0;
++ else
++ writtenlen -= (start&3);
++
++ 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 +277,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));
- }
-- return 0;
+- 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;
}
-+/***********************************************************************/
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/fs.c
+@@ -0,0 +1,693 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/mtd/mtd.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/vfs.h>
++#include <linux/crc32.h>
++#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, 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;
++
++ D2(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, 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.
++ Flush the writebuffer, if neccecary, else we loose it */
++ 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);
++ }
++
++ if (!(*flags & MS_RDONLY))
++ jffs2_start_garbage_collect_thread(c);
++
++ *flags |= MS_NOATIME;
++
++ 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);
+
-
- 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;
++ f = JFFS2_INODE_INFO(inode);
++ jffs2_init_inode_info(f);
+
-+ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
++ memset(ri, 0, sizeof(*ri));
++ /* Set OS-specific defaults for new inodes */
++ ri->uid = cpu_to_je16(current->fsuid);
+
- 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,13 +295,12 @@
- struct jffs2_full_dnode *fn;
- struct jffs2_full_dirent *fd;
- int namelen;
-- __u32 alloclen, phys_ofs;
-- __u32 writtenlen;
-- int ret;
-+ uint32_t alloclen, phys_ofs;
-+ int ret, targetlen = strlen(target);
-
- /* FIXME: If you care. We'd need to use frags for the target
- if it grows much more than this */
-- if (strlen(target) > 254)
-+ if (targetlen > 254)
- return -EINVAL;
-
- ri = jffs2_alloc_raw_inode();
-@@ -537,7 +314,7 @@
- * Just the node will do for now, though
- */
- namelen = dentry->d_name.len;
-- ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
-+ ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-
- if (ret) {
- jffs2_free_raw_inode(ri);
-@@ -556,15 +333,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 = targetlen;
-+ 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, targetlen));
-+ 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, targetlen, phys_ofs, ALLOC_NORMAL);
-
- jffs2_free_raw_inode(ri);
-
-@@ -575,19 +353,26 @@
- jffs2_clear_inode(inode);
- return PTR_ERR(fn);
- }
++ 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));
+
-+ /* We use f->dents field to store the target path. */
-+ f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
-+ if (!f->dents) {
-+ printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
-+ up(&f->sem);
-+ jffs2_complete_reservation(c);
-+ jffs2_clear_inode(inode);
-+ return -ENOMEM;
++ 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);
++
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++ if (c->mtd->type == MTD_NANDFLASH) {
++ printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
++ return -EINVAL;
++ }
++ if (c->mtd->type == MTD_DATAFLASH) {
++ printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
++ return -EINVAL;
+ }
++#endif
+
-+ memcpy(f->dents, target, targetlen + 1);
-+ D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
++ c->flash_size = c->mtd->size;
+
- /* No data here. Only a metadata node, which will be
- obsoleted by the first data write
- */
- 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 +380,6 @@
- jffs2_clear_inode(inode);
- return ret;
- }
-- }
-
- rd = jffs2_alloc_raw_dirent();
- if (!rd) {
-@@ -608,41 +392,42 @@
- dir_f = JFFS2_INODE_INFO(dir_i);
- down(&dir_f->sem);
++ /*
++ * Check, if we have to concatenate physical blocks to larger virtual blocks
++ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
++ */
++ c->sector_size = c->mtd->erasesize;
++ blocks = c->flash_size / c->sector_size;
++ if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
++ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
++ blocks >>= 1;
++ c->sector_size <<= 1;
++ }
++ }
++
++ /*
++ * Size alignment check
++ */
++ if ((c->sector_size * blocks) != c->flash_size) {
++ c->flash_size = c->sector_size * blocks;
++ printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
++ c->flash_size / 1024);
++ }
++
++ 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 */
++
++ /* NAND (or other bizarre) flash... do setup accordingly */
++ ret = jffs2_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);
++ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++ vfree(c->blocks);
++ else
++ kfree(c->blocks);
++ out_inohash:
++ kfree(c->inocache_list);
++ out_wbuf:
++ jffs2_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);
++}
++
++int jffs2_flash_setup(struct jffs2_sb_info *c) {
++ int ret = 0;
++
++ if (jffs2_cleanmarker_oob(c)) {
++ /* NAND flash... do setup accordingly */
++ ret = jffs2_nand_flash_setup(c);
++ if (ret)
++ return ret;
++ }
++
++ /* add setups for other bizarre flashes here... */
++ if (jffs2_nor_ecc(c)) {
++ ret = jffs2_nor_ecc_flash_setup(c);
++ if (ret)
++ return ret;
++ }
++
++ /* and Dataflash */
++ if (jffs2_dataflash(c)) {
++ ret = jffs2_dataflash_setup(c);
++ if (ret)
++ return ret;
++ }
++
++ return ret;
++}
++
++void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
++
++ if (jffs2_cleanmarker_oob(c)) {
++ jffs2_nand_flash_cleanup(c);
++ }
++
++ /* add cleanups for other bizarre flashes here... */
++ if (jffs2_nor_ecc(c)) {
++ jffs2_nor_ecc_flash_cleanup(c);
++ }
++
++ /* and DataFlash */
++ if (jffs2_dataflash(c)) {
++ jffs2_dataflash_cleanup(c);
++ }
++}
+--- linux-2.4.21/fs/jffs2/gc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/gc.c
+@@ -1,76 +1,68 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
-- 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->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));
+ #include <linux/kernel.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
++#include <linux/crc32.h>
++#include <linux/compiler.h>
++#include <linux/stat.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
-- rd->pino = dir_i->i_ino;
-- rd->version = ++dir_f->highest_version;
-- rd->ino = inode->i_ino;
-- rd->mctime = CURRENT_TIME;
-+ 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);
--
-- 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);
- }
++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);
-- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
-+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ /* 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;
- jffs2_free_raw_dirent(rd);
+ /* 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 +72,16 @@
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
- /* Link the fd into the inode's list, obsoleting an old
- one if necessary. */
- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ 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"));
+
- up(&dir_f->sem);
-+ jffs2_complete_reservation(c);
-
- d_instantiate(dentry, inode);
- return 0;
-@@ -659,8 +444,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;
++ 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 */
++ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
+ return NULL;
+ }
- mode |= S_IFDIR;
-@@ -692,13 +476,15 @@
+@@ -94,6 +93,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;
++ }
++
++ D2(jffs2_dump_block_lists(c));
+ return ret;
+ }
- inode->i_op = &jffs2_dir_inode_operations;
- inode->i_fop = &jffs2_dir_operations;
-+ /* Directories get nlink 2 at start */
-+ inode->i_nlink = 2;
+@@ -103,21 +113,90 @@
+ */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+ {
+- struct jffs2_eraseblock *jeb;
+ struct jffs2_inode_info *f;
++ struct jffs2_inode_cache *ic;
++ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
+- struct jffs2_node_frag *frag;
+- struct jffs2_full_dnode *fn = NULL;
+- struct jffs2_full_dirent *fd;
+- __u32 start = 0, end = 0, nrfrags = 0;
+- __u32 inum;
+- struct inode *inode;
+- int ret = 0;
++ int ret = 0, inum, nlink;
- f = JFFS2_INODE_INFO(inode);
+ if (down_interruptible(&c->alloc_sem))
+ return -EINTR;
-- 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);
+- 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);
++ D2(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;
++ }
- jffs2_free_raw_inode(ri);
+ /* First, work out which block we're garbage-collecting */
+ jeb = c->gcblock;
+@@ -126,13 +205,15 @@
+ jeb = jffs2_find_gc_block(c);
-@@ -715,13 +501,6 @@
- f->metadata = fn;
- up(&f->sem);
+ if (!jeb) {
+- printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+- spin_unlock_bh(&c->erase_completion_lock);
++ D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ return -EIO;
+ }
-- /* 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 +508,6 @@
- jffs2_clear_inode(inode);
- return ret;
- }
-- }
-
- rd = jffs2_alloc_raw_dirent();
- if (!rd) {
-@@ -742,41 +520,43 @@
- dir_f = JFFS2_INODE_INFO(dir_i);
- down(&dir_f->sem);
+- 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));
-- 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->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));
+ if (!jeb->used_size) {
+ up(&c->alloc_sem);
+@@ -141,61 +222,215 @@
-- rd->pino = dir_i->i_ino;
-- rd->version = ++dir_f->highest_version;
-- rd->ino = inode->i_ino;
-- rd->mctime = CURRENT_TIME;
-+ 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);
--
-- 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);
+ 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;
}
+
+- inum = jffs2_raw_ref_to_inum(raw);
+- D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
++ ic = jffs2_raw_ref_to_ic(raw);
-- 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++;
+- spin_unlock_bh(&c->erase_completion_lock);
++ /* 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);
- jffs2_free_raw_dirent(rd);
+- 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, inum));
++ spin_unlock(&c->erase_completion_lock);
- /* Link the fd into the inode's list, obsoleting an old
- one if necessary. */
- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- inode = iget(OFNI_BS_2SFFJ(c), inum);
+- if (is_bad_inode(inode)) {
+- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
+- /* NB. This will happen again. We need to do something appropriate here. */
++ 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));
+
- up(&dir_f->sem);
-+ jffs2_complete_reservation(c);
-
- d_instantiate(dentry, inode);
- return 0;
-@@ -786,15 +566,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;
++ /* 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.
++ */
++
++ 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));
++ }
++ 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.
++ */
++ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
++ ic->ino, ic->state);
+ up(&c->alloc_sem);
+- iput(inode);
+- return -EIO;
++ 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;
}
-- 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 +588,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;
+- f = JFFS2_INODE_INFO(inode);
++ /* 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;
++ }
++
++ /* 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)) {
++ ret = PTR_ERR(f);
++ goto release_sem;
++ }
++ if (!f) {
++ ret = 0;
++ goto release_sem;
++ }
++
++ ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++
++ jffs2_gc_release_inode(c, f);
++
++ release_sem:
++ up(&c->alloc_sem);
++
++ 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;
++
+ 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 (!old_valid_dev(rdev))
-+ return -EINVAL;
+- if (raw->flash_offset & 1) {
++ spin_lock(&c->erase_completion_lock);
+
- ri = jffs2_alloc_raw_inode();
- if (!ri)
- return -ENOMEM;
-@@ -817,7 +603,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);
++ 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;
}
-@@ -844,15 +630,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 +655,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 +662,6 @@
- jffs2_clear_inode(inode);
- return ret;
+- 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;
+@@ -206,13 +441,22 @@
}
-- }
-
- rd = jffs2_alloc_raw_dirent();
- if (!rd) {
-@@ -896,44 +674,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->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 = dir_i->i_ino;
-- rd->version = ++dir_f->highest_version;
-- rd->ino = inode->i_ino;
-- rd->mctime = CURRENT_TIME;
-+ 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);
+ }
+ 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;
+ }
+@@ -224,12 +468,13 @@
}
-- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
-+ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ 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;
+@@ -237,53 +482,207 @@
+ }
+ upnout:
+ up(&f->sem);
+- up(&c->alloc_sem);
+- iput(inode);
- jffs2_free_raw_dirent(rd);
+- eraseit_lock:
+- /* If we've finished this block, start it erasing */
+- spin_lock_bh(&c->erase_completion_lock);
++ return ret;
++}
- /* Link the fd into the inode's list, obsoleting an old
- one if necessary. */
- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- 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;
+
- up(&dir_f->sem);
-+ jffs2_complete_reservation(c);
-
- d_instantiate(dentry, inode);
-
-@@ -944,7 +723,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 +754,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;
++ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+
-+ 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);
++ rawlen = ref_totlen(c, c->gcblock, raw);
+
- if (ret)
- return ret;
-
-@@ -989,22 +778,36 @@
- }
++ /* 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;
}
+- spin_unlock_bh(&c->erase_completion_lock);
-+ /* 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++;
++ node = kmalloc(rawlen, GFP_KERNEL);
++ if (!node)
++ return -ENOMEM;
+
- /* 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);
++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
++ if (!ret && retlen != rawlen)
++ ret = -EIO;
++ if (ret)
++ goto out_node;
+
-+ /* 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;
++ 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;
+ }
+
-+ if (S_ISDIR(old_dentry->d_inode->i_mode))
-+ old_dir_i->i_nlink--;
++ 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;
++ }
+
-+ return 0;
++ 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;
++ }
++ }
++ 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);
++ }
++
++ 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-core, the 'nraw' pointer is then
++ going to be fetched from ic->nodes by our caller. */
++ spin_lock(&c->erase_completion_lock);
++ nraw->next_in_ino = ic->nodes;
++ ic->nodes = nraw;
++ spin_unlock(&c->erase_completion_lock);
++
++ 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;
}
---- linux-2.4.21/fs/jffs2/erase.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/erase.c
-@@ -1,68 +1,63 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
-+
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/mtd/mtd.h>
--#include <linux/jffs2.h>
--#include <linux/interrupt.h>
-+#include <linux/compiler.h>
-+#include <linux/crc32.h>
-+#include <linux/sched.h>
-+#include <linux/pagemap.h>
- #include "nodelist.h"
--#include "crc32.h"
+ 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) {
+ printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+ return -ENOMEM;
+ }
+- ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
++ ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
+ if (ret) {
+ printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
+ kfree(mdata);
+@@ -295,34 +694,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.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 = 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.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);
- 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, uint32_t bad_offset);
-+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);
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -333,41 +732,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;
+ }
- void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ 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 erase_info *instr;
+- 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;
-+ uint32_t bad_offset;
-+#ifdef __ECOS
-+ ret = jffs2_flash_erase(c, jeb);
-+ if (!ret) {
-+ jffs2_erase_succeeded(c, jeb);
-+ return;
-+ }
-+ bad_offset = jeb->offset;
-+#else /* Linux */
-+ struct erase_info *instr;
-+ D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
- 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;
- }
+- 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));
-@@ -73,23 +68,29 @@
- instr->len = c->sector_size;
- instr->callback = jffs2_erase_callback;
- instr->priv = (unsigned long)(&instr[1]);
-+ instr->fail_addr = 0xffffffff;
+- 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));
- ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
- ((struct erase_priv_struct *)instr->priv)->c = c;
-
- ret = c->mtd->erase(c->mtd, instr);
-- if (!ret) {
-+ if (!ret)
- return;
-- }
-+
-+ bad_offset = instr->fail_addr;
-+ 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;
+ 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);
-@@ -97,74 +98,119 @@
- 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, bad_offset);
+ if (IS_ERR(new_fd)) {
+ printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+@@ -378,19 +776,97 @@
}
--void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
-+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+ 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_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);
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent **fdp = &f->dents;
+ int found = 0;
-- 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)) {
+- /* 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);
+
-+ 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);
++ rd = kmalloc(rawlen, GFP_KERNEL);
++ if (!rd)
++ return -ENOMEM;
+
-+ if (!--count) {
-+ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
-+ goto done;
++ /* 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 (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
++ 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 %u) reading header from obsolete node at %08x\n",
++ retlen, rawlen, ref_offset(raw));
++ continue;
+ }
-
-+ } 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();
-+ }
++ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
++ continue;
+
- /* 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);
++ /* If the name CRC doesn't match, skip */
++ if (je32_to_cpu(rd->name_crc) != name_crc)
++ continue;
+
-+ spin_unlock(&c->erase_completion_lock);
-+ done:
- D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
++ /* 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;
+
-+ 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, uint32_t bad_offset)
-+{
-+ /* For NAND, if the failure did not occur at the device level for a
-+ specific physical page, don't bother updating the bad block table. */
-+ if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
-+ /* We had a device-level failure to erase. Let's see if we've
-+ failed too many times. */
-+ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
-+ /* We'd like to give this block another try. */
-+ spin_lock(&c->erase_completion_lock);
-+ list_del(&jeb->list);
-+ list_add(&jeb->list, &c->erase_pending_list);
-+ c->erasing_size -= c->sector_size;
-+ c->dirty_size += c->sector_size;
-+ jeb->dirty_size = c->sector_size;
-+ spin_unlock(&c->erase_completion_lock);
-+ return;
++ /* 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);
+ }
-+ }
+
-+ 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);
-+}
++ up(&c->erase_free_sem);
++ kfree(rd);
++ }
+
-+#ifndef __ECOS
- static void jffs2_erase_callback(struct erase_info *instr)
++ /* No need for it any more. Just mark it obsolete and remove it from the list */
+ while (*fdp) {
+ if ((*fdp) == fd) {
+ found = 1;
+@@ -400,7 +876,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);
+@@ -408,93 +884,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 erase_priv_struct *priv = (void *)instr->priv;
+- 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;
- 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, instr->fail_addr);
+- 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 {
-- 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 */
+ 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.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));
- /* Hmmm. Maybe we should accept the extra space it takes and make
- this a standard doubly-linked list? */
-@@ -187,7 +233,7 @@
- continue;
- }
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.offset = start;
+- ri.dsize = end - start;
+- ri.csize = 0;
++ 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));
-- if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
-+ if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
- /* It's in the block we're erasing */
- struct jffs2_raw_node_ref *this;
+ 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);
-@@ -221,7 +267,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;
-@@ -231,11 +277,8 @@
- printk("\n");
+ 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);
+@@ -510,12 +988,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));
});
-- if (ic->nodes == (void *)ic) {
-- D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
-+ if (ic->nodes == (void *)ic)
- jffs2_del_ino_cache(c, ic);
-- jffs2_free_inode_cache(ic);
-- }
- }
-
- static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
-@@ -256,118 +299,148 @@
- jeb->last_node = NULL;
+- 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) {
+@@ -540,49 +1023,146 @@
}
--void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
--{
-- OFNI_BS_2SFFJ(c)->s_dirt = 1;
--}
--
--void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
-+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ 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 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;
-+ uint32_t bad_offset;
+- 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;
-- 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)) && (c->cleanmarker_size > 0)) {
- 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;
+ memset(&ri, 0, sizeof(ri));
- 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;
+- 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));
-- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
-- if (ret < 0) {
-+ bad_offset = ofs;
+ 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;
+
-+ 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; i<readlen; i += sizeof(unsigned long)) {
- /* It's OK. We know it's properly aligned */
- unsigned long datum = *(unsigned long *)(&ebuf[i]);
- if (datum + 1) {
-- printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
-+ bad_offset += i;
-+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
- bad:
-+ if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
- jffs2_free_raw_node_ref(marker_ref);
- kfree(ebuf);
- bad2:
-- spin_lock_bh(&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);
-- wake_up(&c->erase_wait);
-+ spin_lock(&c->erase_completion_lock);
-+ /* Stick it on a list (any list) so
-+ erase_failed can take it right off
-+ again. Silly, but shouldn't happen
-+ often. */
-+ list_add(&jeb->list, &c->erasing_list);
-+ spin_unlock(&c->erase_completion_lock);
-+ jffs2_erase_failed(c, jeb, bad_offset);
- return;
- }
- }
- ofs += readlen;
-+ cond_resched();
- }
- kfree(ebuf);
++ 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;
}
-
-+ bad_offset = jeb->offset;
++ 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;
++ }
+
- /* 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)) {
++ 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;
++ }
++ }
+
-+ if (jffs2_write_nand_cleanmarker(c, jeb))
-+ goto bad2;
-+
-+ jeb->first_node = jeb->last_node = NULL;
++ /* ... then up */
+
-+ jeb->free_size = c->sector_size;
-+ jeb->used_size = 0;
-+ jeb->dirty_size = 0;
-+ jeb->wasted_size = 0;
-+ } else if (c->cleanmarker_size == 0) {
-+ jeb->first_node = jeb->last_node = NULL;
++ /* Find last frag which is actually part of the node we're to GC. */
++ frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
+
-+ jeb->free_size = c->sector_size;
-+ jeb->used_size = 0;
-+ jeb->dirty_size = 0;
-+ jeb->wasted_size = 0;
-+ } else {
-+ struct kvec vecs[1];
-+ 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)
-+ };
++ while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
+
-+ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++ /* 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;
++ }
+
-+ vecs[0].iov_base = (unsigned char *) ▮
-+ vecs[0].iov_len = sizeof(marker);
-+ ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
-+
++ 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 */
+@@ -592,63 +1172,58 @@
+ * 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;
+- char comprtype = JFFS2_COMPR_NONE;
++ uint32_t datalen;
++ uint32_t cdatalen;
++ uint16_t comprtype = JFFS2_COMPR_NONE;
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+
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 %zd, got %zd\n",
- jeb->offset, sizeof(marker), retlen);
- goto bad2;
+- 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;
- 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;
+ writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
-- 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;
-+ }
+- 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);
++ comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
-- 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;
+- 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;
+- ri.compr = comprtype;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = crc32(0, writebuf, 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));
+
+- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
++ 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 & 0xff;
++ ri.usercompr = (comprtype >> 8) & 0xff;
++ 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);
++
++ jffs2_free_comprbuf(comprbuf, writebuf);
- ACCT_SANITY_CHECK(c,jeb);
-- ACCT_PARANOIA_CHECK(jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -663,12 +1238,8 @@
+ f->metadata = NULL;
+ }
+ }
+- if (comprbuf) kfree(comprbuf);
- 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);
+- 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;
}
-+
---- linux-2.4.21/fs/jffs2/file.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/file.c
-@@ -1,319 +1,106 @@
+
+--- linux-2.4.21/fs/jffs2/ioctl.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/ioctl.c
+@@ -1,37 +1,13 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
*
*/
-+#include <linux/version.h>
- #include <linux/kernel.h>
--#include <linux/mtd/compatmac.h> /* for min() */
- #include <linux/slab.h>
- #include <linux/fs.h>
-+#include <linux/time.h>
- #include <linux/pagemap.h>
-+#include <linux/highmem.h>
-+#include <linux/crc32.h>
- #include <linux/jffs2.h>
- #include "nodelist.h"
--#include "crc32.h"
-
- 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)
+@@ -42,6 +18,6 @@
{
-- /* 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;
+ /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+ will include compression support etc. */
+- return -EINVAL;
++ return -ENOTTY;
}
+
+--- linux-2.4.21/fs/jffs2/malloc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/malloc.c
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
- 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
- };
+@@ -47,6 +23,9 @@
+ #define JFFS2_SLAB_POISON 0
+ #endif
- 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
- };
++// 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;
--int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
-{
-- 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);
-- }
+- struct jffs2_tmp_dnode_info *next;
-
-- 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);
+- while (tn) {
+- next = tn;
+- tn = tn->next;
+- jffs2_free_full_dnode(next->fn);
+- jffs2_free_tmp_dnode_info(next);
- }
-- 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;
-- }
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+-{
+- struct jffs2_full_dirent *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"));
-- }
+- while (fd) {
+- next = fd->next;
+- jffs2_free_full_dirent(fd);
+- fd = next;
- }
-- 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 @@
+ int __init jffs2_create_slab_caches(void)
{
- 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.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));
+- 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;
-- 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.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,13 @@
- 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;
-+ unsigned aligned_start = start & ~3;
- int ret = 0;
-- ssize_t writtenlen = 0;
-+ uint32_t writtenlen = 0;
+- 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;
-- 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));
+- 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;
- if (!start && end == PAGE_CACHE_SIZE) {
- /* We need to avoid deadlock with page_cache_read() in
-@@ -435,109 +223,53 @@
- }
+- 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;
- 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;
+- 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;
-- D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+- 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);
-
-- 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;
-- }
-- down(&f->sem);
-- datalen = writelen;
-- cdatalen = min(alloclen - sizeof(*ri), writelen);
++ 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);
-
-- 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;
-+ if (!ri) {
-+ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
-+ return -ENOMEM;
- }
-- /* 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);
-+ /* 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());
+ struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+@@ -146,75 +114,92 @@
-- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
-+ /* 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);
+ 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;
+ }
-- jffs2_complete_reservation(c);
-+ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
-+ (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
-+ end - aligned_start, &writtenlen);
+ 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);
+ }
-- if (comprtype != JFFS2_COMPR_NONE)
-- kfree(comprbuf);
-+ kunmap(pg);
+ 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;
+ }
-- 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;
-+
-+ /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
-+ if (writtenlen < (start&3))
-+ writtenlen = 0;
-+ else
-+ writtenlen -= (start&3);
-+
-+ 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;
- }
+ 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);
+ }
- jffs2_free_raw_inode(ri);
+ 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;
+ }
-- 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 +277,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);
+ 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);
+ }
-- 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;
+ 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;
}
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/fs.c
-@@ -0,0 +1,693 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
+
+ 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);
+ }
+
+--- linux-2.4.21/fs/jffs2/nodelist.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.c
+@@ -1,44 +1,24 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
-+ *
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
+ *
+- * 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.
+ * For licensing information, see the file 'LICENCE' in this directory.
-+ *
+ *
+- * $Id$
+ * $Id$
-+ *
-+ */
-+
-+#include <linux/version.h>
-+#include <linux/config.h>
-+#include <linux/kernel.h>
+ *
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
+#include <linux/sched.h>
-+#include <linux/fs.h>
-+#include <linux/list.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/pagemap.h>
-+#include <linux/slab.h>
-+#include <linux/vmalloc.h>
-+#include <linux/vfs.h>
+ #include <linux/fs.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/rbtree.h>
+#include <linux/crc32.h>
-+#include "nodelist.h"
-+
-+
-+static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #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,93 +69,156 @@
+ *prev = tn;
+ }
+
++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+{
-+ 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, 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);
++ struct jffs2_tmp_dnode_info *next;
+
-+ 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);
++ while (tn) {
++ next = tn;
++ tn = tn->next;
++ jffs2_free_full_dnode(next->fn);
++ jffs2_free_tmp_dnode_info(next);
+ }
-+ 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);
++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++{
++ struct jffs2_full_dirent *next;
+
-+ return 0;
++ while (fd) {
++ next = fd->next;
++ jffs2_free_full_dirent(fd);
++ fd = next;
++ }
+}
+
-+int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
++/* Returns first valid node after 'ref'. May return 'ref' */
++static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
+{
-+ return jffs2_do_setattr(dentry->d_inode, iattr);
++ while (ref && ref->next_in_ino) {
++ if (!ref_obsolete(ref))
++ return ref;
++ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
++ ref = ref->next_in_ino;
++ }
++ return NULL;
+}
+
-+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;
+ /* 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,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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_raw_node_ref *ref, *valid_ref;
+ struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+ struct jffs2_full_dirent *fd, *ret_fd = NULL;
+-
+ union jffs2_node_union node;
+ size_t retlen;
+ int err;
+
+ *mctime_ver = 0;
+
+- 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);
+- }
+- 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) {
+- /* 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));
+- continue;
+- }
+- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
++ D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
+
+ 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;
++ valid_ref = jffs2_first_valid_node(f->inocache->nodes);
+
-+ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++ if (!valid_ref && (f->inocache->ino != 1))
++ printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
+
-+ D2(jffs2_dump_block_lists(c));
++ while (valid_ref) {
++ /* 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. So if we mark 'ref' obsolete while we're
++ not holding the lock, it can go away immediately. For that reason,
++ we find the next valid node first, before processing 'ref'.
++ */
++ ref = valid_ref;
++ valid_ref = jffs2_first_valid_node(ref->next_in_ino);
++ spin_unlock(&c->erase_completion_lock);
+
-+ spin_unlock(&c->erase_completion_lock);
++ cond_resched();
+
-+ return 0;
-+}
++ /* 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;
+ goto free_out;
+ }
+- 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;
+@@ -188,6 +231,7 @@
+ }
+ fd->nhash = full_name_hash(fd->name, node.d.nsize);
+ fd->next = NULL;
++ fd->name[node.d.nsize] = '\0';
+ /* Wheee. We now have a complete jffs2_full_dirent structure, with
+ the name in it and everything. Link it into the list
+ */
+@@ -196,21 +240,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 (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->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "obsoleted\n"));
+- /* Obsoleted */
++ 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;
+
-+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));
++ 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;
++ }
+
-+ jffs2_do_clear_inode(c, f);
-+}
++ 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
+
-+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;
++ 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;
+ }
++
++ }
+
-+ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++ /* 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);
+
-+ f = JFFS2_INODE_INFO(inode);
-+ c = JFFS2_SB_INFO(inode->i_sb);
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
+
-+ jffs2_init_inode_info(f);
-+
-+ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
++ /* 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 (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));
++ 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);
++ }
+
-+ inode->i_nlink = f->inocache->nlink;
+ tn = jffs2_alloc_tmp_dnode_info();
+ if (!tn) {
+ D1(printk(KERN_DEBUG "alloc tn failed\n"));
+@@ -225,36 +374,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;
+
-+ inode->i_blksize = PAGE_SIZE;
-+ inode->i_blocks = (inode->i_size + 511) >> 9;
-+
-+ switch (inode->i_mode & S_IFMT) {
-+ jint16_t rdev;
++ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
++ je16_to_cpu(node.u.nodetype), ref_offset(ref));
+
-+ case S_IFLNK:
-+ inode->i_op = &jffs2_symlink_inode_operations;
-+ break;
-+
-+ case S_IFDIR:
-+ {
-+ struct jffs2_full_dirent *fd;
++ /* 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);
+
-+ 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++;
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
+
-+ 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;
++ 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;
+ }
+
-+ 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, 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;
-+ }
+ }
++ spin_lock(&c->erase_completion_lock);
+
-+ 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;
+ }
++ spin_unlock(&c->erase_completion_lock);
+ *tnp = ret_tn;
+ *fdp = ret_fd;
+
+@@ -266,19 +455,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;
+
+@@ -290,6 +490,8 @@
+ {
+ struct jffs2_inode_cache **prev;
+ D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
++ BUG_ON(!new->ino);
+
-+ default:
-+ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
-+ }
+ spin_lock(&c->inocache_lock);
+
+ prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
+@@ -299,13 +501,14 @@
+ }
+ new->next = *prev;
+ *prev = new;
+
-+ up(&f->sem);
+ spin_unlock(&c->inocache_lock);
+ }
+
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
+ {
+ struct jffs2_inode_cache **prev;
+- D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
++ D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+ spin_lock(&c->inocache_lock);
+
+ prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
+@@ -316,6 +519,15 @@
+ if ((*prev) == old) {
+ *prev = old->next;
+ }
+
-+ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
-+}
++ /* Free it now unless it's in READING or CLEARING state, which
++ are the transitions upon read_inode() and clear_inode(). The
++ rest of the time we know nobody else is looking at it, and
++ if it's held by read_inode() or clear_inode() they'll free it
++ for themselves. */
++ if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
++ jffs2_free_inode_cache(old);
+
-+void jffs2_dirty_inode(struct inode *inode)
+ spin_unlock(&c->inocache_lock);
+ }
+
+@@ -328,7 +540,6 @@
+ this = c->inocache_list[i];
+ while (this) {
+ next = this->next;
+- D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
+ jffs2_free_inode_cache(this);
+ this = next;
+ }
+@@ -352,3 +563,128 @@
+ }
+ }
+
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
+{
-+ 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));
++ /* 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;
+
-+ 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;
++ D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
+
-+ jffs2_do_setattr(inode, &iattr);
-+}
++ next = fragtree->rb_node;
+
-+int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
-+{
-+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ while(next) {
++ frag = rb_entry(next, struct jffs2_node_frag, rb);
+
-+ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
-+ return -EROFS;
++ 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;
++ }
++ }
+
-+ /* 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.
-+ Flush the writebuffer, if neccecary, else we loose it */
-+ 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);
-+ }
++ /* Exact match not found. Go back up looking at each parent,
++ and return the closest smaller one */
+
-+ if (!(*flags & MS_RDONLY))
-+ jffs2_start_garbage_collect_thread(c);
++ 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"));
+
-+ *flags |= MS_NOATIME;
-+
-+ return 0;
++ return prev;
+}
+
-+void jffs2_write_super (struct super_block *sb)
++/* 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_sb_info *c = JFFS2_SB_INFO(sb);
-+ sb->s_dirt = 0;
++ struct jffs2_node_frag *frag;
++ struct jffs2_node_frag *parent;
+
-+ if (sb->s_flags & MS_RDONLY)
++ if (!root->rb_node)
+ 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));
++ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
+
-+ c = JFFS2_SB_INFO(sb);
-+
-+ inode = new_inode(sb);
-+
-+ if (!inode)
-+ return ERR_PTR(-ENOMEM);
++ 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;
++ }
+
-+ f = JFFS2_INODE_INFO(inode);
-+ jffs2_init_inode_info(f);
++ 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;
++ }
+
-+ memset(ri, 0, sizeof(*ri));
-+ /* Set OS-specific defaults for new inodes */
-+ ri->uid = cpu_to_je16(current->fsuid);
++ jffs2_free_node_frag(frag);
++ frag = parent;
+
-+ 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);
++ cond_resched();
+ }
-+ 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)
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
+{
-+ struct jffs2_sb_info *c;
-+ struct inode *root_i;
-+ int ret;
-+ size_t blocks;
-+
-+ c = JFFS2_SB_INFO(sb);
-+
-+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
-+ if (c->mtd->type == MTD_NANDFLASH) {
-+ printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
-+ return -EINVAL;
-+ }
-+ if (c->mtd->type == MTD_DATAFLASH) {
-+ printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
-+ return -EINVAL;
-+ }
-+#endif
-+
-+ 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)
-+ */
-+ c->sector_size = c->mtd->erasesize;
-+ blocks = c->flash_size / c->sector_size;
-+ if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
-+ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
-+ blocks >>= 1;
-+ c->sector_size <<= 1;
-+ }
-+ }
-+
-+ /*
-+ * Size alignment check
-+ */
-+ if ((c->sector_size * blocks) != c->flash_size) {
-+ c->flash_size = c->sector_size * blocks;
-+ printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
-+ c->flash_size / 1024);
-+ }
++ struct rb_node *parent = &base->rb;
++ struct rb_node **link = &parent;
+
-+ 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);
++ D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag,
++ newfrag->ofs, newfrag->ofs+newfrag->size, base));
+
-+ 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;
++ 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();
++ }
+ }
+
-+ c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
-+ /* Joern -- stick alignment for weird 8-byte-page flash here */
++ rb_link_node(&newfrag->rb, &base->rb, link);
++}
+--- linux-2.4.21/fs/jffs2/nodelist.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.h
+@@ -1,48 +1,35 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#ifndef __JFFS2_NODELIST_H__
++#define __JFFS2_NODELIST_H__
+
-+ /* NAND (or other bizarre) flash... do setup accordingly */
-+ ret = jffs2_flash_setup(c);
-+ if (ret)
-+ return ret;
+ #include <linux/config.h>
+ #include <linux/fs.h>
+-
++#include <linux/types.h>
++#include <linux/jffs2.h>
+ #include <linux/jffs2_fs_sb.h>
+ #include <linux/jffs2_fs_i.h>
+
++#ifdef __ECOS
++#include "os-ecos.h"
++#else
++#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
++#include "os-linux.h"
++#endif
+
-+ 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 *));
+ #ifndef CONFIG_JFFS2_FS_DEBUG
+-#define CONFIG_JFFS2_FS_DEBUG 2
++#define CONFIG_JFFS2_FS_DEBUG 1
+ #endif
+
+ #if CONFIG_JFFS2_FS_DEBUG > 0
+@@ -57,6 +44,39 @@
+ #define D2(x)
+ #endif
+
++#define JFFS2_NATIVE_ENDIAN
+
-+ if ((ret = jffs2_do_mount_fs(c)))
-+ goto out_inohash;
++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
++ whatever OS we're actually running on here too. */
+
-+ ret = -EINVAL;
++#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)})
+
-+ 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;
-+ }
++#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))})
+
-+ 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;
++#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))})
+
-+#if LINUX_VERSION_CODE >= 0x20403
-+ sb->s_maxbytes = 0xFFFFFFFF;
++#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
-+ 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);
-+ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+ vfree(c->blocks);
-+ else
-+ kfree(c->blocks);
-+ out_inohash:
-+ kfree(c->inocache_list);
-+ out_wbuf:
-+ jffs2_flash_cleanup(c);
+ /*
+ This is all we need to keep in-core for each raw node during normal
+ operation. As and when we do read_inode on a particular inode, we can
+@@ -71,27 +91,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.
+- */
+-};
+-
+-/*
+- Used for keeping track of deletion nodes &c, which can only be marked
+- as obsolete when the node which they mark as deleted has actually been
+- removed from the flash.
+-*/
+-struct jffs2_raw_node_ref_list {
+- struct jffs2_raw_node_ref *rew;
+- struct jffs2_raw_node_ref_list *next;
+-};
++ 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)
+
+ /* For each inode in the filesystem, we need to keep a record of
+ nlink, because it would be a PITA to scan the whole directory tree
+@@ -101,20 +115,30 @@
+ 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;
+ };
+
+-struct jffs2_scan_info {
+- struct jffs2_full_dirent *dents;
+- struct jffs2_tmp_dnode_info *tmpnodes;
+-};
++/* 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 INO_STATE_CLEARING 6 /* In clear_inode() */
+
-+ return ret;
-+}
++#define INOCACHE_HASHSIZE 128
+
-+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
-+ struct jffs2_inode_info *f)
-+{
-+ iput(OFNI_EDONI_2SFFJ(f));
-+}
+ /*
+ Larger representation of a raw node, kept in-core only when the
+ struct inode for this particular ino is instantiated.
+@@ -123,12 +147,11 @@
+ 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; /* The offset to which the data of this node belongs */
++ uint32_t size;
++ uint32_t frags; /* Number of fragments which currently refer
+ to this node. When this reaches zero,
+- the node is obsolete.
+- */
++ the node is obsolete. */
+ };
+
+ /*
+@@ -140,144 +163,265 @@
+ {
+ 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];
+ };
+
-+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
-+ int inum, int nlink)
+ /*
+ Fragments - used to build a map of which raw node to obtain
+ data from for each part of the ino
+ */
+ 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; /* The offset to which this fragment belongs */
+ };
+
+ 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;
+
+ struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
+-
+- /* For deletia. When a dirent node in this eraseblock is
+- deleted by a node elsewhere, that other node can only
+- be marked as obsolete when this block is actually erased.
+- So we keep a list of the nodes to mark as obsolete when
+- the erase is completed.
+- */
+- // MAYBE struct jffs2_raw_node_ref_list *deletia;
+ };
+
+ #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 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);
-+ }
++ struct jffs2_raw_node_ref *ref;
++ int i=0;
+
-+ return NULL;
++ 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);
+ }
-+ } 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);
++ printk("\n");
+}
+
-+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;
+ #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))) { \
++ if (!ref2->next_phys) \
++ printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
++ ref2, ref_offset(ref2), ref2->next_phys, \
++ jeb->last_node, ref_offset(jeb->last_node)); \
++ else \
++ 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;
+
-+ *priv = (unsigned long)pg;
-+ return kmap(pg);
++ 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);
+}
+
-+void jffs2_gc_release_page(struct jffs2_sb_info *c,
-+ unsigned char *ptr,
-+ unsigned long *priv)
++static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
++ struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *ref)
+{
-+ struct page *pg = (void *)*priv;
-+
-+ kunmap(pg);
-+ page_cache_release(pg);
-+}
++ uint32_t ret;
+
-+int jffs2_flash_setup(struct jffs2_sb_info *c) {
-+ int ret = 0;
-+
-+ if (jffs2_cleanmarker_oob(c)) {
-+ /* NAND flash... do setup accordingly */
-+ ret = jffs2_nand_flash_setup(c);
-+ if (ret)
-+ return 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();
++ })
+
-+ /* add setups for other bizarre flashes here... */
-+ if (jffs2_nor_ecc(c)) {
-+ ret = jffs2_nor_ecc_flash_setup(c);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* and Dataflash */
-+ if (jffs2_dataflash(c)) {
-+ ret = jffs2_dataflash_setup(c);
-+ if (ret)
-+ return ret;
++#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;
+}
+
-+void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
+
-+ if (jffs2_cleanmarker_oob(c)) {
-+ jffs2_nand_flash_cleanup(c);
-+ }
+ #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)
+
+-static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
++static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+ {
+ while(raw->next_in_ino) {
+ raw = raw->next_in_ino;
+ }
+
+- return ((struct jffs2_inode_cache *)raw)->ino;
++ 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;
+
-+ /* add cleanups for other bizarre flashes here... */
-+ if (jffs2_nor_ecc(c)) {
-+ jffs2_nor_ecc_flash_cleanup(c);
-+ }
-+
-+ /* and DataFlash */
-+ if (jffs2_dataflash(c)) {
-+ jffs2_dataflash_cleanup(c);
-+ }
++ if (!node)
++ return NULL;
++ while(node->rb_left)
++ node = node->rb_left;
++ return rb_entry(node, struct jffs2_node_frag, rb);
+}
---- linux-2.4.21/fs/jffs2/gc.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/gc.c
-@@ -1,76 +1,68 @@
++#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));
++D2(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,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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 +445,31 @@
+ /* 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);
+-
+-/* compr.c */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen);
+-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ 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);
+
+ /* 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);
+
+-/* compr_zlib.c */
+-int jffs2_zlib_init(void);
+-void jffs2_zlib_exit(void);
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++/* 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);
++#endif
++
++#endif /* __JFFS2_NODELIST_H__ */
+--- linux-2.4.21/fs/jffs2/nodemgmt.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodemgmt.c
+@@ -1,45 +1,21 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
*/
#include <linux/kernel.h>
- #include <linux/mtd/mtd.h>
#include <linux/slab.h>
-#include <linux/jffs2.h>
--#include <linux/sched.h>
+ #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
- #include <linux/pagemap.h>
-+#include <linux/crc32.h>
+#include <linux/compiler.h>
-+#include <linux/stat.h>
++#include <linux/sched.h> /* For cond_resched() */
#include "nodelist.h"
--#include "crc32.h"
-+#include "compr.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 +72,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 */
-+ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
- return NULL;
- }
+ /**
+@@ -62,53 +38,95 @@
+ * for the requested allocation.
+ */
-@@ -94,6 +93,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;
-+ }
-+
-+ D2(jffs2_dump_block_lists(c));
- return ret;
- }
+-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);
-@@ -103,21 +113,90 @@
- */
- int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+-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)
{
-- struct jffs2_eraseblock *jeb;
- struct jffs2_inode_info *f;
-+ struct jffs2_inode_cache *ic;
-+ struct jffs2_eraseblock *jeb;
- struct jffs2_raw_node_ref *raw;
-- struct jffs2_node_frag *frag;
-- struct jffs2_full_dnode *fn = NULL;
-- struct jffs2_full_dirent *fd;
-- __u32 start = 0, end = 0, nrfrags = 0;
-- __u32 inum;
-- struct inode *inode;
-- int ret = 0;
-+ int ret = 0, inum, nlink;
+ int ret = -EAGAIN;
+- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
++ int blocksneeded = c->resv_blocks_write;
+ /* align it */
+ minsize = PAD(minsize);
- if (down_interruptible(&c->alloc_sem))
- return -EINTR;
+- 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);
-+ 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);
-+ D2(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;
-+ }
++ spin_lock(&c->erase_completion_lock);
+
+- /* this needs a little more thought */
++ /* this needs a little more thought (true <tglx> :)) */
+ while(ret == -EAGAIN) {
+ while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+ int ret;
++ uint32_t dirty, avail;
+
-+ 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;
++ /* 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));
+
-+ 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();
++ 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);
+
-+ 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;
-@@ -126,13 +205,15 @@
- jeb = jffs2_find_gc_block(c);
++ 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 (!jeb) {
-- printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
-- spin_unlock_bh(&c->erase_completion_lock);
-+ D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
-+ spin_unlock(&c->erase_completion_lock);
- up(&c->alloc_sem);
- return -EIO;
- }
+- if (current->need_resched)
+- schedule();
++ cond_resched();
-- 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 (signal_pending(current))
+ return -EINTR;
- if (!jeb->used_size) {
- up(&c->alloc_sem);
-@@ -141,61 +222,215 @@
+ down(&c->alloc_sem);
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ }
- 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();
+ 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));
}
}
-- 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);
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ if (ret)
up(&c->alloc_sem);
- goto eraseit_lock;
- }
-
-- inum = jffs2_raw_ref_to_inum(raw);
-- D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
-+ ic = jffs2_raw_ref_to_ic(raw);
+ return ret;
+ }
-- spin_unlock_bh(&c->erase_completion_lock);
-+ /* 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);
+-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_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, inum));
+ 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;
+ }
-- inode = iget(OFNI_BS_2SFFJ(c), inum);
-- if (is_bad_inode(inode)) {
-- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
-- /* NB. This will happen again. We need to do something appropriate here. */
-+ 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.
-+ */
-+
-+ 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));
+ /* 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;
+ }
-+ 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.
-+ */
-+ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
-+ ic->ino, ic->state);
- up(&c->alloc_sem);
-- iput(inode);
-- return -EIO;
-+ 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;
++ 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 @@
-- f = JFFS2_INODE_INFO(inode);
-+ /* 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 (list_empty(&c->free_list)) {
+
+- DECLARE_WAITQUEUE(wait, current);
++ if (!c->nr_erasing_blocks &&
++ !list_empty(&c->erasable_list)) {
++ struct jffs2_eraseblock *ejeb;
+
-+ if (ic->state == INO_STATE_GC) {
-+ spin_unlock(&c->inocache_lock);
++ 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));
++ }
+
-+ ret = jffs2_garbage_collect_pristine(c, ic, raw);
++ 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_lock(&c->inocache_lock);
-+ ic->state = INO_STATE_CHECKEDABSENT;
-+ wake_up(&c->inocache_wq);
++ 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);
+
-+ if (ret != -EBADFD) {
-+ spin_unlock(&c->inocache_lock);
-+ goto release_sem;
-+ }
+ /* 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)) {
+
-+ /* Fall through if it wanted us to, with inocache_lock held */
++ 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);
+ }
+
-+ /* 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. */
+ 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,61 @@
+ * 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;
+
+- 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));
++ jeb = &c->blocks[new->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, new);
+
-+ inum = ic->ino;
-+ nlink = ic->nlink;
-+ spin_unlock(&c->inocache_lock);
++ 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)) {
++ /* we could get some obsolete nodes after nextblock was refiled
++ in wbuf.c */
++ if ((c->nextblock || !ref_obsolete(new))
++ &&(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);
+
-+ f = jffs2_gc_fetch_inode(c, inum, nlink);
-+ if (IS_ERR(f)) {
-+ ret = PTR_ERR(f);
-+ goto release_sem;
-+ }
-+ if (!f) {
-+ ret = 0;
-+ goto release_sem;
-+ }
+ 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) {
+
-+ ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++ if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_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);
++ }
+
-+ jffs2_gc_release_inode(c, f);
+ 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));
+
-+ release_sem:
-+ up(&c->alloc_sem);
++ spin_unlock(&c->erase_completion_lock);
+
+ return 0;
+ }
+@@ -280,20 +365,34 @@
+ up(&c->alloc_sem);
+ }
+
++static inline int on_list(struct list_head *obj, struct list_head *head)
++{
++ struct list_head *this;
+
-+ eraseit_lock:
-+ /* If we've finished this block, start it erasing */
-+ spin_lock(&c->erase_completion_lock);
++ list_for_each(this, head) {
++ if (this == obj) {
++ D1(printk("%p is on list at %p\n", obj, head));
++ return 1;
+
-+ 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;
++ return 0;
+}
+
-+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;
+ 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,91 +401,439 @@
+ BUG();
+ }
+ jeb = &c->blocks[blocknr];
+- if (jeb->used_size < ref->totlen) {
+
- down(&f->sem);
++ if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
++ !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
++ /* Hm. This may confuse static lock analysis. If any of the above
++ three conditions is false, we're going to return from this
++ function without actually obliterating any nodes or freeing
++ any jffs2_raw_node_refs. So we don't need to stop erases from
++ happening, or protect against people holding an obsolete
++ jffs2_raw_node_ref without the erase_completion_lock. */
++ down(&c->erase_free_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;
++ 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);
}
-+ spin_unlock(&c->erase_completion_lock);
+
+- 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;
++ // 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);
+
- /* 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;
-@@ -206,13 +441,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;
++ /* 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;
++ }
+ }
- /* 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;
- }
-@@ -224,12 +468,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;
-@@ -237,53 +482,207 @@
- }
- upnout:
- up(&f->sem);
-- up(&c->alloc_sem);
-- iput(inode);
++ } 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);
-- eraseit_lock:
-- /* If we've finished this block, start it erasing */
-- spin_lock_bh(&c->erase_completion_lock);
-+ return ret;
-+}
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
-- 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 (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+- /* Mount in progress. Don't muck about with the block
++ if (c->flags & JFFS2_SB_FLAG_SCANNING) {
++ /* Flash scanning is in progress. Don't muck about with the block
+ lists because they're not ready yet, and don't actually
+ 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);
++ /* We didn't lock the erase_free_sem */
+ return;
+ }
+
-+ if (alloclen < rawlen) {
-+ /* Doesn't fit untouched. We'll go the old route and split it */
-+ return -EBADFD;
+ 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);
-+ node = kmalloc(rawlen, GFP_KERNEL);
-+ if (!node)
-+ return -ENOMEM;
+- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
+- return;
+- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++ spin_unlock(&c->erase_completion_lock);
+
-+ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
-+ if (!ret && retlen != rawlen)
-+ ret = -EIO;
-+ if (ret)
-+ goto out_node;
++ if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
++ (c->flags & JFFS2_SB_FLAG_BUILDING)) {
++ /* We didn't lock the erase_free_sem */
+ 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);
++ /* The erase_free_sem is locked, and has been since before we marked the node obsolete
++ and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
++ the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
++ by jffs2_free_all_node_refs() in erase.c. Which is nice. */
+
-+ 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;
++ 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);
+- return;
++ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++ goto out_erase_sem;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+- return;
++ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++ goto out_erase_sem;
+ }
+- 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);
+- return;
++ 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));
++ goto out_erase_sem;
+ }
+- 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));
+- return;
++ 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)));
++ goto out_erase_sem;
+ }
+- 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);
+- return;
++ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++ goto out_erase_sem;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+- return;
++ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++ goto out_erase_sem;
+ }
+
-+ 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;
-+ }
++ /* Nodes which have been marked obsolete no longer need to be
++ associated with any inode. Remove them from the per-inode list.
++
++ Note we can't do this for NAND at the moment because we need
++ obsolete dirent nodes to stay on the lists, because of the
++ horridness in jffs2_garbage_collect_deletion_dirent(). Also
++ because we delete the inocache, and on NAND we need that to
++ stay around until all the nodes are actually erased, in order
++ to stop us from giving the same inode number to another newly
++ created inode. */
++ if (ref->next_in_ino) {
++ struct jffs2_inode_cache *ic;
++ struct jffs2_raw_node_ref **p;
+
-+ 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;
-+ }
-+ }
-+ break;
++ spin_lock(&c->erase_completion_lock);
+
-+ 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;
-+ }
++ ic = jffs2_raw_ref_to_ic(ref);
++ for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
++ ;
+
-+ 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;
-+ }
++ *p = ref->next_in_ino;
++ ref->next_in_ino = NULL;
+
-+ nraw = jffs2_alloc_raw_node_ref();
-+ if (!nraw) {
-+ ret = -ENOMEM;
-+ goto out_node;
-+ }
++ if (ic->nodes == (void *)ic)
++ jffs2_del_ino_cache(c, ic);
+
-+ /* 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;
++ spin_unlock(&c->erase_completion_lock);
+ }
+
-+ 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;
++ /* Merge with the next node in the physical list, if there is one
++ and if it's also obsolete and if it doesn't belong to any inode */
++ if (ref->next_phys && ref_obsolete(ref->next_phys) &&
++ !ref->next_phys->next_in_ino) {
++ struct jffs2_raw_node_ref *n = ref->next_phys;
++
++ spin_lock(&c->erase_completion_lock);
+
-+ 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);
++ ref->__totlen += n->__totlen;
++ ref->next_phys = n->next_phys;
++ if (jeb->last_node == n) jeb->last_node = ref;
++ if (jeb->gc_node == n) {
++ /* gc will be happy continuing gc on this node */
++ jeb->gc_node=ref;
+ }
-+ 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);
++ spin_unlock(&c->erase_completion_lock);
+
-+ if (!ret) {
-+ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
++ jffs2_free_raw_node_ref(n);
++ }
++
++ /* Also merge with the previous node in the list, if there is one
++ and that one is obsolete */
++ if (ref != jeb->first_node ) {
++ struct jffs2_raw_node_ref *p = jeb->first_node;
+
-+ ACCT_SANITY_CHECK(c,jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
++ spin_lock(&c->erase_completion_lock);
+
-+ goto retry;
++ while (p->next_phys != ref)
++ p = p->next_phys;
++
++ if (ref_obsolete(p) && !ref->next_in_ino) {
++ p->__totlen += ref->__totlen;
++ if (jeb->last_node == ref) {
++ jeb->last_node = p;
+ }
-+ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
-+ jffs2_free_raw_node_ref(nraw);
++ if (jeb->gc_node == ref) {
++ /* gc will be happy continuing gc on this node */
++ jeb->gc_node=p;
++ }
++ p->next_phys = ref->next_phys;
++ jffs2_free_raw_node_ref(ref);
+ }
-+
-+ jffs2_free_raw_node_ref(nraw);
-+ if (!ret)
-+ ret = -EIO;
-+ goto out_node;
++ spin_unlock(&c->erase_completion_lock);
+ }
-+ 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-core, the 'nraw' pointer is then
-+ going to be fetched from ic->nodes by our caller. */
-+ spin_lock(&c->erase_completion_lock);
-+ nraw->next_in_ino = ic->nodes;
-+ ic->nodes = nraw;
-+ spin_unlock(&c->erase_completion_lock);
-+
-+ 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) {
- printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
- return -ENOMEM;
- }
-- ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
-+ ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
- if (ret) {
- printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
- kfree(mdata);
-@@ -295,34 +694,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.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 = 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.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));
-@@ -333,41 +732,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));
-@@ -378,19 +776,97 @@
- }
-
- 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;
++ out_erase_sem:
++ up(&c->erase_free_sem);
++}
+
-+ /* 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);
++#if CONFIG_JFFS2_FS_DEBUG >= 2
++void jffs2_dump_block_lists(struct jffs2_sb_info *c)
++{
+
-+ 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;
++ 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);
+
-+ /* Any dirent with the same name is going to have the same length... */
-+ if (ref_totlen(c, NULL, raw) != rawlen)
-+ continue;
++ 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;
+
-+ /* Doesn't matter if there's one in the same erase block. We're going to
-+ delete it too at the same time. */
-+ if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
-+ continue;
++ 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;
+
-+ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
++ 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;
+
-+ /* 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 %u) reading header from obsolete node at %08x\n",
-+ retlen, rawlen, ref_offset(raw));
-+ continue;
-+ }
++ 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;
+
-+ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
-+ continue;
++ 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;
+
-+ /* If the name CRC doesn't match, skip */
-+ if (je32_to_cpu(rd->name_crc) != name_crc)
-+ continue;
++ 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;
+
-+ /* 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;
++ 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;
+
-+ /* OK, check the actual name now */
-+ if (memcmp(rd->name, fd->name, name_len))
-+ continue;
++ 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;
+
-+ /* 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);
++ 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;
+
-+ 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);
++ 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;
+
-+ return jffs2_garbage_collect_dirent(c, jeb, f, fd);
++ 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 */
+
-+ up(&c->erase_free_sem);
-+ kfree(rd);
++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;
+ }
+
-+ /* No need for it any more. Just mark it obsolete and remove it from the list */
- while (*fdp) {
- if ((*fdp) == fd) {
- found = 1;
-@@ -400,7 +876,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);
-@@ -408,93 +884,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.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 = inode->i_ino;
-- ri.version = ++f->highest_version;
-- ri.offset = start;
-- ri.dsize = end - start;
-- ri.csize = 0;
-+ 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);
-@@ -510,12 +988,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);
++ /* 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;
+
-+ 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) {
-@@ -540,49 +1023,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;
++ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
++ (dirty > c->nospc_dirty_size))
++ ret = 1;
+
-+ min = start & ~(PAGE_CACHE_SIZE-1);
-+ max = min + PAGE_CACHE_SIZE;
++ 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"));
+
-+ frag = jffs2_lookup_node_frag(&f->fragtree, start);
++ return ret;
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/os-linux.h
+@@ -0,0 +1,227 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2002-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
+
-+ /* BUG_ON(!frag) but that'll happen anyway... */
++#ifndef __JFFS2_OS_LINUX_H__
++#define __JFFS2_OS_LINUX_H__
++#include <linux/version.h>
+
-+ BUG_ON(frag->ofs != start);
++/* JFFS2 uses Linux mode bits natively -- no need for conversion */
++#define os_to_jffs2_mode(x) (x)
++#define jffs2_to_os_mode(x) (x)
+
-+ /* First grow down... */
-+ while((frag = frag_prev(frag)) && frag->ofs >= min) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define kstatfs statfs
++#endif
+
-+ /* 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 {
++struct kstatfs;
++struct kvec;
+
-+ /* 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. */
++#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
+
-+ struct jffs2_raw_node_ref *raw = frag->node->raw;
-+ struct jffs2_eraseblock *jeb;
+
-+ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++#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 (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;
-+ }
++#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
+
-+ 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;
-+ }
-+ }
++/* 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
+
-+ /* ... then up */
++#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
++}
+
-+ /* 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) {
++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
+
-+ /* 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;
-+ }
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
++#define jffs2_can_mark_obsolete(c) (1)
++#define jffs2_cleanmarker_oob(c) (0)
++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
+
-+ 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 {
++#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_write_nand_badblock(c,jeb,bad_offset) (1)
++#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
++#define jffs2_nor_ecc(c) (0)
++#define jffs2_dataflash(c) (0)
++#define jffs2_nor_ecc_flash_setup(c) (0)
++#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
+
-+ /* 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. */
++#else /* NAND and/or ECC'd NOR support present */
+
-+ struct jffs2_raw_node_ref *raw = frag->node->raw;
-+ struct jffs2_eraseblock *jeb;
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
++#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
+
-+ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++#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)
+
-+ 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;
-+ }
++/* wbuf.c */
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *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, uint32_t bad_offset);
++void jffs2_wbuf_timeout(unsigned long data);
++void jffs2_wbuf_process(void *data);
++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_nand_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
+
-+ 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));
++#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
+
-+ 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 */
-@@ -592,63 +1172,58 @@
- * 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;
-- char comprtype = JFFS2_COMPR_NONE;
-+ uint32_t datalen;
-+ uint32_t cdatalen;
-+ uint16_t 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);
-+ comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
-
-- 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;
-- ri.compr = comprtype;
-- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-- ri.data_crc = crc32(0, writebuf, 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));
-
-- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
-+ 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 & 0xff;
-+ ri.usercompr = (comprtype >> 8) & 0xff;
-+ 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);
++#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
++int jffs2_dataflash_setup(struct jffs2_sb_info *c);
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
+
-+ jffs2_free_comprbuf(comprbuf, writebuf);
-
- if (IS_ERR(new_fn)) {
- printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
-@@ -663,12 +1238,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;
- }
-
---- linux-2.4.21/fs/jffs2/ioctl.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/ioctl.c
-@@ -1,37 +1,13 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
-
-@@ -42,6 +18,6 @@
- {
- /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
- will include compression support etc. */
-- return -EINVAL;
-+ return -ENOTTY;
- }
-
---- linux-2.4.21/fs/jffs2/malloc.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/malloc.c
-@@ -1,37 +1,13 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
-
-@@ -47,6 +23,9 @@
- #define JFFS2_SLAB_POISON 0
- #endif
-
-+// replace this by #define D3 (x) x for cache debugging
-+#define D3(x)
++#endif /* WRITEBUFFER */
+
- /* 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);
- }
-
---- linux-2.4.21/fs/jffs2/nodelist.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodelist.c
-@@ -1,44 +1,24 @@
++/* 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);
++int jffs2_flash_setup(struct jffs2_sb_info *c);
++void jffs2_flash_cleanup(struct jffs2_sb_info *c);
++
++
++/* writev.c */
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
++ unsigned long count, loff_t to, size_t *retlen);
++
++
++#endif /* __JFFS2_OS_LINUX_H__ */
++
++
+--- linux-2.4.21/fs/jffs2/pushpull.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/pushpull.h
+@@ -1,42 +1,21 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
-- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- * Copyright (C) 2001 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
*
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ * Created by David Woodhouse <dwmw2@infradead.org>
*
*/
- #include <linux/kernel.h>
--#include <linux/jffs2.h>
-+#include <linux/sched.h>
- #include <linux/fs.h>
- #include <linux/mtd/mtd.h>
-+#include <linux/rbtree.h>
-+#include <linux/crc32.h>
-+#include <linux/slab.h>
-+#include <linux/pagemap.h>
- #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,93 +69,156 @@
- *prev = tn;
- }
+ #ifndef __PUSHPULL_H__
+ #define __PUSHPULL_H__
++
++#include <linux/errno.h>
++
+ struct pushpull {
+ unsigned char *buf;
+ unsigned int buflen;
+@@ -44,9 +23,36 @@
+ unsigned int reserve;
+ };
-+static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+-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)
+{
-+ struct jffs2_tmp_dnode_info *next;
++ pp->buf = buf;
++ pp->buflen = buflen;
++ pp->ofs = ofs;
++ pp->reserve = reserve;
++}
+
-+ while (tn) {
-+ next = tn;
-+ tn = tn->next;
-+ jffs2_free_full_dnode(next->fn);
-+ jffs2_free_tmp_dnode_info(next);
++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 void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++static inline int pushedbits(struct pushpull *pp)
+{
-+ struct jffs2_full_dirent *next;
++ return pp->ofs;
++}
+
+ static inline int pullbit(struct pushpull *pp)
+ {
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/rbtree.c
+@@ -0,0 +1,363 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++ (C) 2002 David Woodhouse <dwmw2@infradead.org>
++
++ 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.
+
-+ while (fd) {
-+ next = fd->next;
-+ jffs2_free_full_dirent(fd);
-+ fd = next;
++ 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$
++*/
++
++#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 <linux/version.h>
++#include <linux/rbtree.h>
++
++/* 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;
+}
+
-+/* Returns first valid node after 'ref'. May return 'ref' */
-+static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
+{
-+ while (ref && ref->next_in_ino) {
-+ if (!ref_obsolete(ref))
-+ return ref;
-+ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
-+ ref = ref->next_in_ino;
++ 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;
+ }
-+ return NULL;
++ else
++ root->rb_node = left;
++ node->rb_parent = left;
+}
+
- /* 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,
-+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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_raw_node_ref *ref, *valid_ref;
- struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
- struct jffs2_full_dirent *fd, *ret_fd = NULL;
--
- union jffs2_node_union node;
- size_t retlen;
- int err;
-
- *mctime_ver = 0;
-
-- 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);
-- }
-- 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) {
-- /* 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));
-- continue;
-- }
-- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
-+ D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
++void rb_insert_color(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * parent, * gparent;
+
-+ spin_lock(&c->erase_completion_lock);
++ while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
++ {
++ gparent = parent->rb_parent;
+
-+ valid_ref = jffs2_first_valid_node(f->inocache->nodes);
++ 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 (!valid_ref && (f->inocache->ino != 1))
-+ printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
++ if (parent->rb_right == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_left(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
+
-+ while (valid_ref) {
-+ /* 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. So if we mark 'ref' obsolete while we're
-+ not holding the lock, it can go away immediately. For that reason,
-+ we find the next valid node first, before processing 'ref'.
-+ */
-+ ref = valid_ref;
-+ valid_ref = jffs2_first_valid_node(ref->next_in_ino);
-+ spin_unlock(&c->erase_completion_lock);
++ 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;
++ }
++ }
+
-+ cond_resched();
++ if (parent->rb_left == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_right(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
+
-+ /* 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();
++ 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 (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();
++ 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;
+ }
-+
- fd = jffs2_alloc_full_dirent(node.d.nsize+1);
- if (!fd) {
- err = -ENOMEM;
- goto free_out;
- }
-- 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;
-@@ -188,6 +231,7 @@
- }
- fd->nhash = full_name_hash(fd->name, node.d.nsize);
- fd->next = NULL;
-+ fd->name[node.d.nsize] = '\0';
- /* Wheee. We now have a complete jffs2_full_dirent structure, with
- the name in it and everything. Link it into the list
- */
-@@ -196,21 +240,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 (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->flash_offset & 1) {
-- D1(printk(KERN_DEBUG "obsoleted\n"));
-- /* Obsoleted */
-+ 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();
++ 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;
+ }
-+
-+ /* 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;
++ 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;
++}
+
-+ 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
++void rb_erase(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * child, * parent;
++ int color;
+
-+ 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;
- }
-+
-+ }
++ if (!node->rb_left)
++ child = node->rb_right;
++ else if (!node->rb_right)
++ child = node->rb_left;
++ else
++ {
++ struct rb_node * old = node, * left;
+
-+ /* 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);
++ node = node->rb_right;
++ while ((left = node->rb_left))
++ node = left;
++ child = node->rb_right;
++ parent = node->rb_parent;
++ color = node->rb_color;
+
-+ jeb->used_size += len;
-+ jeb->unchecked_size -= len;
-+ c->used_size += len;
-+ c->unchecked_size -= len;
++ 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 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 (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 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);
-+ }
++ 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;
+
- tn = jffs2_alloc_tmp_dnode_info();
- if (!tn) {
- D1(printk(KERN_DEBUG "alloc tn failed\n"));
-@@ -225,36 +374,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;
++ old->rb_left->rb_parent = node;
++ if (old->rb_right)
++ old->rb_right->rb_parent = node;
++ goto color;
++ }
+
-+ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
-+ je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ parent = node->rb_parent;
++ color = node->rb_color;
+
-+ /* 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);
++ 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;
+
-+ jeb->used_size += len;
-+ jeb->unchecked_size -= len;
-+ c->used_size += len;
-+ c->unchecked_size -= len;
++ color:
++ if (color == RB_BLACK)
++ __rb_erase_color(child, parent, root);
++}
++#endif /* Before 2.4.11 */
+
-+ 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;
- }
++ /* 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;
++ }
+
- }
-+ spin_lock(&c->erase_completion_lock);
++ /* 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;
+
- }
-+ spin_unlock(&c->erase_completion_lock);
- *tnp = ret_tn;
- *fdp = ret_fd;
-
-@@ -266,19 +455,30 @@
- return err;
- }
-
-+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
++ return node->rb_parent;
++}
++
++struct rb_node *rb_prev(struct rb_node *node)
+{
-+ spin_lock(&c->inocache_lock);
-+ ic->state = state;
-+ wake_up(&c->inocache_wq);
-+ spin_unlock(&c->inocache_lock);
++ 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;
+}
+
-+/* 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)
++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;
++}
+--- linux-2.4.21/fs/jffs2/read.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/read.c
+@@ -1,52 +1,32 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
+
+-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ struct jffs2_full_dnode *fd, unsigned char *buf,
++ int ofs, int len)
{
- struct jffs2_inode_cache *ret;
+ 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 +35,41 @@
+ if (!ri)
+ return -ENOMEM;
- 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;
+- 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 +86,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 +109,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;
}
-
-- spin_unlock(&c->inocache_lock);
--
- if (ret && ret->ino != ino)
- ret = NULL;
-
-@@ -290,6 +490,8 @@
- {
- struct jffs2_inode_cache **prev;
- D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
-+ BUG_ON(!new->ino);
-+
- spin_lock(&c->inocache_lock);
-
- prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
-@@ -299,13 +501,14 @@
+ 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(c, f, ri->compr | (ri->usercompr << 8), 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;
+ }
}
- new->next = *prev;
- *prev = new;
-+
- spin_unlock(&c->inocache_lock);
- }
- void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
- {
- struct jffs2_inode_cache **prev;
-- D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
-+ D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
- spin_lock(&c->inocache_lock);
-
- prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
-@@ -316,6 +519,15 @@
- if ((*prev) == old) {
- *prev = old->next;
+- if (len < ri->dsize) {
++ if (len < je32_to_cpu(ri->dsize)) {
+ memcpy(buf, decomprbuf+ofs, len);
}
-+
-+ /* Free it now unless it's in READING or CLEARING state, which
-+ are the transitions upon read_inode() and clear_inode(). The
-+ rest of the time we know nobody else is looking at it, and
-+ if it's held by read_inode() or clear_inode() they'll free it
-+ for themselves. */
-+ if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
-+ jffs2_free_inode_cache(old);
-+
- spin_unlock(&c->inocache_lock);
- }
+ out_decomprbuf:
+@@ -161,3 +151,66 @@
-@@ -328,7 +540,6 @@
- this = c->inocache_list[i];
- while (this) {
- next = this->next;
-- D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
- jffs2_free_inode_cache(this);
- this = next;
- }
-@@ -352,3 +563,128 @@
- }
+ return ret;
}
-
-+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)
++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;
-+ struct jffs2_node_frag *parent;
++ int ret;
+
-+ if (!root->rb_node)
-+ return;
++ D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
++ f->inocache->ino, offset, offset+len));
+
-+ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
++ frag = jffs2_lookup_node_frag(&f->fragtree, offset);
+
-+ 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);
++ /* 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);
++ D2(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;
-+ }
-+ 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);
++ } 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;
-+ }
-+
-+ 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);
++ } else {
++ uint32_t readlen;
++ uint32_t fragofs; /* offset within the frag to start reading */
+
-+ 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;
++ 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, f, 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"));
+ }
-+
-+ jffs2_free_node_frag(frag);
-+ frag = parent;
-+
-+ cond_resched();
+ }
++ return 0;
+}
+
-+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);
-+}
---- linux-2.4.21/fs/jffs2/nodelist.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodelist.h
-@@ -1,48 +1,35 @@
+--- linux-2.4.21/fs/jffs2/readinode.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/readinode.c
+@@ -1,79 +1,124 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
*
*/
-+#ifndef __JFFS2_NODELIST_H__
-+#define __JFFS2_NODELIST_H__
-+
- #include <linux/config.h>
+-/* Given an inode, probably with existing list of fragments, add the new node
+- * to the fragment list.
+- */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
#include <linux/fs.h>
--
-+#include <linux/types.h>
-+#include <linux/jffs2.h>
- #include <linux/jffs2_fs_sb.h>
- #include <linux/jffs2_fs_i.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include "crc32.h"
-+#ifdef __ECOS
-+#include "os-ecos.h"
-+#else
-+#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
-+#include "os-linux.h"
++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 >= 2
++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 (f->metadata) {
+- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
++ 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);
+
+-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
++ if (f->metadata) {
++ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
++ }
++}
+#endif
+
- #ifndef CONFIG_JFFS2_FS_DEBUG
--#define CONFIG_JFFS2_FS_DEBUG 2
-+#define CONFIG_JFFS2_FS_DEBUG 1
- #endif
++#if CONFIG_JFFS2_FS_DEBUG >= 1
++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 +127,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 +167,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 && (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);
+ }
+- newfrag->next = NULL;
+- *prev = newfrag;
++ rb_insert_color(&holefrag->rb, list);
++ this = holefrag;
++ }
++ 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;
+ }
- #if CONFIG_JFFS2_FS_DEBUG > 0
-@@ -57,6 +44,39 @@
- #define D2(x)
- #endif
+- 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));
-+#define JFFS2_NATIVE_ENDIAN
+- /* 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) {
+
-+/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
-+ whatever OS we're actually running on here too. */
++ /* 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 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)})
++ 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;
+
-+#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))})
++ /* Adjust size of original 'this' */
+ this->size = newfrag->ofs - this->ofs;
+
-+#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))})
++ /* 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;
+
-+#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
++ /* 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;
+
- /*
- This is all we need to keep in-core for each raw node during normal
- operation. As and when we do read_inode on a particular inode, we can
-@@ -71,27 +91,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.
-- */
--};
--
--/*
-- Used for keeping track of deletion nodes &c, which can only be marked
-- as obsolete when the node which they mark as deleted has actually been
-- removed from the flash.
--*/
--struct jffs2_raw_node_ref_list {
-- struct jffs2_raw_node_ref *rew;
-- struct jffs2_raw_node_ref_list *next;
--};
-+ 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)
-
- /* For each inode in the filesystem, we need to keep a record of
- nlink, because it would be a PITA to scan the whole directory tree
-@@ -101,20 +115,30 @@
- 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;
- };
++ 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;
--struct jffs2_scan_info {
-- struct jffs2_full_dirent *dents;
-- struct jffs2_tmp_dnode_info *tmpnodes;
--};
-+/* 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 INO_STATE_CLEARING 6 /* In clear_inode() */
+ 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;
+
-+#define INOCACHE_HASHSIZE 128
++ /* 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);
+
- /*
- Larger representation of a raw node, kept in-core only when the
- struct inode for this particular ino is instantiated.
-@@ -123,12 +147,11 @@
- 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; /* The offset to which the data of this node belongs */
-+ uint32_t size;
-+ uint32_t frags; /* Number of fragments which currently refer
- to this node. When this reaches zero,
-- the node is obsolete.
-- */
-+ the node is obsolete. */
- };
+ return 0;
+ }
- /*
-@@ -140,144 +163,265 @@
+-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_tmp_dnode_info *next;
- struct jffs2_full_dnode *fn;
-- __u32 version;
-+ uint32_t version;
- };
++ struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
++
+ D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
- 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];
- };
+- 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;
++ /* 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;
+ }
+- list = &(*list)->next;
++ frag = frag_next(frag);
++ }
++ while (frag && frag->ofs >= size) {
++ struct jffs2_node_frag *next = frag_next(frag);
+
- /*
- Fragments - used to build a map of which raw node to obtain
- data from for each part of the ino
- */
- 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; /* The offset to which this fragment belongs */
- };
++ 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;
+ }
+ }
- 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 */
+ /* Scan the list of all nodes present for this ino, build map of versions, etc. */
-- __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;
+-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"));
- struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
--
-- /* For deletia. When a dirent node in this eraseblock is
-- deleted by a node elsewhere, that other node can only
-- be marked as obsolete when this block is actually erased.
-- So we keep a list of the nodes to mark as obsolete when
-- the erase is completed.
-- */
-- // MAYBE struct jffs2_raw_node_ref_list *deletia;
- };
+- 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);
- #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)
+- f = JFFS2_INODE_INFO(inode);
+- c = JFFS2_SB_INFO(inode->i_sb);
++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
-+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))) { \
-+ if (!ref2->next_phys) \
-+ printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
-+ ref2, ref_offset(ref2), ref2->next_phys, \
-+ jeb->last_node, ref_offset(jeb->last_node)); \
-+ else \
-+ 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)
+- 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));
++ 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;
-+/* 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];
+- if (!f->inocache && inode->i_ino == 1) {
++ 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;
+
-+ /* Last node in block. Use free_space */
-+ BUG_ON(ref != jeb->last_node);
-+ ref_end = jeb->offset + c->sector_size - jeb->free_size;
++ 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;
++
++ default:
++ BUG();
++ }
+ }
-+ return ref_end - ref_offset(ref);
++ spin_unlock(&c->inocache_lock);
++
++ 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);
+}
+
-+static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
-+ struct jffs2_eraseblock *jeb,
-+ struct jffs2_raw_node_ref *ref)
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
-+ uint32_t ret;
++ struct jffs2_raw_inode n;
++ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
++ int 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 (!f)
++ return -ENOMEM;
+
-+#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();
++ 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);
+ }
-+#endif
++ 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;
+
- #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 */
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
--#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))
+ /* 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, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
-+/* check if dirty space is more than 255 Byte */
-+#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
+ 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;
- #define PAD(x) (((x)+3)&~3)
+@@ -304,219 +527,217 @@
--static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
-+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
- {
- while(raw->next_in_ino) {
- raw = raw->next_in_ino;
- }
+ fn = tn->fn;
-- return ((struct jffs2_inode_cache *)raw)->ino;
-+ return ((struct jffs2_inode_cache *)raw);
- }
+- 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;
++ }
+ }
-+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);
+ 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));
+
- /* nodelist.c */
--D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
-+D2(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,
-+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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);
+ 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;
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ return -EIO;
+ }
+- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
++ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
+ }
+- 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;
++ 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;
+ }
- /* 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);
+- 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;
++ 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;
+ }
- /* 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);
-+
+- 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;
++ 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;
+ }
- /* 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);
+- /* 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;
++ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
++ case S_IFDIR:
++ 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. */
++ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+ }
++ break;
+
+- inode->i_blksize = PAGE_SIZE;
+- inode->i_blocks = (inode->i_size + 511) >> 9;
+
+- switch (inode->i_mode & S_IFMT) {
+- unsigned short rdev;
++ case S_IFREG:
++ /* 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;
- /* 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);
+ 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;
++ if (!je32_to_cpu(latest_node->isize))
++ latest_node->isize = latest_node->dsize;
+
+- case S_IFDIR:
+- if (mctime_ver > 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;
++ if (f->inocache->state != INO_STATE_CHECKING) {
++ /* Symlink's inode data is the target path. Read it and
++ * keep in RAM to facilitate quick follow symlink operation.
++ * We use f->dents field to store the target path, which
++ * is somewhat ugly. */
++ f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
++ if (!f->dents) {
++ printk(KERN_WARNING "Can't allocate %d bytes of memory "
++ "for the symlink target path cache\n",
++ je32_to_cpu(latest_node->csize));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -ENOMEM;
+ }
+- inode->i_op = &jffs2_dir_inode_operations;
+- inode->i_fop = &jffs2_dir_operations;
+- break;
-@@ -301,54 +445,31 @@
- /* gc.c */
- int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+- 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;
++ ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
++ je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
++
++ if (ret || retlen != je32_to_cpu(latest_node->csize)) {
++ if (retlen != je32_to_cpu(latest_node->csize))
++ ret = -EIO;
++ kfree(f->dents);
++ f->dents = NULL;
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -ret;
++ }
++
++ ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
++ (char *)f->dents));
++ }
++
++ /* fall through... */
--/* 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;
+ 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;
+ }
-
--/* 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);
+- 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;
-
--/* ioctl.c */
--int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+- 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);
+- /* I don't think we care about the potential race due to reading this
+- without f->sem. It can never get undeleted. */
+- int deleted = f->inocache && !f->inocache->nlink;
-
- /* read.c */
--int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
-
--/* compr.c */
--unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
-- __u32 *datalen, __u32 *cdatalen);
--int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
-- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
-+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-+ 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);
+- /* If it's a deleted inode, grab the alloc_sem. This prevents
+- jffs2_garbage_collect_pass() from deciding that it wants to
+- garbage collect one of the nodes we're just about to mark
+- obsolete -- by the time we drop alloc_sem and return, all
+- the nodes are marked obsolete, and jffs2_g_c_pass() won't
+- call iget() for the inode in question.
+- */
+- if (deleted)
+- down(&c->alloc_sem);
++ int deleted;
- /* scan.c */
- int jffs2_scan_medium(struct jffs2_sb_info *c);
-+void jffs2_rotate_lists(struct jffs2_sb_info *c);
+ down(&f->sem);
++ deleted = f->inocache && !f->inocache->nlink;
++
++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
- /* 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);
+- 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);
+ }
- /* 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);
+- 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));
+-
+- 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);
++ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
--/* compr_zlib.c */
--int jffs2_zlib_init(void);
--void jffs2_zlib_exit(void);
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+/* 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);
-+#endif
+- jffs2_free_full_dnode(frag->node);
+- }
+- jffs2_free_node_frag(frag);
++ /* For symlink inodes we us f->dents to store the target path name */
++ if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
++ if (f->dents) {
++ kfree(f->dents);
++ f->dents = NULL;
+ }
++ } else {
++ fds = f->dents;
+
-+#endif /* __JFFS2_NODELIST_H__ */
---- linux-2.4.21/fs/jffs2/nodemgmt.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodemgmt.c
-@@ -1,45 +1,21 @@
+ while(fds) {
+ fd = fds;
+ fds = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
++ }
+
+- up(&f->sem);
+-
+- if(deleted)
+- up(&c->alloc_sem);
+-};
++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ if (f->inocache->nodes == (void *)f->inocache)
++ jffs2_del_ino_cache(c, f->inocache);
++ }
+
++ up(&f->sem);
++}
+--- linux-2.4.21/fs/jffs2/scan.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/scan.c
+@@ -1,47 +1,25 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
+ * $Id$
*
*/
-
#include <linux/kernel.h>
++#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/jffs2.h>
#include <linux/mtd/mtd.h>
--#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
++#include <linux/crc32.h>
+#include <linux/compiler.h>
-+#include <linux/sched.h> /* For cond_resched() */
#include "nodelist.h"
+-#include "crc32.h"
- /**
-@@ -62,53 +38,95 @@
- * for the requested allocation.
++#define DEFAULT_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,96 @@
+ } 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_WRITEBUFFER
++ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
++ return c->wbuf_pagesize;
++#endif
++ return min;
--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);
++}
++
++static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
++ if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
++ return sector_size;
++ else
++ return DEFAULT_EMPTY_SCAN_SIZE;
++}
--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 jffs2_scan_medium(struct jffs2_sb_info *c)
{
- int ret = -EAGAIN;
-- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
-+ int blocksneeded = c->resv_blocks_write;
- /* align it */
- minsize = PAD(minsize);
+ int i, ret;
+- __u32 empty_blocks = 0;
++ uint32_t empty_blocks = 0, bad_blocks = 0;
++ unsigned char *flashbuf = NULL;
++ uint32_t buf_size = 0;
++#ifndef __ECOS
++ size_t pointlen;
-- 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);
+- if (!c->blocks) {
+- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+- return -EINVAL;
++ 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;
++
++ /* Respect kmalloc limitations */
++ if (buf_size > 128*1024)
++ buf_size = 128*1024;
++
++ 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; i<c->nr_blocks; i++) {
+ struct jffs2_eraseblock *jeb = &c->blocks[i];
- D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+- 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;
-- spin_lock_bh(&c->erase_completion_lock);
-+ spin_lock(&c->erase_completion_lock);
+ ACCT_PARANOIA_CHECK(jeb);
-- /* this needs a little more thought */
-+ /* this needs a little more thought (true <tglx> :)) */
- while(ret == -EAGAIN) {
- while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
- int ret;
-+ uint32_t dirty, avail;
+ /* 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 +142,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;
+
-+ /* 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;
++ case BLK_STATE_CLEANMARKER:
+ /* Only a CLEANMARKER node is valid */
+ if (!jeb->dirty_size) {
+ /* It's actually free */
+@@ -118,74 +159,224 @@
+ 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) &&
++ /* We want to remember the block with most free space
++ and stick it in the 'nextblock' position to start writing to it. */
++ 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);
++ }
+ }
-+ 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));
+ 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_WRITEBUFFER
++ 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 && ((c->nr_free_blocks+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;
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++ 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_WRITEBUFFER
++ 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;
+
-+ spin_unlock(&c->erase_completion_lock);
-+ up(&c->alloc_sem);
-+ return -ENOSPC;
++ if (!buf_size) {
++ buf_len = c->sector_size;
++ } else {
++ buf_len = EMPTY_SCAN_SIZE(c->sector_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(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
++ ofs += 4;
++
++ if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++ 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;
++ default: return ret;
+ }
-+
-+ /* 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;
-+ }
++ }
++#endif
+ D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+- return 1; /* special return code */
++ if (c->cleanmarker_size == 0)
++ return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */
++ else
++ 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;
-+ 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);
++scan_more:
+ while(ofs < jeb->offset + c->sector_size) {
+- ssize_t retlen;
+- ACCT_PARANOIA_CHECK(jeb);
+
-+ up(&c->alloc_sem);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
-+ 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);
++ 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;
}
-
- 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));
+ if (ofs == prevofs) {
+@@ -196,102 +387,183 @@
+ }
+ 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;
}
- }
-- 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));
+- 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;
++ uint32_t empty_start;
++
++ empty_start = ofs;
++ ofs += 4;
++
++ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
++ more_empty:
++ inbuf_ofs = ofs - buf_ofs;
++ while (inbuf_ofs < buf_len) {
++ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
++ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
++ empty_start, ofs);
++ DIRTY_SPACE(ofs-empty_start);
++ goto scan_more;
+ }
-- 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));
+- 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;
+- continue;
++ inbuf_ofs+=4;
++ ofs += 4;
}
- }
-- spin_unlock_bh(&c->erase_completion_lock);
-+ spin_unlock(&c->erase_completion_lock);
- return ret;
- }
++ /* Ran off end. */
++ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
- /* 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);
+- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
++ /* If we're only checking the beginning of a block with a cleanmarker,
++ bail now */
++ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
++ c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
++ D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
++ return BLK_STATE_CLEANMARKER;
+ }
-+ } 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);
++
++ /* See how much more there is to read in this eraseblock... */
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ if (!buf_len) {
++ /* No more to read. Break out of main loop without marking
++ this range of empty space as dirty (because it's not) */
++ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
++ empty_start));
++ break;
++ }
++ D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
++ return err;
++ buf_ofs = ofs;
++ goto more_empty;
+ }
- c->nextblock = jeb = NULL;
- }
-
-@@ -164,33 +209,44 @@
++
++ 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 (list_empty(&c->free_list)) {
+- 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;
+ }
-- 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));
-+ }
+- 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;
++ }
+
-+ 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;
++ 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;
- 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;
+ 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 +572,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);
}
-- /* 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)) {
+- ofs += PAD(sizeof(struct jffs2_unknown_node));
++ break;
+
-+ 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;
++ 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;
+ }
}
-@@ -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;
+ }
+- 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;
++ 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));
+
+- 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;
++ /* 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;
+ }
+- 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;
+- }
++ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
++ && (!jeb->first_node || !jeb->first_node->next_phys) )
++ return BLK_STATE_CLEANMARKER;
+
+- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+- kfree(buf);
+- *startofs = curofs;
+- return 0;
++ /* 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;
}
-@@ -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,61 @@
- * 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)
+
+-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_eraseblock *jeb;
-+ uint32_t len;
+ struct jffs2_inode_cache *ic;
-- 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));
-+ jeb = &c->blocks[new->flash_offset / c->sector_size];
-+ len = ref_totlen(c, jeb, new);
-+
-+ 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)) {
-+ /* we could get some obsolete nodes after nextblock was refiled
-+ in wbuf.c */
-+ if ((c->nextblock || !ref_obsolete(new))
-+ &&(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;
+@@ -399,137 +653,77 @@
+ if (ic)
+ return ic;
-- 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 (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_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);
-+ }
++ if (ino > c->highest_ino)
++ c->highest_ino = ino;
+
- list_add_tail(&jeb->list, &c->clean_list);
- c->nextblock = NULL;
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+ printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+ return NULL;
}
- ACCT_SANITY_CHECK(c,jeb);
-- ACCT_PARANOIA_CHECK(jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
+ 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));
+
-+ spin_unlock(&c->erase_completion_lock);
-
- return 0;
- }
-@@ -280,20 +365,34 @@
- up(&c->alloc_sem);
+ 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 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)
+-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_eraseblock *jeb;
- int blocknr;
- struct jffs2_unknown_node n;
+ 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;
-+ 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,91 +401,439 @@
- BUG();
- }
- jeb = &c->blocks[blocknr];
-- if (jeb->used_size < ref->totlen) {
-+
-+ if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
-+ !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
-+ /* Hm. This may confuse static lock analysis. If any of the above
-+ three conditions is false, we're going to return from this
-+ function without actually obliterating any nodes or freeing
-+ any jffs2_raw_node_refs. So we don't need to stop erases from
-+ happening, or protect against people holding an obsolete
-+ jffs2_raw_node_ref without the erase_completion_lock. */
-+ down(&c->erase_free_sem);
-+ }
-+
-+ 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);
- }
+-
+- 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;
+- }
+-
+- /* 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;
+-
+- 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;
+- }
++ uint32_t ino = je32_to_cpu(ri->ino);
-- 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;
-+ // 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;
-+ }
-+ }
-+ } 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);
+- if (ri.csize) {
+- /* Check data CRC too */
+- unsigned char *dbuf;
+- __u32 crc;
++ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
-- ACCT_PARANOIA_CHECK(jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
+- 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;
+- }
+- }
++ /* 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 (c->flags & JFFS2_SB_FLAG_MOUNTING) {
-- /* Mount in progress. Don't muck about with the block
-+ if (c->flags & JFFS2_SB_FLAG_SCANNING) {
-+ /* Flash scanning is in progress. Don't muck about with the block
- lists because they're not ready yet, and don't actually
- 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);
-+ /* We didn't lock the erase_free_sem */
- return;
+- /* 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);
+
- 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));
++ 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;
}
-- spin_unlock_bh(&c->erase_completion_lock);
-
-- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
-- return;
-- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
-+ spin_unlock(&c->erase_completion_lock);
-+
-+ if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
-+ (c->flags & JFFS2_SB_FLAG_BUILDING)) {
-+ /* We didn't lock the erase_free_sem */
- 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);
-+ /* The erase_free_sem is locked, and has been since before we marked the node obsolete
-+ and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
-+ the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
-+ by jffs2_free_all_node_refs() in erase.c. Which is nice. */
+- /* Build the data structures and file them for later */
+- raw->flash_offset = *ofs;
+- raw->totlen = PAD(ri.totlen);
++ /* Wheee. It worked */
+
-+ 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);
-- return;
-+ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-+ goto out_erase_sem;
- }
- if (retlen != sizeof(n)) {
-- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
-- return;
-+ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-+ goto out_erase_sem;
++ 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 +732,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;
+-
+- 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) {
++ 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)));
+
+- 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));
++ pseudo_random += je32_to_cpu(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;
}
-- 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);
-- return;
-+ 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));
-+ goto out_erase_sem;
+
+- 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 (!(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));
-- return;
-+ 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)));
-+ goto out_erase_sem;
+- 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;
}
-- 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);
-- return;
-+ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-+ goto out_erase_sem;
+ raw = jffs2_alloc_raw_node_ref();
+@@ -674,15 +790,15 @@
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+ return -ENOMEM;
}
- if (retlen != sizeof(n)) {
-- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
-- return;
-+ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-+ goto out_erase_sem;
-+ }
-+
-+ /* Nodes which have been marked obsolete no longer need to be
-+ associated with any inode. Remove them from the per-inode list.
-+
-+ Note we can't do this for NAND at the moment because we need
-+ obsolete dirent nodes to stay on the lists, because of the
-+ horridness in jffs2_garbage_collect_deletion_dirent(). Also
-+ because we delete the inocache, and on NAND we need that to
-+ stay around until all the nodes are actually erased, in order
-+ to stop us from giving the same inode number to another newly
-+ created inode. */
-+ if (ref->next_in_ino) {
-+ struct jffs2_inode_cache *ic;
-+ struct jffs2_raw_node_ref **p;
-+
-+ spin_lock(&c->erase_completion_lock);
-+
-+ ic = jffs2_raw_ref_to_ic(ref);
-+ for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
-+ ;
-+
-+ *p = ref->next_in_ino;
-+ ref->next_in_ino = NULL;
-+
-+ if (ic->nodes == (void *)ic)
-+ jffs2_del_ino_cache(c, ic);
-+
-+ spin_unlock(&c->erase_completion_lock);
+- 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;
}
-+
-+
-+ /* Merge with the next node in the physical list, if there is one
-+ and if it's also obsolete and if it doesn't belong to any inode */
-+ if (ref->next_phys && ref_obsolete(ref->next_phys) &&
-+ !ref->next_phys->next_in_ino) {
-+ struct jffs2_raw_node_ref *n = ref->next_phys;
-+
-+ spin_lock(&c->erase_completion_lock);
-+
-+ ref->__totlen += n->__totlen;
-+ ref->next_phys = n->next_phys;
-+ if (jeb->last_node == n) jeb->last_node = ref;
-+ if (jeb->gc_node == n) {
-+ /* gc will be happy continuing gc on this node */
-+ jeb->gc_node=ref;
-+ }
-+ spin_unlock(&c->erase_completion_lock);
-+
-+ jffs2_free_raw_node_ref(n);
-+ }
-+
-+ /* Also merge with the previous node in the list, if there is one
-+ and that one is obsolete */
-+ if (ref != jeb->first_node ) {
-+ struct jffs2_raw_node_ref *p = jeb->first_node;
-+
-+ spin_lock(&c->erase_completion_lock);
-+
-+ while (p->next_phys != ref)
-+ p = p->next_phys;
-+
-+ if (ref_obsolete(p) && !ref->next_in_ino) {
-+ p->__totlen += ref->__totlen;
-+ if (jeb->last_node == ref) {
-+ jeb->last_node = p;
-+ }
-+ if (jeb->gc_node == ref) {
-+ /* gc will be happy continuing gc on this node */
-+ jeb->gc_node=p;
-+ }
-+ p->next_phys = ref->next_phys;
-+ jffs2_free_raw_node_ref(ref);
-+ }
-+ spin_unlock(&c->erase_completion_lock);
+
+- 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 +808,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 +838,90 @@
+ struct list_head *n = head->next;
+
+ list_del(head);
+- while(count--)
++ while(count--) {
+ n = n->next;
+ }
-+ out_erase_sem:
-+ up(&c->erase_free_sem);
-+}
-+
-+#if CONFIG_JFFS2_FS_DEBUG >= 2
-+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);
+ 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));
+
-+ 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;
++ rotate_list((&c->clean_list), rotateby);
+
-+ 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");
++ 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 {
-+ 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);
++ D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
+ }
-+ 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;
++ 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));
+
-+ 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;
++ rotate_list((&c->very_dirty_list), rotateby);
+
-+ 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");
++ 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 {
-+ 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);
-+ }
++ D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
+ }
-+ if (list_empty(&c->erasable_pending_wbuf_list)) {
-+ printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\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));
+
+- if (c->nr_erasing_blocks)
+- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
++ rotate_list((&c->dirty_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 dirty_list is at %08x\n",
++ list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
+ } 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);
-+ }
++ D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
+ }
-+ 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;
++ x = count_list(&c->erasable_list);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
+
-+ 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;
++ rotate_list((&c->erasable_list), rotateby);
+
-+ 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);
-+ }
++ 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"));
+ }
-+}
-+#endif /* CONFIG_JFFS2_FS_DEBUG */
+
-+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
-+{
-+ int ret = 0;
-+ uint32_t dirty;
++ if (c->nr_erasing_blocks) {
++ rotateby = pseudo_random % c->nr_erasing_blocks;
++ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
+
-+ 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;
-+ }
++ rotate_list((&c->erase_pending_list), rotateby);
+
-+ /* 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;
++ 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 + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
-+ (dirty > c->nospc_dirty_size))
-+ ret = 1;
++ if (c->nr_free_blocks) {
++ rotateby = pseudo_random % c->nr_free_blocks;
++ D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
+
-+ 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"));
++ rotate_list((&c->free_list), rotateby);
+
-+ return ret;
++ 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"));
++ }
}
--- /dev/null
-+++ linux-2.4.21/fs/jffs2/os-linux.h
-@@ -0,0 +1,227 @@
++++ linux-2.4.21/fs/jffs2/super-v24.c
+@@ -0,0 +1,170 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
-+ * Copyright (C) 2002-2003 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ *
+ */
+
-+#ifndef __JFFS2_OS_LINUX_H__
-+#define __JFFS2_OS_LINUX_H__
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
+#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/jffs2.h>
++#include <linux/pagemap.h>
++#include <linux/mtd/mtd.h>
++#include "compr.h"
++#include "nodelist.h"
+
-+/* 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
-+
-+struct kstatfs;
-+struct kvec;
-+
-+#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)
++#ifndef MTD_BLOCK_MAJOR
++#define MTD_BLOCK_MAJOR 31
+#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 void jffs2_put_super (struct super_block *);
+
-+static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
++static struct super_operations jffs2_super_operations =
+{
-+#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)
-+#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
++ .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,
++};
+
-+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
-+#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
-+#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_write_nand_badblock(c,jeb,bad_offset) (1)
-+#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
-+#define jffs2_nor_ecc(c) (0)
-+#define jffs2_dataflash(c) (0)
-+#define jffs2_nor_ecc_flash_setup(c) (0)
-+#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++{
++ struct jffs2_sb_info *c;
++ int ret;
+
-+#else /* NAND and/or ECC'd NOR support present */
++ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
+
-+#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
-+#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
-+#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
++ 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;
++ }
+
-+#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)
++ c = JFFS2_SB_INFO(sb);
++ memset(c, 0, sizeof(*c));
+
-+/* wbuf.c */
-+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *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, uint32_t bad_offset);
-+void jffs2_wbuf_timeout(unsigned long data);
-+void jffs2_wbuf_process(void *data);
-+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_nand_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
++ sb->s_op = &jffs2_super_operations;
+
-+#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
-+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
++ 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;
++ }
+
-+#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
-+int jffs2_dataflash_setup(struct jffs2_sb_info *c);
-+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
++ ret = jffs2_do_fill_super(sb, data, silent);
++ if (ret) {
++ put_mtd_device(c->mtd);
++ return NULL;
++ }
+
-+#endif /* WRITEBUFFER */
++ return sb;
++}
+
-+/* erase.c */
-+static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++static void jffs2_put_super (struct super_block *sb)
+{
-+ OFNI_BS_2SFFJ(c)->s_dirt = 1;
-+}
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
-+/* 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);
++ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
+
-+/* 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);
++ 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);
++ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++ vfree(c->blocks);
++ else
++ kfree(c->blocks);
++ jffs2_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"));
++}
+
-+/* ioctl.c */
-+int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
+
-+/* symlink.c */
-+extern struct inode_operations jffs2_symlink_inode_operations;
++static int __init init_jffs2_fs(void)
++{
++ int ret;
+
-+/* 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);
++ printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++ " (NAND)"
++#endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
+
-+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);
-+int jffs2_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_flash_cleanup(struct jffs2_sb_info *c);
-+
++#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;
++ }
+
-+/* writev.c */
-+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
-+ unsigned long count, loff_t to, size_t *retlen);
++ 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_compressors_init();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
++ goto out;
++ }
++ ret = jffs2_create_slab_caches();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
++ goto out_compressors;
++ }
++ 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_compressors:
++ jffs2_compressors_exit();
++ out:
++ return ret;
++}
+
-+#endif /* __JFFS2_OS_LINUX_H__ */
++static void __exit exit_jffs2_fs(void)
++{
++ jffs2_destroy_slab_caches();
++ jffs2_compressors_exit();
++ unregister_filesystem(&jffs2_fs_type);
++}
+
++module_init(init_jffs2_fs);
++module_exit(exit_jffs2_fs);
+
---- linux-2.4.21/fs/jffs2/pushpull.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/pushpull.h
-@@ -1,42 +1,21 @@
++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.
+--- linux-2.4.21/fs/jffs2/super.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/super.c
+@@ -1,291 +1,270 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
-+ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
*
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ * Created by David Woodhouse <dwmw2@infradead.org>
*
*/
- #ifndef __PUSHPULL_H__
- #define __PUSHPULL_H__
-+
-+#include <linux/errno.h>
-+
- 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)
- {
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/rbtree.c
-@@ -0,0 +1,363 @@
-+/*
-+ Red Black Trees
-+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
-+ (C) 2002 David Woodhouse <dwmw2@infradead.org>
-+
-+ 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$
-+*/
-+
-+#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 <linux/version.h>
-+#include <linux/rbtree.h>
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/fs.h>
++#include <linux/mount.h>
+ #include <linux/jffs2.h>
+ #include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/namei.h>
++#include "compr.h"
+ #include "nodelist.h"
+
+-#ifndef MTD_BLOCK_MAJOR
+-#define MTD_BLOCK_MAJOR 31
+-#endif
++static void jffs2_put_super(struct super_block *);
+
+-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 *);
++static kmem_cache_t *jffs2_inode_cachep;
+
-+/* 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)
++static struct inode *jffs2_alloc_inode(struct super_block *sb)
+{
-+ 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;
++ 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 __rb_rotate_right(struct rb_node * node, struct rb_root * root)
++static void jffs2_destroy_inode(struct inode *inode)
+{
-+ 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;
++ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
+}
+
-+void rb_insert_color(struct rb_node * node, struct rb_root * root)
++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
-+ 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;
-+ }
++ struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
+
-+ parent->rb_color = RB_BLACK;
-+ gparent->rb_color = RB_RED;
-+ __rb_rotate_left(gparent, root);
-+ }
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR) {
++ init_MUTEX_LOCKED(&ei->sem);
++ inode_init_once(&ei->vfs_inode);
+ }
-+
-+ root->rb_node->rb_color = RB_BLACK;
+}
+
-+static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
-+ struct rb_root * root)
++static int jffs2_sync_fs(struct super_block *sb, int wait)
+{
-+ struct rb_node * other;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
-+ 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;
-+ }
-+ }
++ down(&c->alloc_sem);
++ jffs2_flush_wbuf_pad(c);
++ up(&c->alloc_sem);
++ return 0;
++}
+
+ 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,
++ .sync_fs = jffs2_sync_fs,
+ };
+
+-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;
++ /* 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 {
++ 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 (node)
-+ node->rb_color = RB_BLACK;
+}
-+
-+void rb_erase(struct rb_node * node, struct rb_root * root)
+
+- spin_lock_bh(&c->erase_completion_lock);
++static int jffs2_sb_set(struct super_block *sb, void *data)
+{
-+ 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;
++ struct jffs2_sb_info *p = data;
+
+- 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;
++ /* 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);
+
+- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++ return 0;
++}
+
+-#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);
++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;
+
+- 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);
+- } 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;
++ 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->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;
++ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
+
+- 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;
++ if (IS_ERR(sb))
++ goto out_put;
+
+- 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 (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;
+ }
+- 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\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->free_list)) {
+- printk(KERN_DEBUG "free_list: empty\n");
+- } else {
+- struct list_head *this;
++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
++ mtd->index, mtd->name));
+
+- 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;
++ sb->s_op = &jffs2_super_operations;
++ sb->s_flags = flags | MS_NOATIME;
+
+- 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;
++ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
+
+- 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 (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 (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 (!dev_name)
++ return ERR_PTR(-EINVAL);
+
-+ color:
-+ if (color == RB_BLACK)
-+ __rb_erase_color(child, parent, root);
-+}
-+#endif /* Before 2.4.11 */
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
+
-+ /* 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;
-+ }
++ /* 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. */
+
-+ /* 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;
++ /* FIXME: How to do the root fs this way? */
+
-+ return node->rb_parent;
-+}
++ 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;
+
-+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;
++ /* 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; i<c->nr_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;
+ }
++ 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;
+
+- 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);
++ 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);
++ }
++ }
+ }
-+ while (node->rb_parent && node == node->rb_parent->rb_left)
-+ node = node->rb_parent;
+
+- 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;
++ /* Try the old way - the hack where we allowed users to mount
++ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
+
+- if (jffs2_build_filesystem(c)) {
+- D1(printk(KERN_DEBUG "build_fs failed\n"));
+- goto out_nodes;
+- }
++ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+
+- sb->s_op = &jffs2_super_operations;
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
++ err, nd.dentry->d_inode));
+
+- 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;
++ if (err)
++ return ERR_PTR(err);
+
-+ return node->rb_parent;
-+}
++ err = -EINVAL;
+
-+void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
-+{
-+ struct rb_node *parent = victim->rb_parent;
++ if (!S_ISBLK(nd.dentry->d_inode->i_mode))
++ goto out;
+
-+ /* 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 (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 (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;
-+}
---- linux-2.4.21/fs/jffs2/read.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/read.c
-@@ -1,52 +1,32 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
- #include <linux/kernel.h>
- #include <linux/slab.h>
--#include <linux/jffs2.h>
-+#include <linux/crc32.h>
-+#include <linux/pagemap.h>
- #include <linux/mtd/mtd.h>
-+#include <linux/compiler.h>
- #include "nodelist.h"
--#include "crc32.h"
-+#include "compr.h"
+-#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);
--int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
-+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-+ struct jffs2_full_dnode *fd, unsigned char *buf,
-+ int ofs, int len)
+- 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_raw_inode *ri;
- size_t readlen;
-- __u32 crc;
-+ uint32_t crc;
- unsigned char *decomprbuf = NULL;
- unsigned char *readbuf = NULL;
- int ret = 0;
-@@ -55,35 +35,41 @@
- if (!ri)
- return -ENOMEM;
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-- 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);
+@@ -293,83 +272,65 @@
-- 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);
- }
+ 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);
++ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++ vfree(c->blocks);
++ else
+ kfree(c->blocks);
++ jffs2_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"));
+ }
-- 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 +86,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 +109,35 @@
- decomprbuf = readbuf;
- }
+-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)
++static void jffs2_kill_sb(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);
+ }
-- 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);
+-
+-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,
++};
-- if (!ret && readlen != ri->csize)
-+ if (!ret && readlen != je32_to_cpu(ri->csize))
- ret = -EIO;
- if (ret)
- goto out_decomprbuf;
+ static int __init init_jffs2_fs(void)
+ {
+ int ret;
-- 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(c, f, ri->compr | (ri->usercompr << 8), 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;
- }
- }
+- 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_JFFS2_FS_WRITEBUFFER
++ " (NAND)"
+ #endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
-- if (len < ri->dsize) {
-+ if (len < je32_to_cpu(ri->dsize)) {
- memcpy(buf, decomprbuf+ofs, len);
+- ret = jffs2_zlib_init();
++ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
++ sizeof(struct jffs2_inode_info),
++ 0, 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_compressors_init();
+ if (ret) {
+- printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
++ printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
+ goto out;
}
- out_decomprbuf:
-@@ -161,3 +151,66 @@
+ ret = jffs2_create_slab_caches();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+- goto out_zlib;
++ goto out_compressors;
+ }
+ ret = register_filesystem(&jffs2_fs_type);
+ if (ret) {
+@@ -380,17 +341,19 @@
+ out_slab:
+ jffs2_destroy_slab_caches();
+- out_zlib:
+- jffs2_zlib_exit();
++ out_compressors:
++ jffs2_compressors_exit();
+ out:
++ kmem_cache_destroy(jffs2_inode_cachep);
return ret;
}
+
+ static void __exit exit_jffs2_fs(void)
+ {
+- jffs2_destroy_slab_caches();
+- jffs2_zlib_exit();
+ unregister_filesystem(&jffs2_fs_type);
++ jffs2_destroy_slab_caches();
++ jffs2_compressors_exit();
++ kmem_cache_destroy(jffs2_inode_cachep);
+ }
+
+ module_init(init_jffs2_fs);
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/symlink-v24.c
+@@ -0,0 +1,52 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
+
-+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));
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include "nodelist.h"
+
-+ frag = jffs2_lookup_node_frag(&f->fragtree, offset);
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+
-+ /* 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);
-+ D2(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, f, 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"));
-+ }
++struct inode_operations jffs2_symlink_inode_operations =
++{
++ .readlink = jffs2_readlink,
++ .follow_link = jffs2_follow_link,
++ .setattr = jffs2_setattr
++};
++
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++ if (!f->dents) {
++ printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
++ return -EIO;
+ }
-+ return 0;
++
++ return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
+}
+
---- linux-2.4.21/fs/jffs2/readinode.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/readinode.c
-@@ -1,79 +1,124 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++ if (!f->dents) {
++ printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++ return -EIO;
++ }
++
++ return vfs_follow_link(nd, (char *)f->dents);
++}
+--- linux-2.4.21/fs/jffs2/symlink.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/symlink.c
+@@ -3,35 +3,11 @@
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
*
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- * The original JFFS, from which the design for JFFS2 was derived,
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
+- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ * Created by David Woodhouse <dwmw2@infradead.org>
*
*
*/
--/* Given an inode, probably with existing list of fragments, add the new node
-- * to the fragment list.
-- */
+@@ -39,72 +15,49 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
-+#include <linux/crc32.h>
-+#include <linux/pagemap.h>
- #include <linux/mtd/mtd.h>
-#include <linux/jffs2.h>
-+#include <linux/compiler.h>
++#include <linux/namei.h>
#include "nodelist.h"
--#include "crc32.h"
-+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
--D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
-+#if CONFIG_JFFS2_FS_DEBUG >= 2
-+static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+ struct inode_operations jffs2_symlink_inode_operations =
+ {
+- readlink: jffs2_readlink,
+- follow_link: jffs2_follow_link,
+- setattr: jffs2_setattr
++ .readlink = generic_readlink,
++ .follow_link = jffs2_follow_link,
++ .setattr = jffs2_setattr
+ };
+
+-static char *jffs2_getlink(struct dentry *dentry)
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
-- struct jffs2_node_frag *this = f->fraglist;
-+ struct jffs2_node_frag *this = frag_first(list);
-+ uint32_t lastofs = 0;
-+ int buggy = 0;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+- char *buf;
+- int ret;
- 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 (f->metadata) {
-- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
-+ if (buggy && !permitbug) {
-+ printk(KERN_CRIT "Frag tree got a hole in it\n");
-+ BUG();
+- 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;
++ /*
++ * We don't acquire the f->sem mutex here since the only data we
++ * use is f->dents which in case of the symlink inode points to the
++ * symlink's target path.
++ *
++ * 1. If we are here the inode has already built and f->dents has
++ * to point to the target path.
++ * 2. Nobody uses f->dents (if the inode is symlink's inode). The
++ * exception is inode freeing function which frees f->dents. But
++ * it can't be called while we are here and before VFS has
++ * stopped using our f->dents string which we provide by means of
++ * nd_set_link() call.
++ */
+
+- 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);
++ if (!f->dents) {
++ printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++ return -EIO;
}
--})
-+}
+- return buf;
+-
+-}
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+-{
+- unsigned char *kbuf;
+- int ret;
++ D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
-+void jffs2_print_frag_list(struct jffs2_inode_info *f)
-+{
-+ jffs2_print_fragtree(&f->fragtree, 0);
+- kbuf = jffs2_getlink(dentry);
+- if (IS_ERR(kbuf))
+- return PTR_ERR(kbuf);
++ nd_set_link(nd, (char *)f->dents);
--int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
-+ if (f->metadata) {
-+ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+- ret = vfs_readlink(dentry, buffer, buflen, kbuf);
+- kfree(kbuf);
+- return ret;
++ /*
++ * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
++ * since the only way that may cause f->dents to be changed is iput() operation.
++ * But VFS will not use f->dents after iput() has been called.
++ */
++ return 0;
+ }
+
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+-{
+- unsigned char *buf;
+- int ret;
+-
+- buf = jffs2_getlink(dentry);
+-
+- if (IS_ERR(buf))
+- return PTR_ERR(buf);
+-
+- ret = vfs_follow_link(nd, buf);
+- kfree(buf);
+- return ret;
+-}
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/wbuf.c
+@@ -0,0 +1,1240 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/crc32.h>
++#include <linux/mtd/nand.h>
++#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 2
++
++/* 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);
++ }
++ }
++}
++
++#define REFILE_NOTEMPTY 0
++#define REFILE_ANYWAY 1
++
++static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
++{
++ 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_ON(allow_empty == REFILE_NOTEMPTY);
++ /* 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));
++}
++
++/* 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];
++
++ jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
++
++ /* 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... */
++ if (jffs2_cleanmarker_oob(c))
++ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
++ else
++ ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
++
++ if (ret == -EBADMSG && 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");
++ kfree(buf);
++ return;
++ }
++ if (end-start >= c->wbuf_pagesize) {
++ /* Need to do another write immediately, but it's possible
++ that this is just because the wbuf itself is completely
++ full, and there's nothing earlier read back from the
++ flash. Hence 'buf' isn't necessarily what we're writing
++ from. */
++ unsigned char *rewrite_buf = buf?:c->wbuf;
++ 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
++ if (jffs2_cleanmarker_oob(c))
++ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++ rewrite_buf, NULL, c->oobinfo);
++ else
++ ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
+
-+#if CONFIG_JFFS2_FS_DEBUG >= 1
-+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 (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 (ref_flags(fn->raw) == REF_PRISTINE) {
++ if (retlen) {
++ struct jffs2_raw_node_ref *raw2;
+
-+ 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;
-+ }
++ raw2 = jffs2_alloc_raw_node_ref();
++ if (!raw2)
++ return;
+
-+ 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;
++ 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;
+ }
-+ }
-+
-+ if (bitched) {
-+ struct jffs2_node_frag *thisfrag;
++ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+
-+ 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);
++ c->wbuf_len = (end - start) - towrite;
++ c->wbuf_ofs = ofs + towrite;
++ memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
++ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
++ if (buf)
++ 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;
+ }
-+ return bitched;
- }
-+#endif /* D1 */
-
- static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
- {
-@@ -82,42 +127,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 +167,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);
++ /* 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];
+
-+ 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);
++ 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;
+ }
+
-+ 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);
++ 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;
+ }
-+ 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) {
++ /* 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);
+
-+ /* 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 && (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);
-+ }
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
-+ 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);
- }
-- newfrag->next = NULL;
-- *prev = newfrag;
-+ rb_insert_color(&holefrag->rb, list);
-+ this = holefrag;
-+ }
-+ 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) {
++ ACCT_SANITY_CHECK(c,new_jeb);
++ D1(ACCT_PARANOIA_CHECK(new_jeb));
+
-+ /* 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);
++ spin_unlock(&c->erase_completion_lock);
+
-+ 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;
++ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++}
+
-+ /* Adjust size of original 'this' */
- this->size = newfrag->ofs - this->ofs;
++/* 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
++*/
++#define NOPAD 0
++#define PAD_NOACCOUNT 1
++#define PAD_ACCOUNTING 2
+
-+ /* 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;
++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++{
++ int ret;
++ size_t retlen;
+
-+ /* 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;
++ /* Nothing to do if not write-buffering the flash. In particular, we shouldn't
++ del_timer() the timer we never initialised. */
++ if (!jffs2_is_writebuffered(c))
++ return 0;
+
-+ 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
++ if (!down_trylock(&c->alloc_sem)) {
++ up(&c->alloc_sem);
++ printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
++ BUG();
+ }
-+ /* 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);
++ if (!c->wbuf_len) /* already checked c->wbuf above */
++ 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 && !jffs2_dataflash(c)) {
++ c->wbuf_len = PAD(c->wbuf_len);
++
++ /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
++ with 8 byte page size */
++ memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - 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
++
++ if (jffs2_cleanmarker_oob(c))
++ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
++ else
++ ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
++
++ 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;
++ }
+
- 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);
++ jffs2_wbuf_recover(c);
+
- 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;
-+ /* 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;
- }
-- list = &(*list)->next;
-+ frag = frag_next(frag);
++ return ret;
+ }
-+ 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);
++ spin_lock(&c->erase_completion_lock);
+
-+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);
-
-- f = JFFS2_INODE_INFO(inode);
-- c = JFFS2_SB_INFO(inode->i_sb);
-+ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
-
-- 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));
-+ 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;
-
-- if (!f->inocache && inode->i_ino == 1) {
-+ 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;
++ /* Adjust free size of the block if we padded. */
++ if (pad && !jffs2_dataflash(c)) {
++ struct jffs2_eraseblock *jeb;
+
-+ 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;
++ jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
+
-+ default:
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
++ (jeb==c->nextblock)?"next":"", jeb->offset));
++
++ /* 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 (jeb->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",
++ jeb->offset, jeb->free_size);
+ BUG();
+ }
++ jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
+ }
-+ spin_unlock(&c->inocache_lock);
+
-+ 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;
++ /* Stick any now-obsoleted blocks on the erase_pending_list */
++ jffs2_refile_wbuf_blocks(c);
++ jffs2_clear_wbuf_ino_list(c);
++ spin_unlock(&c->erase_completion_lock);
+
-+ return jffs2_do_read_inode_internal(c, f, latest_node);
++ 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;
+}
+
-+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++/* 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)
+{
-+ struct jffs2_raw_inode n;
-+ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
-+ int ret;
++ uint32_t old_wbuf_ofs;
++ uint32_t old_wbuf_len;
++ int ret = 0;
+
-+ if (!f)
-+ return -ENOMEM;
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
+
-+ memset(f, 0, sizeof(*f));
-+ init_MUTEX_LOCKED(&f->sem);
-+ f->inocache = ic;
++ if (!c->wbuf)
++ return 0;
+
-+ ret = jffs2_do_read_inode_internal(c, f, &n);
-+ if (!ret) {
-+ up(&f->sem);
-+ jffs2_do_clear_inode(c, f);
++ 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;
+ }
-+ 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;
++ old_wbuf_ofs = c->wbuf_ofs;
++ old_wbuf_len = c->wbuf_len;
+
-+ 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, &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,219 +527,217 @@
-
- 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 (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"));
++ down_write(&c->wbuf_sem);
++ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++ /* retry flushing wbuf in case jffs2_wbuf_recover
++ left some data in the wbuf */
++ if (ret)
++ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++ up_write(&c->wbuf_sem);
++ } else while (old_wbuf_len &&
++ old_wbuf_ofs == c->wbuf_ofs) {
+
- 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;
-+ if (f->inocache->state == INO_STATE_READING)
-+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
-+ return -EIO;
- }
-- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
-+ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
- }
-- 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;
-+ 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;
- }
-
-- 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;
-+ 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_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;
-+ 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;
- }
-
-- /* 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;
-+ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
-+ case S_IFDIR:
-+ 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. */
-+ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
- }
-+ break;
-
-- inode->i_blksize = PAGE_SIZE;
-- inode->i_blocks = (inode->i_size + 511) >> 9;
-
-- switch (inode->i_mode & S_IFMT) {
-- unsigned short rdev;
-+ case S_IFREG:
-+ /* 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:
-- 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;
-+ if (!je32_to_cpu(latest_node->isize))
-+ latest_node->isize = latest_node->dsize;
-
-- case S_IFDIR:
-- if (mctime_ver > 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;
-+ if (f->inocache->state != INO_STATE_CHECKING) {
-+ /* Symlink's inode data is the target path. Read it and
-+ * keep in RAM to facilitate quick follow symlink operation.
-+ * We use f->dents field to store the target path, which
-+ * is somewhat ugly. */
-+ f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
-+ if (!f->dents) {
-+ printk(KERN_WARNING "Can't allocate %d bytes of memory "
-+ "for the symlink target path cache\n",
-+ je32_to_cpu(latest_node->csize));
-+ up(&f->sem);
-+ jffs2_do_clear_inode(c, f);
-+ return -ENOMEM;
- }
-- 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;
-+ ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
-+ je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
-+
-+ if (ret || retlen != je32_to_cpu(latest_node->csize)) {
-+ if (retlen != je32_to_cpu(latest_node->csize))
-+ ret = -EIO;
-+ kfree(f->dents);
-+ f->dents = NULL;
-+ up(&f->sem);
-+ jffs2_do_clear_inode(c, f);
-+ return -ret;
++ 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);
++ down_write(&c->wbuf_sem);
++ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++ /* retry flushing wbuf in case jffs2_wbuf_recover
++ left some data in the wbuf */
++ if (ret)
++ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++ up_write(&c->wbuf_sem);
++ 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)
++{
++ int ret;
++
++ if (!c->wbuf)
++ return 0;
++
++ down_write(&c->wbuf_sem);
++ ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++ /* retry - maybe wbuf recover left some data in wbuf. */
++ if (ret)
++ ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++ up_write(&c->wbuf_sem);
++
++ return ret;
++}
++
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
++#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
++#else
++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
++#endif
++
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
++{
++ struct kvec 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 (!jffs2_is_writebuffered(c))
++ return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
++
++ down_write(&c->wbuf_sem);
++
++ /* 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);
++ }
++
++ /* Fixup the wbuf if we are moving to a new eraseblock. The checks below
++ fail for ECC'd NOR because cleanmarker == 16, so a block starts at
++ xxx0010. */
++ if (jffs2_nor_ecc(c)) {
++ if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
++ 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 (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
++ /* 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(c, PAD_NOACCOUNT);
++ 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;
++ goto exit;
+ }
++ }
++ /* set pointer to new block */
++ c->wbuf_ofs = PAGE_DIV(to);
++ c->wbuf_len = PAGE_MOD(to);
++ }
+
-+ ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
-+ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
-+ (char *)f->dents));
++ 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;
+ }
+
-+ /* 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;
++ 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, NOPAD);
++ 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;
++ goto exit;
+ }
-+ /* 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;
++ 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++;
+ }
-+ /* 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);
++ invec++;
++ }
+
-+ 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);
-- /* I don't think we care about the potential race due to reading this
-- without f->sem. It can never get undeleted. */
-- int deleted = f->inocache && !f->inocache->nlink;
--
-- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
--
-- /* If it's a deleted inode, grab the alloc_sem. This prevents
-- jffs2_garbage_collect_pass() from deciding that it wants to
-- garbage collect one of the nodes we're just about to mark
-- obsolete -- by the time we drop alloc_sem and return, all
-- the nodes are marked obsolete, and jffs2_g_c_pass() won't
-- call iget() for the inode in question.
-- */
-- if (deleted)
-- down(&c->alloc_sem);
-+ int deleted;
-
- down(&f->sem);
-+ deleted = f->inocache && !f->inocache->nlink;
++ /* OK, now we've flushed the wbuf and the start of the bits
++ we have been asked to write, now to write the rest.... */
+
-+ if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
-+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
-
-- 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));
--
-- 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);
-+ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
-
-- jffs2_free_full_dnode(frag->node);
-- }
-- jffs2_free_node_frag(frag);
-+ /* For symlink inodes we us f->dents to store the target path name */
-+ if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
-+ if (f->dents) {
-+ kfree(f->dents);
-+ f->dents = NULL;
- }
-+ } else {
-+ fds = f->dents;
++ /* 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;
++ }
++ }
+
- while(fds) {
- fd = fds;
- fds = fd->next;
- jffs2_free_full_dirent(fd);
- }
++ /* 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;
++
++ remainder = outvecs[splitvec].iov_len - split_ofs;
++ outvecs[splitvec].iov_len = split_ofs;
++
++ /* We did cross a page boundary, so we write some now */
++ if (jffs2_cleanmarker_oob(c))
++ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
++ else
++ ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
++
++ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
++ /* At this point we have no problem,
++ c->wbuf is empty. However refile nextblock to avoid
++ writing again to same address.
++ */
++ struct jffs2_eraseblock *jeb;
++
++ spin_lock(&c->erase_completion_lock);
++
++ jeb = &c->blocks[outvec_to / c->sector_size];
++ jffs2_block_refile(c, jeb, REFILE_ANYWAY);
++
++ *retlen = 0;
++ spin_unlock(&c->erase_completion_lock);
++ goto exit;
++ }
++
++ 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;
+ }
-
-- up(&f->sem);
--
-- if(deleted)
-- up(&c->alloc_sem);
--};
-+ if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
-+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
-+ if (f->inocache->nodes == (void *)f->inocache)
-+ jffs2_del_ino_cache(c, f->inocache);
++
++ /* 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;
+ }
-
-+ up(&f->sem);
++ 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);
++
++ ret = 0;
++
++exit:
++ up_write(&c->wbuf_sem);
++ return ret;
+}
---- linux-2.4.21/fs/jffs2/scan.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/scan.c
-@@ -1,47 +1,25 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
- #include <linux/kernel.h>
-+#include <linux/sched.h>
- #include <linux/slab.h>
--#include <linux/jffs2.h>
- #include <linux/mtd/mtd.h>
- #include <linux/pagemap.h>
-+#include <linux/crc32.h>
-+#include <linux/compiler.h>
- #include "nodelist.h"
--#include "crc32.h"
-
-+#define DEFAULT_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,96 @@
- } 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
++/*
++ * This is the entry for flash write.
++ * Check, if we work on NAND FLASH, if so build an kvec 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 kvec vecs[1];
++
++ if (!jffs2_is_writebuffered(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;
++
++ if (!jffs2_is_writebuffered(c))
++ return c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++ /* Read flash */
++ if (jffs2_cleanmarker_oob(c))
++ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
++ else
++ ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++ if ( (ret == -EBADMSG) && (*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;
++ }
++
++ /* if no writebuffer available or write buffer empty, return */
++ if (!c->wbuf_pagesize || !c->wbuf_len)
++ return ret;;
+
-+static inline int min_free(struct jffs2_sb_info *c)
-+{
-+ uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
-+ return c->wbuf_pagesize;
-+#endif
-+ return min;
-
-+}
++ /* if we read in a different block, return */
++ if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
++ return ret;
+
-+static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
-+ if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
-+ return sector_size;
-+ else
-+ return DEFAULT_EMPTY_SCAN_SIZE;
++ /* Lock only if we have reason to believe wbuf contains relevant data,
++ so that checking an erased block during wbuf recovery space allocation
++ does not deadlock. */
++ down_read(&c->wbuf_sem);
++
++ if (ofs >= c->wbuf_ofs) {
++ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
++ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
++ goto exit;
++ 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 ? */
++ goto exit;
++ 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);
++
++exit:
++ up_read(&c->wbuf_sem);
++ return ret;
+}
-
- int jffs2_scan_medium(struct jffs2_sb_info *c)
- {
- int i, ret;
-- __u32 empty_blocks = 0;
-+ uint32_t empty_blocks = 0, bad_blocks = 0;
-+ unsigned char *flashbuf = NULL;
-+ uint32_t buf_size = 0;
-+#ifndef __ECOS
-+ size_t pointlen;
-
-- if (!c->blocks) {
-- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
-- return -EINVAL;
-+ 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;
++
++/*
++ * 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;
++
++ /* allocate a buffer for all oob data in this sector */
++ oob_size = c->mtd->oobsize;
++ 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 page */
++ 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[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;
+ }
-+ 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;
+
-+ /* Respect kmalloc limitations */
-+ if (buf_size > 128*1024)
-+ buf_size = 128*1024;
++ /* we know, we are aligned :) */
++ for (page = oob_size; page < len; page += sizeof(long)) {
++ unsigned long dat = *(unsigned long *)(&buf[page]);
++ if(dat != -1) {
++ ret = 1;
++ goto out;
++ }
++ }
+
-+ D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
-+ flashbuf = kmalloc(buf_size, GFP_KERNEL);
-+ if (!flashbuf)
-+ return -ENOMEM;
- }
++out:
++ kfree(buf);
++
++ return ret;
++}
+
- for (i=0; i<c->nr_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);
++/*
++* 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[2 * NAND_MAX_OOBSIZE];
++ unsigned char *p;
++ int ret, i, cnt, retval = 0;
++ size_t retlen, offset;
++ int oob_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 +142,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;
++ offset = jeb->offset;
++ oob_size = c->mtd->oobsize;
+
-+ case BLK_STATE_CLEANMARKER:
- /* Only a CLEANMARKER node is valid */
- if (!jeb->dirty_size) {
- /* It's actually free */
-@@ -118,74 +159,224 @@
- 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;
++ /* Loop through the physical blocks */
++ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
++ /* Check first if the block is bad. */
++ if (c->mtd->block_isbad (c->mtd, offset)) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
++ return 2;
++ }
++ /*
++ * 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);
+
-+ 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;
++ 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;
++ }
+
-+ 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) &&
-+ /* We want to remember the block with most free space
-+ and stick it in the 'nextblock' position to start writing to it. */
-+ 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;
++ /* 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;
+
-+ 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;
++ 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;
+ }
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ 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. */
++ return retval;
++}
+
-+ uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_unknown_node n;
++ int ret;
++ size_t retlen;
+
-+ 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;
++ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++ n.totlen = cpu_to_je32(8);
+
-+ c->nextblock->free_size -= skip;
-+ c->free_size -= skip;
++ 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;
+ }
-+#endif
- if (c->nr_erasing_blocks) {
-- if (!c->used_size && empty_blocks != c->nr_blocks) {
-+ if ( !c->used_size && ((c->nr_free_blocks+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;
++ 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;
+}
+
-+static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
-+ uint32_t ofs, uint32_t len)
++/*
++ * On NAND we try to mark this block bad. If the block was erased more
++ * than MAX_ERASE_FAILURES we mark it finaly bad.
++ * 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, uint32_t bad_offset)
+{
-+ int ret;
-+ size_t retlen;
++ int ret;
+
-+ ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
++ /* if the count is < max, we try to write the counter to the 2nd page oob area */
++ if( ++jeb->bad_count < MAX_ERASE_FAILURES)
++ return 0;
++
++ if (!c->mtd->block_markbad)
++ return 1; // What else can we do?
++
++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
++ ret = c->mtd->block_markbad(c->mtd, bad_offset);
++
+ if (ret) {
-+ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, 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 < 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;
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ 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_WRITEBUFFER
-+ 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;
++ return 1;
++}
++
++#define NAND_JFFS2_OOB16_FSDALEN 8
++
++static struct nand_oobinfo jffs2_oobinfo_docecc = {
++ .useecc = MTD_NANDECC_PLACE,
++ .eccbytes = 6,
++ .eccpos = {0,1,2,3,4,5}
++};
++
++
++int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
++{
++ struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
++
++ /* Do this only, if we have an oob buffer */
++ if (!c->mtd->oobsize)
++ return 0;
++
++ /* Cleanmarker is out-of-band, so inline size zero */
++ c->cleanmarker_size = 0;
++
++ /* Should we use autoplacement ? */
++ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
++ D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
++ /* Get the position of the free bytes */
++ if (!oinfo->oobfree[0][1]) {
++ printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
++ return -ENOSPC;
++ }
++ c->fsdata_pos = oinfo->oobfree[0][0];
++ c->fsdata_len = oinfo->oobfree[0][1];
++ if (c->fsdata_len > 8)
++ c->fsdata_len = 8;
++ } else {
++ /* This is just a legacy fallback and should go away soon */
++ switch(c->mtd->ecctype) {
++ case MTD_ECC_RS_DiskOnChip:
++ printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
++ c->oobinfo = &jffs2_oobinfo_docecc;
++ c->fsdata_pos = 6;
++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++ c->badblock_pos = 15;
++ break;
++
++ default:
++ D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
++ return -EINVAL;
+ }
+ }
-+#endif
-+ buf_ofs = jeb->offset;
++ return 0;
++}
+
-+ if (!buf_size) {
-+ buf_len = c->sector_size;
-+ } else {
-+ buf_len = EMPTY_SCAN_SIZE(c->sector_size);
-+ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
-+ if (err)
-+ return err;
-+ }
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++{
++ int res;
++
++ /* Initialise write buffer */
++ init_rwsem(&c->wbuf_sem);
++ c->wbuf_pagesize = c->mtd->oobblock;
++ c->wbuf_ofs = 0xFFFFFFFF;
+
-+ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
-+ ofs = 0;
++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!c->wbuf)
++ return -ENOMEM;
+
-+ /* Scan only 4KiB of 0xFF before declaring it's empty */
-+ while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
-+ ofs += 4;
++ res = jffs2_nand_set_oobinfo(c);
+
-+ if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ 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;
-+ default: return ret;
-+ }
-+ }
-+#endif
- D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
-- return 1; /* special return code */
-+ if (c->cleanmarker_size == 0)
-+ return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */
-+ else
-+ return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
++#ifdef BREAKME
++ if (!brokenbuf)
++ brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!brokenbuf) {
++ kfree(c->wbuf);
++ return -ENOMEM;
+ }
-+ 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;
++ memset(brokenbuf, 0xdb, c->wbuf_pagesize);
++#endif
++ return res;
++}
+
- noise = 10;
-
-+scan_more:
- while(ofs < jeb->offset + c->sector_size) {
-- ssize_t retlen;
-- ACCT_PARANOIA_CHECK(jeb);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
++{
++ kfree(c->wbuf);
++}
+
-+ D1(ACCT_PARANOIA_CHECK(jeb));
++int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
++ c->cleanmarker_size = 0; /* No cleanmarkers needed */
++
++ /* Initialize write buffer */
++ init_rwsem(&c->wbuf_sem);
++ c->wbuf_pagesize = c->sector_size;
++ c->wbuf_ofs = 0xFFFFFFFF;
+
-+ 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 +387,183 @@
- }
- 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;
++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!c->wbuf)
++ return -ENOMEM;
+
-+ node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
++ printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
+
-+ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
-+ uint32_t inbuf_ofs;
-+ uint32_t empty_start;
++ return 0;
++}
+
-+ empty_start = ofs;
-+ ofs += 4;
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
++ kfree(c->wbuf);
++}
+
-+ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
-+ more_empty:
-+ inbuf_ofs = ofs - buf_ofs;
-+ while (inbuf_ofs < buf_len) {
-+ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
-+ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
-+ empty_start, ofs);
-+ DIRTY_SPACE(ofs-empty_start);
-+ goto scan_more;
- }
-
-- 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;
-- continue;
-+ inbuf_ofs+=4;
-+ ofs += 4;
- }
-+ /* Ran off end. */
-+ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
-
-- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
-+ /* If we're only checking the beginning of a block with a cleanmarker,
-+ bail now */
-+ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
-+ c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
-+ D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
-+ return BLK_STATE_CLEANMARKER;
-+ }
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
++ /* Cleanmarker is actually larger on the flashes */
++ c->cleanmarker_size = 16;
+
-+ /* See how much more there is to read in this eraseblock... */
-+ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-+ if (!buf_len) {
-+ /* No more to read. Break out of main loop without marking
-+ this range of empty space as dirty (because it's not) */
-+ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
-+ empty_start));
-+ break;
-+ }
-+ D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
-+ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
-+ if (err)
-+ return err;
-+ buf_ofs = ofs;
-+ goto more_empty;
-+ }
++ /* Initialize write buffer */
++ init_rwsem(&c->wbuf_sem);
++ c->wbuf_pagesize = c->mtd->eccsize;
++ c->wbuf_ofs = 0xFFFFFFFF;
+
-+ 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);
++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!c->wbuf)
++ return -ENOMEM;
+
-+ 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;
-+ }
++ return 0;
++}
+
-+ 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;
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
++ kfree(c->wbuf);
++}
+--- linux-2.4.21/fs/jffs2/write.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/write.c
+@@ -1,154 +1,70 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
- 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 +572,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;
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
- 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;
+-/* 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));
- 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;
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+- return ERR_PTR(-ENOMEM);
+- }
+- memset(ic, 0, sizeof(*ic));
+-
+- inode = new_inode(sb);
+-
+- if (!inode) {
+- jffs2_free_inode_cache(ic);
+- return ERR_PTR(-ENOMEM);
++ return -ENOMEM;
+ }
- 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;
+- /* Alloc jffs2_inode_info when that's split in 2.5 */
++ memset(ic, 0, sizeof(*ic));
- 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;
+- 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);
+-
+- 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;
+-
+- insert_inode_hash(inode);
++ f->inocache->ino = ++c->highest_ino;
++ f->inocache->state = INO_STATE_PRESENT;
+
+- return inode;
-}
++ ri->ino = cpu_to_je32(f->inocache->ino);
--/* 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)
+-/* 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)
-{
-- __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;
-+ 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));
-
-- 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;
-+ /* 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;
- }
-- for (i=0; i<(retlen / 4); i++) {
-- if (buf[i] != 0xffffffff) {
-- curofs += i*4;
+- unsigned long i;
+- size_t totlen = 0, thislen;
+- int ret = 0;
++ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
++ jffs2_add_ino_cache(c, f->inocache);
-- 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;
+- for (i=0; i<count; i++) {
+- ret = mtd->write(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 ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
-+ && (!jeb->first_node || !jeb->first_node->next_phys) )
-+ return BLK_STATE_CLEANMARKER;
+- if (retlen)
+- *retlen = totlen;
+- return ret;
+-}
++ 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);
-- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
-- kfree(buf);
-- *startofs = curofs;
-- return 0;
-+ /* 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;
++ f->highest_version = 1;
++ ri->version = cpu_to_je32(f->highest_version);
+
+-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 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)
+-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)
{
- struct jffs2_inode_cache *ic;
-
-@@ -399,137 +653,77 @@
- if (ic)
- return ic;
+ unsigned char buf[16];
+- ssize_t retlen;
++ size_t retlen;
+ int ret, i;
-+ 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;
+- 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]);
}
- 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;
}
+-
+-
++#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)
--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_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;
-- struct jffs2_tmp_dnode_info *tn, **tn_list;
- struct jffs2_inode_cache *ic;
-- struct jffs2_raw_inode ri;
-- __u32 crc;
-- __u16 oldnodetype;
-- int ret;
+ struct jffs2_full_dnode *fn;
- 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;
-- }
--
-- /* 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;
--
-- 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;
-- }
-+ uint32_t ino = je32_to_cpu(ri->ino);
+- struct iovec vecs[2];
++ size_t retlen;
++ struct kvec vecs[2];
+ int ret;
++ int retried = 0;
++ unsigned long cnt = 2;
-- if (ri.csize) {
-- /* Check data CRC too */
-- unsigned char *dbuf;
-- __u32 crc;
-+ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+- 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;
-- 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;
-- }
-- }
-+ /* 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. */
+- writecheck(c->mtd, flash_ofs);
++ D1(writecheck(c, flash_ofs));
-- /* Wheee. It worked */
+- 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) {
- printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
- return -ENOMEM;
+ if (!raw)
+@@ -206,19 +121,37 @@
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-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);
+- 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;
+
-+ 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);
++ /* 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;
+
-+ 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;
- }
++ if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
++ BUG_ON(!retried);
++ D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
++ "highest version %d -> updating dnode\n",
++ je32_to_cpu(ri->version), f->highest_version));
++ ri->version = cpu_to_je32(++f->highest_version);
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+ }
-
-- /* 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;
++ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
++ (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
+
- ic->nodes = raw;
- if (!jeb->first_node)
- jeb->first_node = raw;
-@@ -538,134 +732,56 @@
- jeb->last_node = raw;
+ 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 +162,98 @@
+ 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);
++ }
- 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;
--
-- 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) {
-+ 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)));
++ 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);
-- 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));
-+ pseudo_random += je32_to_cpu(ri->version);
+ /* Link into per-inode list */
++ spin_lock(&c->erase_completion_lock);
+ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
++ spin_unlock(&c->erase_completion_lock);
-- 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;
+- 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;
}
--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_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;
- 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;
- }
+- struct iovec vecs[2];
++ size_t retlen;
++ struct kvec vecs[2];
++ int retried = 0;
+ int ret;
-- pseudo_random += rd.version;
-+ pseudo_random += je32_to_cpu(rd->version);
+- 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));
-- 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 +790,15 @@
- printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
- return -ENOMEM;
+- 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();
}
-- 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);
+@@ -291,44 +274,457 @@
jffs2_free_raw_node_ref(raw);
- return -ENOMEM;
+ return ERR_PTR(-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 +808,15 @@
- jeb->last_node->next_phys = raw;
- jeb->last_node = raw;
+- 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;
-- 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 = 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(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 +838,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));
+ fd->nhash = full_name_hash(name, strlen(name));
+ fd->type = rd->type;
+ memcpy(fd->name, name, namelen);
+ fd->name[namelen]=0;
+
-+ rotate_list((&c->clean_list), rotateby);
++ 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;
+
-+ 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"));
++ if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
++ BUG_ON(!retried);
++ D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
++ "highest version %d -> updating dirent\n",
++ je32_to_cpu(rd->version), f->highest_version));
++ rd->version = cpu_to_je32(++f->highest_version);
++ fd->version = je32_to_cpu(rd->version);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+ }
+
-+ 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);
++ 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];
+
-+ 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));
-
-- if (c->nr_erasing_blocks)
-- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
-+ rotate_list((&c->dirty_list), rotateby);
++ retried = 1;
-- 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 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));
-+
-+ rotate_list((&c->erasable_list), rotateby);
++ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+
-+ 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"));
-+ }
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
-+ if (c->nr_erasing_blocks) {
-+ rotateby = pseudo_random % c->nr_erasing_blocks;
-+ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
++ 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);
++ }
+
-+ rotate_list((&c->erase_pending_list), rotateby);
++ 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);
+
++ spin_lock(&c->erase_completion_lock);
++ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
++ spin_unlock(&c->erase_completion_lock);
+
-+ 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 (retried) {
++ ACCT_SANITY_CHECK(c,NULL);
+ }
+
-+ 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"));
-+ }
+ return fd;
}
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/super-v24.c
-@@ -0,0 +1,170 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-+ *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * For licensing information, see the file 'LICENCE' in this directory.
-+ *
-+ * $Id$
-+ *
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/version.h>
-+#include <linux/slab.h>
-+#include <linux/init.h>
-+#include <linux/list.h>
-+#include <linux/fs.h>
-+#include <linux/jffs2.h>
-+#include <linux/pagemap.h>
-+#include <linux/mtd/mtd.h>
-+#include "compr.h"
-+#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)
++/* 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)
+{
-+ struct jffs2_sb_info *c;
-+ int ret;
-+
-+ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++ int ret = 0;
++ uint32_t writtenlen = 0;
+
-+ 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;
-+ }
++ 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;
++ uint16_t comprtype = JFFS2_COMPR_NONE;
++ uint32_t phys_ofs, alloclen;
++ uint32_t datalen, cdatalen;
++ int retried = 0;
+
-+ c = JFFS2_SB_INFO(sb);
-+ memset(c, 0, sizeof(*c));
++ retry:
++ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
+
-+ sb->s_op = &jffs2_super_operations;
++ 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);
+
-+ 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;
-+ }
++ comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
+
-+ ret = jffs2_do_fill_super(sb, data, silent);
-+ if (ret) {
-+ put_mtd_device(c->mtd);
-+ return NULL;
-+ }
++ 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));
+
-+ return sb;
-+}
++ 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 & 0xff;
++ ri->usercompr = (comprtype >> 8 ) & 0xff;
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
+
-+static void jffs2_put_super (struct super_block *sb)
-+{
-+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
+
-+ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
++ 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);
+
-+ 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);
-+ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+ vfree(c->blocks);
-+ else
-+ kfree(c->blocks);
-+ jffs2_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"));
++ 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;
+}
+
-+static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
-+
-+static int __init init_jffs2_fs(void)
++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;
+
-+ printk(KERN_INFO "JFFS2 version 2.2."
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ " (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;
++ /* 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;
+ }
+
-+ 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_compressors_init();
-+ if (ret) {
-+ printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
-+ goto out;
++ 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);
+ }
-+ ret = jffs2_create_slab_caches();
++ /* 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) {
-+ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-+ goto out_compressors;
++ /* Eep. */
++ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
++ return ret;
+ }
-+ ret = register_filesystem(&jffs2_fs_type);
-+ if (ret) {
-+ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
-+ goto out_slab;
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd) {
++ /* Argh. Now we treat it like a normal delete */
++ jffs2_complete_reservation(c);
++ return -ENOMEM;
+ }
-+ return 0;
+
-+ out_slab:
-+ jffs2_destroy_slab_caches();
-+ out_compressors:
-+ jffs2_compressors_exit();
-+ out:
-+ return ret;
-+}
++ down(&dir_f->sem);
+
-+static void __exit exit_jffs2_fs(void)
-+{
-+ jffs2_destroy_slab_caches();
-+ jffs2_compressors_exit();
-+ unregister_filesystem(&jffs2_fs_type);
-+}
++ 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));
+
-+module_init(init_jffs2_fs);
-+module_exit(exit_jffs2_fs);
++ 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));
+
-+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.
---- linux-2.4.21/fs/jffs2/super.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/super.c
-@@ -1,291 +1,270 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
-
- #include <linux/config.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
--#include <linux/version.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/list.h>
- #include <linux/fs.h>
-+#include <linux/mount.h>
- #include <linux/jffs2.h>
- #include <linux/pagemap.h>
- #include <linux/mtd/mtd.h>
--#include <linux/interrupt.h>
-+#include <linux/ctype.h>
-+#include <linux/namei.h>
-+#include "compr.h"
- #include "nodelist.h"
-
--#ifndef MTD_BLOCK_MAJOR
--#define MTD_BLOCK_MAJOR 31
--#endif
-+static void jffs2_put_super(struct super_block *);
-
--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 *);
-+static kmem_cache_t *jffs2_inode_cachep;
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
+
-+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;
-+}
++ 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);
++ }
+
-+static void jffs2_destroy_inode(struct inode *inode)
-+{
-+ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
-+}
++ /* Link the fd into the inode's list, obsoleting an old
++ one if necessary. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
-+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;
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
+
-+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-+ SLAB_CTOR_CONSTRUCTOR) {
-+ init_MUTEX_LOCKED(&ei->sem);
-+ inode_init_once(&ei->vfs_inode);
-+ }
++ return 0;
+}
+
-+static int jffs2_sync_fs(struct super_block *sb, int wait)
-+{
-+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
-+ down(&c->alloc_sem);
-+ jffs2_flush_wbuf_pad(c);
-+ up(&c->alloc_sem);
-+ return 0;
-+}
-
- 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,
-+ .sync_fs = jffs2_sync_fs,
- };
-
--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;
-+ /* 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 {
-+ 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;
-+ }
-+}
-
-- spin_lock_bh(&c->erase_completion_lock);
-+static int jffs2_sb_set(struct super_block *sb, void *data)
-+{
-+ struct jffs2_sb_info *p = data;
-
-- 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;
-+ /* 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);
-
-- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
-+ return 0;
-+}
-
--#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);
-+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)
++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 super_block *sb;
-+ struct jffs2_sb_info *c;
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
+ int ret;
-
-- 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);
-- } 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;
-+ 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->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;
-+ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
-
-- 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;
-+ if (IS_ERR(sb))
-+ goto out_put;
-
-- 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 (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;
- }
-- 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\n", jeb->offset);
-- }
-- }
-- if (list_empty(&c->free_list)) {
-- printk(KERN_DEBUG "free_list: empty\n");
-- } else {
-- struct list_head *this;
-+ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
-+ mtd->index, mtd->name));
-
-- 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;
-+ sb->s_op = &jffs2_super_operations;
-+ sb->s_flags = flags | MS_NOATIME;
-
-- 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;
-+ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
-
-- 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 (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);
++ if (1 /* alternative branch needs testing */ ||
++ !jffs2_can_mark_obsolete(c)) {
++ /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
++
++ 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);
++ }
+
-+ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
++ /* File it. This will mark the old one obsolete. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++ up(&dir_f->sem);
++ } else {
++ struct jffs2_full_dirent **prev = &dir_f->dents;
++ uint32_t nhash = full_name_hash(name, namelen);
+
-+ /* 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. */
++ down(&dir_f->sem);
+
-+ /* FIXME: How to do the root fs this way? */
++ while ((*prev) && (*prev)->nhash <= nhash) {
++ if ((*prev)->nhash == nhash &&
++ !memcmp((*prev)->name, name, namelen) &&
++ !(*prev)->name[namelen]) {
++ struct jffs2_full_dirent *this = *prev;
+
-+ 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;
++ D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
++ this->ino, ref_offset(this->raw)));
+
-+ /* 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; i<c->nr_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;
- }
-+ 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;
-
-- 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);
-+ 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);
++ *prev = this->next;
++ jffs2_mark_node_obsolete(c, (this->raw));
++ jffs2_free_full_dirent(this);
++ break;
+ }
++ prev = &((*prev)->next);
+ }
++ up(&dir_f->sem);
+ }
-
-- 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;
-+ /* Try the old way - the hack where we allowed users to mount
-+ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
-
-- if (jffs2_build_filesystem(c)) {
-- D1(printk(KERN_DEBUG "build_fs failed\n"));
-- goto out_nodes;
-- }
-+ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
-
-- sb->s_op = &jffs2_super_operations;
-+ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
-+ err, nd.dentry->d_inode));
-
-- 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;
-+ if (err)
-+ return ERR_PTR(err);
+
-+ err = -EINVAL;
++ /* 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) {
+
-+ if (!S_ISBLK(nd.dentry->d_inode->i_mode))
-+ goto out;
++ down(&dead_f->sem);
+
-+ 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 (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
++ 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);
+ }
-
--#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,83 +272,65 @@
-
- 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);
-+ if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+ vfree(c->blocks);
-+ else
- kfree(c->blocks);
-+ jffs2_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)
--{
-- 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)
-+static void jffs2_kill_sb(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_JFFS2_FS_WRITEBUFFER
-+ " (NAND)"
- #endif
-+ " (C) 2001-2003 Red Hat, Inc.\n");
-
-- ret = jffs2_zlib_init();
-+ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
-+ sizeof(struct jffs2_inode_info),
-+ 0, SLAB_RECLAIM_ACCOUNT,
-+ jffs2_i_init_once, NULL);
-+ if (!jffs2_inode_cachep) {
-+ printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
++ 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;
+ }
-+ ret = jffs2_compressors_init();
- if (ret) {
-- printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
-+ printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
- goto out;
- }
- ret = jffs2_create_slab_caches();
- if (ret) {
- printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-- goto out_zlib;
-+ goto out_compressors;
- }
- ret = register_filesystem(&jffs2_fs_type);
- if (ret) {
-@@ -380,17 +341,19 @@
-
- out_slab:
- jffs2_destroy_slab_caches();
-- out_zlib:
-- jffs2_zlib_exit();
-+ out_compressors:
-+ jffs2_compressors_exit();
- out:
-+ kmem_cache_destroy(jffs2_inode_cachep);
- return ret;
- }
-
- static void __exit exit_jffs2_fs(void)
- {
-- jffs2_destroy_slab_caches();
-- jffs2_zlib_exit();
- unregister_filesystem(&jffs2_fs_type);
-+ jffs2_destroy_slab_caches();
-+ jffs2_compressors_exit();
-+ kmem_cache_destroy(jffs2_inode_cachep);
- }
-
- module_init(init_jffs2_fs);
++
++ 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;
++}
--- /dev/null
-+++ linux-2.4.21/fs/jffs2/symlink-v24.c
-@@ -0,0 +1,52 @@
++++ linux-2.4.21/fs/jffs2/writev.c
+@@ -0,0 +1,50 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ *
+ */
+
-+
+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/fs.h>
++#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
-+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
-+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-+
-+struct inode_operations jffs2_symlink_inode_operations =
-+{
-+ .readlink = jffs2_readlink,
-+ .follow_link = jffs2_follow_link,
-+ .setattr = jffs2_setattr
-+};
-+
-+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
++/* 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 kvec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
+{
-+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++ unsigned long i;
++ size_t totlen = 0, thislen;
++ int ret = 0;
+
-+ if (!f->dents) {
-+ printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
-+ return -EIO;
++ for (i=0; i<count; i++) {
++ if (!vecs[i].iov_len)
++ continue;
++ ret = mtd->write(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;
+ }
-+
-+ return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
++ if (retlen)
++ *retlen = totlen;
++ return ret;
+}
+
-+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
+{
-+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
-+
-+ if (!f->dents) {
-+ printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
-+ return -EIO;
-+ }
-+
-+ return vfs_follow_link(nd, (char *)f->dents);
++ 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);
+}
---- linux-2.4.21/fs/jffs2/symlink.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/symlink.c
-@@ -3,35 +3,11 @@
- *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
++
+--- linux-2.4.21/include/asm-arm/arch-pxa/hardware.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/hardware.h
+@@ -127,16 +127,20 @@
+ * Implementation specifics
*/
-@@ -39,72 +15,49 @@
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
--#include <linux/jffs2.h>
-+#include <linux/namei.h>
- #include "nodelist.h"
+-//#ifdef CONFIG_ARCH_LUBBOCK
++#ifdef CONFIG_ARCH_LUBBOCK
+ #include "lubbock.h"
+-//#endif
++#endif
--int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
--int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+-//#ifdef CONFIG_ARCH_PXA_IDP
++#ifdef CONFIG_ARCH_PXA_IDP
+ #include "idp.h"
+-//#endif
++#endif
- struct inode_operations jffs2_symlink_inode_operations =
- {
-- readlink: jffs2_readlink,
-- follow_link: jffs2_follow_link,
-- setattr: jffs2_setattr
-+ .readlink = generic_readlink,
-+ .follow_link = jffs2_follow_link,
-+ .setattr = jffs2_setattr
- };
+-//#ifdef CONFIG_ARCH_PXA_CERF
++#ifdef CONFIG_ARCH_PXA_CERF
+ #include "cerf.h"
+-//#endif
++#endif
++
++#ifdef CONFIG_ARCH_RAMSES
++#include "ramses.h"
++#endif
--static char *jffs2_getlink(struct dentry *dentry)
-+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
- {
- struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
-- char *buf;
-- int ret;
+ #endif /* _ASM_ARCH_HARDWARE_H */
+--- linux-2.4.21/include/asm-arm/arch-pxa/irqs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/irqs.h
+@@ -105,14 +105,13 @@
+ #define S0_BVD1_STSCHG SA1111_IRQ(53)
+ #define S1_BVD1_STSCHG SA1111_IRQ(54)
-- 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;
-+ /*
-+ * We don't acquire the f->sem mutex here since the only data we
-+ * use is f->dents which in case of the symlink inode points to the
-+ * symlink's target path.
-+ *
-+ * 1. If we are here the inode has already built and f->dents has
-+ * to point to the target path.
-+ * 2. Nobody uses f->dents (if the inode is symlink's inode). The
-+ * exception is inode freeing function which frees f->dents. But
-+ * it can't be called while we are here and before VFS has
-+ * stopped using our f->dents string which we provide by means of
-+ * nd_set_link() call.
-+ */
+-#define SA1111_IRQ_MAX SA1111_IRQ(54)
-- 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);
-+ if (!f->dents) {
-+ printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
-+ return -EIO;
- }
-- return buf;
--
--}
--int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
--{
-- unsigned char *kbuf;
-- int ret;
-+ D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
+ #undef NR_IRQS
+ #define NR_IRQS (SA1111_IRQ_MAX + 1)
-- kbuf = jffs2_getlink(dentry);
-- if (IS_ERR(kbuf))
-- return PTR_ERR(kbuf);
-+ nd_set_link(nd, (char *)f->dents);
+ #endif // defined(CONFIG_SA1111)
-- ret = vfs_readlink(dentry, buffer, buflen, kbuf);
-- kfree(kbuf);
-- return ret;
-+ /*
-+ * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
-+ * since the only way that may cause f->dents to be changed is iput() operation.
-+ * But VFS will not use f->dents after iput() has been called.
-+ */
-+ return 0;
- }
+-#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
++#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
+ #if CONFIG_SA1111
+ #define LUBBOCK_IRQ(x) (SA1111_IRQ_MAX + 1 + (x))
+ #else
+@@ -132,6 +131,3 @@
+ #define NR_IRQS (LUBBOCK_LAST_IRQ + 1)
--int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
--{
-- unsigned char *buf;
-- int ret;
+ #endif // CONFIG_ARCH_LUBBOCK
-
-- buf = jffs2_getlink(dentry);
-
-- if (IS_ERR(buf))
-- return PTR_ERR(buf);
-
-- ret = vfs_follow_link(nd, buf);
-- kfree(buf);
-- return ret;
--}
+--- linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h
+@@ -1051,6 +1051,7 @@
+ #define PGSR1 __REG(0x40F00024) /* Power Manager GPIO Sleep State Register for GP[63-32] */
+ #define PGSR2 __REG(0x40F00028) /* Power Manager GPIO Sleep State Register for GP[84-64] */
+ #define RCSR __REG(0x40F00030) /* Reset Controller Status Register */
++#define PMFW __REG(0x40F00034) /* Power Manager Fast-Sleep Wakeup Configuration Register */
+
+ #define PSSR_RDH (1 << 5) /* Read Disable Hold */
+ #define PSSR_PH (1 << 4) /* Peripheral Control Hold */
--- /dev/null
-+++ linux-2.4.21/fs/jffs2/wbuf.c
-@@ -0,0 +1,1240 @@
++++ linux-2.4.21/include/asm-arm/arch-pxa/ramses.h
+@@ -0,0 +1,364 @@
+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-+ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
++ * linux/include/asm-arm/arch-pxa/ramses.h
+ *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
++ * 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.
+ *
-+ * For licensing information, see the file 'LICENCE' in this directory.
++ * Copyright (c) 2002,2003 M&N Logistik-Lösungen Online GmbH
+ *
-+ * $Id$
++ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
++ * Initial code
+ *
++ * 2002-10-08: adaption from PXA IDP to Ramses
++ *
+ */
+
-+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/crc32.h>
-+#include <linux/mtd/nand.h>
-+#include "nodelist.h"
+
-+/* For testing write failures */
-+#undef BREAKME
-+#undef BREAKMEHEADER
++/*
++ * Note: this file must be safe to include in assembly files
++ */
+
-+#ifdef BREAKME
-+static unsigned char *brokenbuf;
++#define RAMSES_FLASH_PHYS (PXA_CS0_PHYS)
++#define RAMSES_ALT_FLASH_PHYS (PXA_CS1_PHYS)
++#define RAMSES_MEDIAQ_PHYS (PXA_CS3_PHYS)
++#define RAMSES_CONTROL_PHYS (PXA_CS4_PHYS)
++#define RAMSES_IDE_PHYS (PXA_CS5_PHYS + 0x03000000)
++#define RAMSES_ETH_PHYS (PXA_CS5_PHYS + 0x03400000)
++#define RAMSES_COREVOLT_PHYS (PXA_CS5_PHYS + 0x03800000)
++#define RAMSES_CPLD_PHYS (PXA_CS5_PHYS + 0x03C00000)
++
++/*
++ * virtual memory map
++ */
++
++#define RAMSES_IDE_BASE (0xf0000000)
++#define RAMSES_IDE_SIZE (1*1024*1024)
++#define RAMSES_ETH_BASE (RAMSES_IDE_BASE + RAMSES_IDE_SIZE)
++#define RAMSES_ETH_SIZE (1*1024*1024)
++#define RAMSES_COREVOLT_BASE (RAMSES_ETH_BASE + RAMSES_ETH_SIZE)
++#define RAMSES_COREVOLT_SIZE (1*1024*1024)
++#define RAMSES_CPLD_BASE (RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_SIZE)
++#define RAMSES_CPLD_SIZE (1*1024*1024)
++#define RAMSES_CONTROL_BASE (RAMSES_CPLD_BASE + RAMSES_CPLD_SIZE)
++#define RAMSES_CONTROL_SIZE (1*1024*1024)
++
++#if (RAMSES_CONTROL_BASE + RAMSES_CONTROL_SIZE) > 0xfc000000
++#error Your custom IO space is getting a bit large !!
+#endif
+
-+/* max. erase failures before we mark a block bad */
-+#define MAX_ERASE_FAILURES 2
++#define CPLD_P2V(x) ((x) - RAMSES_CPLD_PHYS + RAMSES_CPLD_BASE)
++#define CPLD_V2P(x) ((x) - RAMSES_CPLD_BASE + RAMSES_CPLD_PHYS)
++#define CTRL_P2V(x) ((x) - RAMSES_CONTROL_PHYS + RAMSES_CONTROL_BASE)
++#define CTRL_V2P(x) ((x) - RAMSES_CONTROL_BASE + RAMSES_CONTROL_PHYS)
++#define CORE_P2V(x) ((x) - RAMSES_COREVOLT_PHYS + RAMSES_COREVOLT_BASE)
++#define CORE_V2P(x) ((x) - RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_PHYS)
+
-+/* two seconds timeout for timed wbuf-flushing */
-+#define WBUF_FLUSH_TIMEOUT 2 * HZ
++//smc91111 driver compatibility issue
++#define ETH_BASE RAMSES_ETH_BASE
+
-+struct jffs2_inodirty {
-+ uint32_t ino;
-+ struct jffs2_inodirty *next;
-+};
++#ifndef __ASSEMBLY__
++# define __CPLD_REG(x) (*((volatile unsigned long *)CPLD_P2V(x)))
++# define __CTRL_REG(x) (*((volatile unsigned long *)CTRL_P2V(x)))
++# define __CORE_REG(x) (*((volatile unsigned long *)CORE_P2V(x)))
++#else
++# define __CPLD_REG(x) CPLD_P2V(x)
++# define __CTRL_REG(x) CTRL_P2V(x)
++# define __CORE_REG(x) CORE_P2V(x)
++#endif
+
-+static struct jffs2_inodirty inodirty_nomem;
++/* CPLD addresses */
+
-+static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
-+{
-+ struct jffs2_inodirty *this = c->wbuf_inodes;
++#define RAMSES_CPLD_PERIPH_PWR_ (RAMSES_CPLD_PHYS + 0x04)
++#define RAMSES_CPLD_PERIPH_PWR __CPLD_REG(RAMSES_CPLD_PERIPH_PWR_)
++#define PER_RESET (1 << 4)
++#define PER_PWR_EN (1 << 3)
++#define USB_HOST_PWR_EN (1 << 2)
++#define CORE_VAR_EN (1 << 0)
+
-+ /* If a malloc failed, consider _everything_ dirty */
-+ if (this == &inodirty_nomem)
-+ return 1;
++#define RAMSES_CPLD_LED_CONTROL_ (RAMSES_CPLD_PHYS + 0x08)
++#define RAMSES_CPLD_LED_CONTROL __CPLD_REG(RAMSES_CPLD_LED_CONTROL_)
++#define CPLD_LED2 (1 << 6)
++#define CPLD_LED1 (1 << 5)
++#define GSM_ACTIVE (1 << 4)
+
-+ /* If ino == 0, _any_ non-GC writes mean 'yes' */
-+ if (this && !ino)
-+ return 1;
++#define RAMSES_CPLD_KB_COL_HIGH_ (RAMSES_CPLD_PHYS + 0x0C)
++#define RAMSES_CPLD_KB_COL_HIGH __CPLD_REG(RAMSES_CPLD_KB_COL_HIGH_)
++// kbch(7)..kbch(13) on bit 0..6
+
-+ /* 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;
-+}
++#define RAMSES_CPLD_KB_COL_LOW_ (RAMSES_CPLD_PHYS + 0x10)
++#define RAMSES_CPLD_KB_COL_LOW __CPLD_REG(RAMSES_CPLD_KB_COL_LOW_)
++// kbcl(0)..kbch(6) on bit 0..6
+
-+static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
-+{
-+ struct jffs2_inodirty *this;
++#define RAMSES_CPLD_PCCARD_EN_ (RAMSES_CPLD_PHYS + 0x14)
++#define RAMSES_CPLD_PCCARD_EN __CPLD_REG(RAMSES_CPLD_PCCARD_EN_)
++#define PCC1_RESET (1 << 7)
++#define PCC0_RESET (1 << 6)
++#define PCC1_ENABLE (1 << 1)
++#define PCC0_ENABLE (1 << 0)
+
-+ this = c->wbuf_inodes;
++#define RAMSES_CPLD_PCCARD_PWR_ (RAMSES_CPLD_PHYS + 0x28)
++#define RAMSES_CPLD_PCCARD_PWR __CPLD_REG(RAMSES_CPLD_PCCARD_PWR_)
++#define PCC1_PWR3 (1 << 7)
++#define PCC1_PWR2 (1 << 6)
++#define PCC1_PWR1 (1 << 5)
++#define PCC1_PWR0 (1 << 4)
++#define PCC0_PWR3 (1 << 3)
++#define PCC0_PWR2 (1 << 2)
++#define PCC0_PWR1 (1 << 1)
++#define PCC0_PWR0 (1 << 0)
+
-+ if (this != &inodirty_nomem) {
-+ while (this) {
-+ struct jffs2_inodirty *next = this->next;
-+ kfree(this);
-+ this = next;
-+ }
-+ }
-+ c->wbuf_inodes = NULL;
-+}
++#define RAMSES_CPLD_MISC_CTRL_ (RAMSES_CPLD_PHYS + 0x2C)
++#define RAMSES_CPLD_MISC_CTRL __CPLD_REG(RAMSES_CPLD_MISC_CTRL_)
++#define RAMSES_IRDA_MD1 (1 << 5)
++#define RAMSES_IRDA_MD0 (1 << 4)
++#define RAMSES_FIR (1 << 3)
+
-+static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
-+{
-+ struct jffs2_inodirty *new;
++#define RAMSES_CPLD_LCD_ (RAMSES_CPLD_PHYS + 0x30)
++#define RAMSES_CPLD_LCD __CPLD_REG(RAMSES_CPLD_LCD_)
++#define RAMSES_LCD_PINC (1 << 7)
++#define RAMSES_LCD_PUP (1 << 6)
++#define RAMSES_LCD_PCS (1 << 5)
++#define RAMSES_LCD_DISPOFF (1 << 2)
++#define RAMSES_LCD_VCC (1 << 0)
+
-+ /* Mark the superblock dirty so that kupdated will flush... */
-+ OFNI_BS_2SFFJ(c)->s_dirt = 1;
++#define RAMSES_CPLD_FLASH_WE_ (RAMSES_CPLD_PHYS + 0x34)
++#define RAMSES_CPLD_FLASH_WE __CPLD_REG(RAMSES_CPLD_FLASH_WE_)
++#define RAMSES_FLASH_WE (1 << 0)
+
-+ if (jffs2_wbuf_pending_for_ino(c, ino))
-+ return;
++/* Read-Only registers */
+
-+ 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;
-+}
++#define RAMSES_CPLD_KB_ROW_ (RAMSES_CPLD_PHYS + 0x50)
++#define RAMSES_CPLD_KB_ROW __CPLD_REG(RAMSES_CPLD_KB_ROW_)
++// kbr(0)..kbr(6) on bits 0..6
+
-+static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
-+{
-+ struct list_head *this, *next;
-+ static int n;
++#define RAMSES_CPLD_PCCARD0_STATUS_ (RAMSES_CPLD_PHYS + 0x54)
++#define RAMSES_CPLD_PCCARD0_STATUS __CPLD_REG(RAMSES_CPLD_PCCARD0_STATUS_)
++#define RAMSES_CPLD_PCCARD1_STATUS_ (RAMSES_CPLD_PHYS + 0x58)
++#define RAMSES_CPLD_PCCARD1_STATUS __CPLD_REG(RAMSES_CPLD_PCCARD1_STATUS_)
++#define _PCC_WRPROT (1 << 7)
++#define _PCC_S16 (1 << 7)
++#define _PCC_RESET (1 << 6)
++#define _PCC_IRQ (1 << 5)
++#define _PCC_INPACK (1 << 4)
++#define PCC_BVD2 (1 << 3)
++#define PCC_BVD1 (1 << 2)
++#define PCC_VS2 (1 << 1)
++#define PCC_VS1 (1 << 0)
+
-+ if (list_empty(&c->erasable_pending_wbuf_list))
-+ return;
++#define RAMSES_CPLD_MISC_STATUS_ (RAMSES_CPLD_PHYS + 0x5C)
++#define RAMSES_CPLD_MISC_STATUS __CPLD_REG(RAMSES_CPLD_MISC_STATUS_)
++#define RAMSES_MMC_WRPROT (1 << 7)
++#define RAMSES_USB_OVERCURR (1 << 4)
++#define RAMSES_CHG_STS (1 << 2)
++#define RAMSES_WALL_IN (1 << 1)
++#define RAMSES_USB_D_CON (1 << 0)
+
-+ list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
-+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++#define RAMSES_CPLD_YEAR_ (RAMSES_CPLD_PHYS + 0x60)
++#define RAMSES_CPLD_YEAR __CPLD_REG(RAMSES_CPLD_YEAR_)
+
-+ 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);
-+ }
-+ }
-+}
++#define RAMSES_CPLD_MONTH_ (RAMSES_CPLD_PHYS + 0x64)
++#define RAMSES_CPLD_MONTH __CPLD_REG(RAMSES_CPLD_MONTH_)
+
-+#define REFILE_NOTEMPTY 0
-+#define REFILE_ANYWAY 1
++#define RAMSES_CPLD_DAY_ (RAMSES_CPLD_PHYS + 0x68)
++#define RAMSES_CPLD_DAY __CPLD_REG(RAMSES_CPLD_DAY_)
+
-+static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
-+{
-+ D1(printk("About to refile bad block at %08x\n", jeb->offset));
++#define RAMSES_CPLD_REV_ (RAMSES_CPLD_PHYS + 0x6C)
++#define RAMSES_CPLD_REV __CPLD_REG(RAMSES_CPLD_REV_)
+
-+ 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_ON(allow_empty == REFILE_NOTEMPTY);
-+ /* 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));
++#define RAMSES_CPLD_VSTAT_ (RAMSES_CPLD_PHYS + 0x7C)
++#define RAMSES_CPLD_VSTAT __CPLD_REG(RAMSES_CPLD_VSTAT_)
++#define RAMSES_BWE (1 << 1)
++
++
++/* Flags for ramses_flags */
++
++#define RAMSES_FLAGS_LCD_FBTURN (1<<0)
++/* MUST stay bit 0 */
++#define RAMSES_FLAGS_SCANNER_BEAM (1<<1)
++#define RAMSES_FLAGS_KEY_SCAN (1<<2)
++#define RAMSES_FLAGS_KEY_SUSPEND (1<<3)
++#define RAMSES_FLAGS_KEY_OFF (1<<4)
++
++
++/* Offset in SMC EEPROM for LCD type */
++#define RAMSES_LCD_TYPE_OFFSET 0x23
++
++
++/* The control register on the I/O board */
++
++#define RAMSES_CONTROL_ (RAMSES_CONTROL_PHYS + 0)
++#define RAMSES_CONTROL __CTRL_REG(RAMSES_CONTROL_)
++// 5c00 = 0101 1100 0000 0000
++#define RAMSES_CONTROL_SCANNER_TRIG_ (1 << 15)
++#define RAMSES_CONTROL_SCANNER_WAKE_ (1 << 14)
++#define RAMSES_CONTROL_SCANNER_PWR (1 << 13)
++#define RAMSES_CONTROL_LED_BLUE_ (1 << 12)
++
++#define RAMSES_CONTROL_LED_ORANGE_ (1 << 11)
++#define RAMSES_CONTROL_GSM_RESET (1 << 10)
++#define RAMSES_CONTROL_GSM_BOOT (1 << 9)
++#define RAMSES_CONTROL_GSM_PWR (1 << 8)
++
++#define RAMSES_CONTROL_POWEROFF (1 << 7)
++#define RAMSES_CONTROL_USB_INTERN (1 << 6)
++#define RAMSES_CONTROL_MMC_PWR (1 << 5)
++#define RAMSES_CONTROL_UART_PWR (1 << 4)
++
++#define RAMSES_CONTROL_LCD_BLIGHT (1 << 3)
++#define RAMSES_CONTROL_USB (1 << 2)
++
++#define RAMSES_POWER_OFF() { ramses_control_shadow |= RAMSES_CONTROL_POWEROFF; RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active low
++#define RAMSES_SCANNER_TRIG_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_TRIG_OFF() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_LED_BLUE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_LED_BLUE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_LED_ORANGE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_LED_ORANGE_; RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active high
++#define RAMSES_SCANNER_ON() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_RESET; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_RESET; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_BOOT; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_BOOT; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_INTERN() { ramses_control_shadow |= RAMSES_CONTROL_USB_INTERN; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_EXTERN() { ramses_control_shadow &= ~RAMSES_CONTROL_USB_INTERN; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_ON() { ramses_control_shadow |= RAMSES_CONTROL_UART_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_UART_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_ON() { ramses_control_shadow |= RAMSES_CONTROL_MMC_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_MMC_PWR; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_ON() { ramses_control_shadow |= RAMSES_CONTROL_LCD_BLIGHT; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_LCD_BLIGHT; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_ON() { ramses_control_shadow |= RAMSES_CONTROL_USB; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_USB; RAMSES_CONTROL = ramses_control_shadow; }
++
++// Corevolt settings
++#define RAMSES_COREVOLT_ (RAMSES_COREVOLT_PHYS)
++#define RAMSES_COREVOLT __CORE_REG(RAMSES_COREVOLT_)
++
++// Battery protocol
++#define HDQ_TMP 0x02
++#define HDQ_LMD 0x05
++#define HDQ_VSB 0x0b
++#define HDQ_CACT 0x0d
++#define HDQ_SAEH 0x0f
++#define HDQ_SAEL 0x10
++#define HDQ_RCAC 0x11
++#define HDQ_DCR 0x18
++
++
++#ifndef __ASSEMBLY__
++
++/* Ramses specific functions */
++void ramses_lcd_power_on(void);
++void ramses_lcd_power_off(void);
++void ramses_lcd_backlight_on(void);
++void ramses_lcd_backlight_off(void);
++void ramses_lcd_set_intensity(int i);
++#ifdef OLDCODE
++void ramses_lcd_set_pwm1(int p);
++#endif
++void ramses_lcd_set_brightness(int b);
++void ramses_lcd_set_contrast(int c);
++int ramses_lcd_get_intensity(void);
++int ramses_lcd_get_brightness(void);
++int ramses_lcd_get_contrast(void);
++int ramses_hdq_get_reg(unsigned char reg);
++void ramses_shut_off(void);
++void ramses_set_corevolt(int volt);
++
++
++/* shadow registers for write only registers */
++extern u16 ramses_control_shadow;
++extern int ramses_corevolt_shadow;
++extern u16 ramses_lcd_type;
++extern int ramses_lcd_pwm1_shadow;
++
++
++/* flag register for various settings */
++extern unsigned int ramses_flags;
++
++/*
++ * macros to write to write only register
++ *
++ * none of these macros are protected from
++ * multiple drivers using them in interrupt context.
++ */
++
++#define WRITE_RAMSES_CONTROL(value, mask) \
++{\
++ ramses_control_shadow = ((value & mask) | (ramses_control_shadow & ~mask));\
++ RAMSES_CONTROL = ramses_control_shadow;\
++}
++#endif
++
++/*
++ * USB Host
++ *
++ * The SL811HS is selected with nCS3 and some address bits:
++ *
++ * 12 8 4
++ * nA14, nCS[3], address mask 1011 1111 1111 0000 = xBf00
++ */
++#define SL811HS_PHYS (PXA_CS3_PHYS+0xBFF0)
++#define SL811HS_DATA (PXA_CS3_PHYS+0xBFF4)
+
-+ /* 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));
-+}
+
-+/* 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];
+
-+ jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
++#define PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
+
-+ /* 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);
++/* A listing of interrupts used by external hardware devices */
+
-+ D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
++#define TOUCH_PANEL_IRQ IRQ_GPIO(21)
++#define TOUCH_PANEL_IRQ_EDGE GPIO_FALLING_EDGE
+
-+ buf = NULL;
-+ if (start < c->wbuf_ofs) {
-+ /* First affected node was already partially written.
-+ * Attempt to reread the old data into our buffer. */
++#define ETHERNET_IRQ IRQ_GPIO(4)
++#define ETHERNET_IRQ_EDGE GPIO_RISING_EDGE
+
-+ buf = kmalloc(end - start, GFP_KERNEL);
-+ if (!buf) {
-+ printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
++#define CFCARD_CD_VALID IRQ_GPIO(8)
++#define CFCARD_CD_VALID_EDGE GPIO_BOTH_EDGES
+
-+ goto read_failed;
-+ }
++#define CFCARD_RDYINT IRQ_GPIO(22)
+
-+ /* Do the read... */
-+ if (jffs2_cleanmarker_oob(c))
-+ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
-+ else
-+ ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
-+
-+ if (ret == -EBADMSG && 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");
++#define RAMSES_KEYBOARD_IRQ IRQ_GPIO(3)
++#define RAMSES_KEYBOARD_IRQ_EDGE GPIO_FALLING_EDGE
+
-+ 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;
++#define SL811HS_IRQ IRQ_GPIO(32)
++#define SL811HS_IRQ_EDGE GPIO_RISING_EDGE
+
-+ /* 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 */
++/*
++ * Macros for LED Driver
++ */
+
++/* leds 0 = ON */
++#define RAMSES_HB_LED (1<<5)
++#define RAMSES_BUSY_LED (1<<6)
+
-+ /* ... 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");
-+ kfree(buf);
-+ return;
-+ }
-+ if (end-start >= c->wbuf_pagesize) {
-+ /* Need to do another write immediately, but it's possible
-+ that this is just because the wbuf itself is completely
-+ full, and there's nothing earlier read back from the
-+ flash. Hence 'buf' isn't necessarily what we're writing
-+ from. */
-+ unsigned char *rewrite_buf = buf?:c->wbuf;
-+ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
++#define RAMSES_LEDS_MASK (RAMSES_HB_LED | RAMSES_BUSY_LED)
+
-+ 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
-+ if (jffs2_cleanmarker_oob(c))
-+ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
-+ rewrite_buf, NULL, c->oobinfo);
-+ else
-+ ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
++#define RAMSES_WRITE_LEDS(value) (RAMSES_CPLD_LED_CONTROL = ((RAMSES_CPLD_LED_CONTROL & ~(RAMSES_LEDS_MASK)) | value))
+
-+ 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);
++/*
++ * macros for MTD driver
++ */
+
-+ if (retlen) {
-+ struct jffs2_raw_node_ref *raw2;
++#define FLASH_WRITE_PROTECT_DISABLE() ((RAMSES_CPLD_FLASH_WE) &= ~(0x1))
++#define FLASH_WRITE_PROTECT_ENABLE() ((RAMSES_CPLD_FLASH_WE) |= (0x1))
+
-+ 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;
+--- linux-2.4.21/include/asm-arm/arch-pxa/time.h~pxa-timerint
++++ linux-2.4.21/include/asm-arm/arch-pxa/time.h
+@@ -33,7 +33,7 @@
+ /* IRQs are disabled before entering here from do_gettimeofday() */
+ static unsigned long pxa_gettimeoffset (void)
+ {
+- unsigned long ticks_to_match, elapsed, usec;
++ long ticks_to_match, elapsed, usec;
+
+ /* Get ticks before next timer match */
+ ticks_to_match = OSMR0 - OSCR;
+@@ -41,6 +41,10 @@
+ /* We need elapsed ticks since last match */
+ elapsed = LATCH - ticks_to_match;
+
++ /* don't get fooled by the workaround in pxa_timer_interrupt() */
++ if (elapsed <= 0)
++ return 0;
+
-+ jffs2_add_physical_node_ref(c, raw2);
-+ }
-+ return;
-+ }
-+ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+ /* Now convert them to usec */
+ usec = (unsigned long)(elapsed*tick)/LATCH;
+
+@@ -59,6 +63,15 @@
+ * IRQs are disabled inside the loop to ensure coherence between
+ * lost_ticks (updated in do_timer()) and the match reg value, so we
+ * can use do_gettimeofday() from interrupt handlers.
++ *
++ * HACK ALERT: it seems that the PXA timer regs aren't updated right
++ * away in all cases when a write occurs. We therefore compare with
++ * 8 instead of 0 in the while() condition below to avoid missing a
++ * match if OSCR has already reached the next OSMR value.
++ * Experience has shown that up to 6 ticks are needed to work around
++ * this problem, but let's use 8 to be conservative. Note that this
++ * affect things only when the timer IRQ has been delayed by nearly
++ * exactly one tick period which should be a pretty rare event.
+ */
+ do {
+ do_leds();
+@@ -68,7 +81,7 @@
+ OSSR = OSSR_M0; /* Clear match on timer 0 */
+ next_match = (OSMR0 += LATCH);
+ restore_flags( flags );
+- } while( (signed long)(next_match - OSCR) <= 0 );
++ } while( (signed long)(next_match - OSCR) <= 8 );
+ }
+
+ extern inline void setup_timer (void)
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/bug.h
+@@ -0,0 +1 @@
++/* dummy */
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/sl811-hw.h
+@@ -0,0 +1,202 @@
++/*
++File: include/asm-arm/sl811-hw.h
+
-+ c->wbuf_len = (end - start) - towrite;
-+ c->wbuf_ofs = ofs + towrite;
-+ memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
-+ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
-+ if (buf)
-+ 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;
-+ }
++19.09.2003 hne@ist1.de
++Use Kernel 2.4.20 and this source from 2.4.22
++Splitt hardware depens into file sl811-x86.h and sl811-arm.h.
++Functions as inline.
+
-+ /* 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];
++23.09.2003 hne
++Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h.
++GPRD as parameter.
+
-+ 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;
-+ }
++24.09.2003 hne
++Use Offset from ADDR to DATA instand of direct io.
+
-+ raw = first_raw;
-+ while (*raw) {
-+ uint32_t rawlen = ref_totlen(c, jeb, *raw);
++03.10.2003 hne
++Low level only for port io into hardware-include.
++*/
+
-+ D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
-+ rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
++#ifndef __LINUX_SL811_HW_H
++#define __LINUX_SL811_HW_H
+
-+ 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;
++#ifdef CONFIG_X86
++#define MAX_CONTROLERS 1 /* Max number of sl811 controllers */
++ /* Always 1 for this architecture! */
+
-+ raw = &(*raw)->next_phys;
-+ }
++#define SIZEOF_IO_REGION 1 /* Size for request/release region */
+
-+ /* 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);
++#define OFFSET_DATA_REG data_off /* Offset from ADDR_IO to DATA_IO (future) */
++ /* Can change by arg */
+
-+ ACCT_SANITY_CHECK(c,jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
++static int io = 0xf100000e; /* Base addr_io */
++static int data_off = 1; /* Offset from addr_io to addr_io */
++static int irq = 44; /* also change gprd !!! */
++static int gprd = 23; /* also change irq !!! */
+
-+ ACCT_SANITY_CHECK(c,new_jeb);
-+ D1(ACCT_PARANOIA_CHECK(new_jeb));
++MODULE_PARM(io,"i");
++MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e");
++MODULE_PARM(data_off,"i");
++MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)");
++MODULE_PARM(irq,"i");
++MODULE_PARM_DESC(irq,"sl811 irq 44(default)");
++MODULE_PARM(gprd,"i");
++MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)");
++#endif
+
-+ spin_unlock(&c->erase_completion_lock);
++#ifdef CONFIG_ARCH_RAMSES
++#define SIZEOF_IO_REGION 8 /* Size for request/release region */
++static void *ramses_sl811hs; /* dynamically assign virtual address */
++#endif
+
-+ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++
++/*
++ * Low level: Read from Data port [arm]
++ */
++static __u8 inline sl811_read_data (struct sl811_hc *hc)
++{
++ __u8 data;
++ data = readb(hc->data_io);
++ rmb();
++//printk("%s: in %08p %02x\n", __FUNCTION__, hc->data_io, data);
++ return data;
+}
+
-+/* 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
-+*/
-+#define NOPAD 0
-+#define PAD_NOACCOUNT 1
-+#define PAD_ACCOUNTING 2
++/*
++ * Low level: Write to index register [arm]
++ */
++static void inline sl811_write_index (struct sl811_hc *hc, __u8 index)
++{
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++ writeb(index, hc->addr_io);
++ wmb();
++}
+
-+static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++/*
++ * Low level: Write to Data port [arm]
++ */
++static void inline sl811_write_data (struct sl811_hc *hc, __u8 data)
+{
-+ int ret;
-+ size_t retlen;
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++ writeb(data, hc->data_io);
++ wmb();
++}
+
-+ /* Nothing to do if not write-buffering the flash. In particular, we shouldn't
-+ del_timer() the timer we never initialised. */
-+ if (!jffs2_is_writebuffered(c))
-+ return 0;
++/*
++ * Low level: Write to index register and data port [arm]
++ */
++static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data)
++{
++ writeb(index, hc->addr_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++ writeb(data, hc->data_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++ wmb();
++}
+
-+ 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_len) /* already checked c->wbuf above */
-+ return 0;
++/*
++ * This function is board specific. It sets up the interrupt to
++ * be an edge trigger and trigger on the rising edge
++ */
++static void inline sl811_init_irq(void)
++{
++#ifdef CONFIG_X86
++ GPDR &= ~(1<<gprd);
++ set_GPIO_IRQ_edge(1<<gprd, GPIO_RISING_EDGE);
++#endif
++#ifdef CONFIG_ARCH_PXA
++ int irq_gpio_pin = IRQ_TO_GPIO_2_80(SL811HS_IRQ);
++ GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
++ set_GPIO_IRQ_edge(irq_gpio_pin, SL811HS_IRQ_EDGE);
++#endif
++}
+
-+ /* 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 && !jffs2_dataflash(c)) {
-+ c->wbuf_len = PAD(c->wbuf_len);
++/*****************************************************************
++ *
++ * Function Name: release_regions [arm]
++ *
++ * This function is board specific. It release all io address
++ * from memory (if can).
++ *
++ * Input: struct sl811_hc * *
++ *
++ * Return value : 0 = OK
++ *
++ *****************************************************************/
++static void inline sl811_release_regions(struct sl811_hc *hc)
++{
++#ifdef CONFIG_X86
++ if (hc->addr_io)
++ release_region(hc->addr_io, SIZEOF_IO_REGION);
++ hc->addr_io = 0;
+
-+ /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
-+ with 8 byte page size */
-+ memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - 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));
-+ }
++ if (hc->data_io)
++ release_region(hc->data_io, SIZEOF_IO_REGION);
++ hc->data_io = 0;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++ if (ramses_sl811hs) {
++ iounmap(ramses_sl811hs);
++ release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
+ }
-+ /* 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
++ hc->addr_io = 0;
++ hc->data_io = 0;
++ RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN;
++ RAMSES_USB_BUS_OFF();
+#endif
-+
-+ if (jffs2_cleanmarker_oob(c))
-+ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
-+ else
-+ ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
-+
-+ 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;
++/*****************************************************************
++ *
++ * Function Name: request_regions [arm]
++ *
++ * This function is board specific. It request all io address and
++ * maps into memory (if can).
++ *
++ * Input: struct sl811_hc *
++ *
++ * Return value : 0 = OK
++ *
++ *****************************************************************/
++static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name)
++{
++#ifdef CONFIG_X86
++ if (!request_region(addr_io, SIZEOF_IO_REGION, name)) {
++ PDEBUG(3, "request address %d failed", addr_io);
++ return -EBUSY;
+ }
++ hc->addr_io = addr_io;
+
-+ spin_lock(&c->erase_completion_lock);
-+
-+ /* Adjust free size of the block if we padded. */
-+ if (pad && !jffs2_dataflash(c)) {
-+ struct jffs2_eraseblock *jeb;
-+
-+ jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
-+
-+ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
-+ (jeb==c->nextblock)?"next":"", jeb->offset));
-+
-+ /* 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 (jeb->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",
-+ jeb->offset, jeb->free_size);
-+ BUG();
-+ }
-+ jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
-+ c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
-+ jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
-+ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) {
++ PDEBUG(3, "request address %d failed", data_io);
++ /* release_region(hc->addr_io, SIZEOF_IO_REGION); */
++ return -EBUSY;
+ }
++ hc->data_io = data_io;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++ RAMSES_USB_BUS_ON();
++ RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN;
++ mdelay(300);
+
-+ /* Stick any now-obsoleted blocks on the erase_pending_list */
-+ jffs2_refile_wbuf_blocks(c);
-+ jffs2_clear_wbuf_ino_list(c);
-+ spin_unlock(&c->erase_completion_lock);
++ if (!request_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION, name)) {
++ printk(KERN_ERR "unable to reserve region\n");
++ return -EBUSY;
++ } else {
++ ramses_sl811hs = ioremap_nocache(SL811HS_PHYS, SIZEOF_IO_REGION);
++ dbg("phys %p -> virt %p\n", SL811HS_PHYS, ramses_sl811hs);
++ if (!ramses_sl811hs) {
++ printk(KERN_ERR "unable to map region\n");
++ release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
++ return -EBUSY;
++ }
++ }
++ hc->addr_io = (unsigned long) ramses_sl811hs;
++ hc->data_io = (unsigned long) ramses_sl811hs+4;
++#endif
+
-+ 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));
-+
-+ if (!c->wbuf)
-+ return 0;
++#endif // __LINUX_SL811_HW_H
+--- linux-2.4.21/include/linux/apm_bios.h~pm
++++ linux-2.4.21/include/linux/apm_bios.h
+@@ -16,6 +16,8 @@
+ * General Public License for more details.
+ */
+
++#include <linux/pm-devices.h>
+
-+ 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;
-+ }
+ typedef unsigned short apm_event_t;
+ typedef unsigned short apm_eventinfo_t;
+
+@@ -59,6 +61,16 @@
+ };
+
+ /*
++ * Allow device specific code to register function which
++ * gets the battery power status (see arch/arm/mach-pxa/apm.c).
++ */
++extern void apm_register_get_power_status( int (*fn)(u_char *ac_line_status,
++ u_char *battery_status,
++ u_char *battery_flag,
++ u_char *battery_percentage,
++ u_short *battery_life));
+
-+ old_wbuf_ofs = c->wbuf_ofs;
-+ old_wbuf_len = c->wbuf_len;
++/*
+ * The APM function codes
+ */
+ #define APM_FUNC_INST_CHECK 0x5300
+@@ -168,6 +180,7 @@
+ /*
+ * APM Device IDs
+ */
++#ifdef _i386_
+ #define APM_DEVICE_BIOS 0x0000
+ #define APM_DEVICE_ALL 0x0001
+ #define APM_DEVICE_DISPLAY 0x0100
+@@ -181,6 +194,21 @@
+ #define APM_DEVICE_OLD_ALL 0xffff
+ #define APM_DEVICE_CLASS 0x00ff
+ #define APM_DEVICE_MASK 0xff00
++#endif
+
-+ 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"));
-+ down_write(&c->wbuf_sem);
-+ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+ /* retry flushing wbuf in case jffs2_wbuf_recover
-+ left some data in the wbuf */
-+ if (ret)
-+ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+ up_write(&c->wbuf_sem);
-+ } else while (old_wbuf_len &&
-+ old_wbuf_ofs == c->wbuf_ofs) {
++/*
++ * APM devices IDs for non-x86
++ */
++#define APM_DEVICE_ALL PM_SYS_DEV
++#define APM_DEVICE_DISPLAY PM_DISPLAY_DEV
++#define APM_DEVICE_STORAGE PM_STORAGE_DEV
++#define APM_DEVICE_PARALLEL PM_PARALLEL_DEV
++#define APM_DEVICE_SERIAL PM_SERIAL_DEV
++#define APM_DEVICE_NETWORK PM_NETWORK_DEV
++#define APM_DEVICE_PCMCIA PM_PCMCIA_DEV
++#define APM_DEVICE_BATTERY PM_BATTERY_DEV
++#define APM_DEVICE_TPANEL PM_TPANEL_DEV
+
-+ up(&c->alloc_sem);
+
+ #ifdef __KERNEL__
+ /*
+@@ -214,5 +242,6 @@
+
+ #define APM_IOC_STANDBY _IO('A', 1)
+ #define APM_IOC_SUSPEND _IO('A', 2)
++#define APM_IOC_SET_WAKEUP _IO('A', 3)
+
+ #endif /* LINUX_APM_H */
+--- linux-2.4.21/include/linux/crc32.h~mtd-cvs
++++ linux-2.4.21/include/linux/crc32.h
+@@ -46,4 +46,25 @@
+ return crc;
+ }
+
+-#endif /* _LINUX_CRC32_H */
++#ifndef CRC32_H
++#define CRC32_H
+
-+ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
++/* $Id$ */
+
-+ ret = jffs2_garbage_collect_pass(c);
-+ if (ret) {
-+ /* GC failed. Flush it with padding instead */
-+ down(&c->alloc_sem);
-+ down_write(&c->wbuf_sem);
-+ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+ /* retry flushing wbuf in case jffs2_wbuf_recover
-+ left some data in the wbuf */
-+ if (ret)
-+ ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+ up_write(&c->wbuf_sem);
-+ break;
-+ }
-+ down(&c->alloc_sem);
-+ }
++#include <linux/types.h>
+
-+ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
++extern const uint32_t crc32_table[256];
+
-+ up(&c->alloc_sem);
-+ return ret;
-+}
++/* Return a 32-bit CRC of the contents of the buffer. */
+
-+/* Pad write-buffer to end and write it, wasting space. */
-+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
++static inline uint32_t
++crc32(uint32_t val, const void *ss, int len)
+{
-+ int ret;
-+
-+ if (!c->wbuf)
-+ return 0;
-+
-+ down_write(&c->wbuf_sem);
-+ ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
-+ /* retry - maybe wbuf recover left some data in wbuf. */
-+ if (ret)
-+ ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
-+ up_write(&c->wbuf_sem);
-+
-+ return ret;
++ const unsigned char *s = ss;
++ while (--len >= 0)
++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
++ return val;
+}
+
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
-+#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
-+#else
-+#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
-+#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
+#endif
++#endif
+--- /dev/null
++++ linux-2.4.21/include/linux/firmware.h
+@@ -0,0 +1,20 @@
++#ifndef _LINUX_FIRMWARE_H
++#define _LINUX_FIRMWARE_H
++#include <linux/module.h>
++#include <linux/types.h>
++#define FIRMWARE_NAME_MAX 30
++struct firmware {
++ size_t size;
++ u8 *data;
++};
++int request_firmware (const struct firmware **fw, const char *name,
++ const char *device);
++int request_firmware_nowait (
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context));
++/* On 2.5 'device' is 'struct device *' */
++
++void release_firmware (const struct firmware *fw);
++void register_firmware (const char *name, const u8 *data, size_t size);
++#endif
+--- linux-2.4.21/include/linux/fs.h~mtd-cvs
++++ linux-2.4.21/include/linux/fs.h
+@@ -1376,6 +1376,7 @@
+ extern void iput(struct inode *);
+ extern void force_delete(struct inode *);
+ extern struct inode * igrab(struct inode *);
++extern struct inode * ilookup(struct super_block *, unsigned long);
+ extern ino_t iunique(struct super_block *, ino_t);
+
+ typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+--- linux-2.4.21/include/linux/i2c-id.h~i2c-ds1337
++++ linux-2.4.21/include/linux/i2c-id.h
+@@ -95,13 +95,14 @@
+ #define I2C_DRIVERID_ADV717x 48 /* ADV 7175/7176 video encoder */
+ #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_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */
++#define I2C_DRIVERID_DS1337 52 /* DS1337 real time clock */
+
+
+
+-#define I2C_DRIVERID_DS1307 46 /* real time clock: DS1307 */
+-#define I2C_DRIVERID_24LC64 47 /* EEprom 24LC64 */
+-#define I2C_DRIVERID_FM24CLB4 48 /* EEprom FM24CLB4 */
++//#define I2C_DRIVERID_DS1307 46 /* real time clock: DS1307 */
++//#define I2C_DRIVERID_24LC64 47 /* EEprom 24LC64 */
++//#define I2C_DRIVERID_FM24CLB4 48 /* EEprom FM24CLB4 */
+
+ #define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */
+ #define I2C_DRIVERID_EXP1 0xF1
+--- linux-2.4.21/include/linux/input.h~bluetooth
++++ linux-2.4.21/include/linux/input.h
+@@ -472,7 +472,8 @@
+ #define BUS_PCI 0x01
+ #define BUS_ISAPNP 0x02
+ #define BUS_USB 0x03
+-#define BUS_HIL 0x04
++#define BUS_HIL 0x04
++#define BUS_BLUETOOTH 0x05
+
+ #define BUS_ISA 0x10
+ #define BUS_I8042 0x11
+--- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2.h
+@@ -1,50 +1,30 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in the
++ * jffs2 directory.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #ifndef __LINUX_JFFS2_H__
+ #define __LINUX_JFFS2_H__
+
+-#include <asm/types.h>
++/* 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. */
+
-+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
-+{
-+ struct kvec 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 (!jffs2_is_writebuffered(c))
-+ return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
-+
-+ down_write(&c->wbuf_sem);
-+
-+ /* 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);
-+ }
-+
-+ /* Fixup the wbuf if we are moving to a new eraseblock. The checks below
-+ fail for ECC'd NOR because cleanmarker == 16, so a block starts at
-+ xxx0010. */
-+ if (jffs2_nor_ecc(c)) {
-+ if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
-+ 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 (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
-+ /* 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(c, PAD_NOACCOUNT);
-+ 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;
-+ goto exit;
-+ }
-+ }
-+ /* set pointer to new block */
-+ c->wbuf_ofs = PAGE_DIV(to);
-+ c->wbuf_len = PAGE_MOD(to);
-+ }
+ #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
+
+@@ -63,6 +43,8 @@
+ #define JFFS2_COMPR_COPY 0x04
+ #define JFFS2_COMPR_DYNRUBIN 0x05
+ #define JFFS2_COMPR_ZLIB 0x06
++#define JFFS2_COMPR_LZO 0x07
++#define JFFS2_COMPR_LZARI 0x08
+ /* Compatibility flags. */
+ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
+ #define JFFS2_NODE_ACCURATE 0x2000
+@@ -78,16 +60,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 +74,46 @@
+ compression type */
+
+
++/* These can go once we've made sure we've caught all uses without
++ byteswapping */
+
-+ 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();
-+ }
++typedef struct {
++ uint32_t v32;
++} __attribute__((packed)) jint32_t;
+
-+ /* 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();
-+ }
++typedef struct {
++ uint32_t m;
++} __attribute__((packed)) jmode_t;
+
-+ invec = 0;
-+ outvec = 0;
++typedef struct {
++ uint16_t v16;
++} __attribute__((packed)) jint16_t;
+
-+ /* Fill writebuffer first, if already in use */
-+ if (c->wbuf_len) {
-+ uint32_t invec_ofs = 0;
+ 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 +124,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 {
+--- linux-2.4.21/include/linux/jffs2_fs_i.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_i.h
+@@ -1,22 +1,13 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #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 <linux/pipe_fs_i.h>
+-#endif
++#include <linux/version.h>
++#include <linux/rbtree.h>
++#include <asm/semaphore.h>
+
+ 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 +17,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 +35,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 */
+-
+--- linux-2.4.21/include/linux/jffs2_fs_sb.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_sb.h
+@@ -1,18 +1,23 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef _JFFS2_FS_SB
+ #define _JFFS2_FS_SB
+
+ #include <linux/types.h>
+ #include <linux/spinlock.h>
++#include <linux/workqueue.h>
+ #include <linux/completion.h>
+ #include <asm/semaphore.h>
++#include <linux/timer.h>
++#include <linux/wait.h>
+ #include <linux/list.h>
+-
+-#define INOCACHE_HASHSIZE 1
++#include <linux/rwsem.h>
+
+ #define JFFS2_SB_FLAG_RO 1
+-#define JFFS2_SB_FLAG_MOUNTING 2
++#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
++#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
+
-+ /* 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;
++struct jffs2_inodirty;
+
+ /* A struct for the overall file system control. Pointers to
+ jffs2_sb_info structs are named `c' in the source code.
+@@ -21,36 +26,44 @@
+ struct jffs2_sb_info {
+ struct mtd_info *mtd;
+
+- __u32 highest_ino;
++ uint32_t highest_ino;
++ uint32_t checked_ino;
+
-+ thislen = c->wbuf_pagesize - c->wbuf_len;
+ 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;
++ out-of-order writing of nodes. And GC. */
++ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
++ (i.e. zero for OOB CLEANMARKER */
+
+- __u32 nr_free_blocks;
+- __u32 nr_erasing_blocks;
++ 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;
+
+- __u32 nr_blocks;
++ uint32_t nr_free_blocks;
++ uint32_t nr_erasing_blocks;
+
-+ if (thislen >= invecs[invec].iov_len)
-+ thislen = invecs[invec].iov_len;
-+
-+ invec_ofs = thislen;
++ /* 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 */
+
-+ 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, NOPAD);
-+ 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;
-+ goto exit;
-+ }
-+ outvec_to += donelen;
-+ c->wbuf_ofs = outvec_to;
++ uint32_t nospc_dirty_size;
+
-+ /* All invecs done ? */
-+ if (invec == count)
-+ goto alldone;
++ 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,35 @@
+ 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];
+
-+ /* 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++;
-+ }
++ 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;
+
-+ /* OK, now we've flushed the wbuf and the start of the bits
-+ we have been asked to write, now to write the rest.... */
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++ /* 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;
+
-+ /* 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;
-+ }
-+ }
++ struct rw_semaphore wbuf_sem; /* Protects the write buffer */
+
-+ /* 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 */
++ /* 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 */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/blktrans.h
+@@ -0,0 +1,72 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux block layer for MTD 'translation layers'.
++ *
++ */
+
-+ if (splitvec != -1) {
-+ uint32_t remainder;
++#ifndef __MTD_TRANS_H__
++#define __MTD_TRANS_H__
+
-+ remainder = outvecs[splitvec].iov_len - split_ofs;
-+ outvecs[splitvec].iov_len = split_ofs;
++#include <asm/semaphore.h>
+
-+ /* We did cross a page boundary, so we write some now */
-+ if (jffs2_cleanmarker_oob(c))
-+ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
-+ else
-+ ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
-+
-+ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
-+ /* At this point we have no problem,
-+ c->wbuf is empty. However refile nextblock to avoid
-+ writing again to same address.
-+ */
-+ struct jffs2_eraseblock *jeb;
++struct hd_geometry;
++struct mtd_info;
++struct mtd_blktrans_ops;
++struct file;
++struct inode;
+
-+ spin_lock(&c->erase_completion_lock);
++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 */
++};
+
-+ jeb = &c->blocks[outvec_to / c->sector_size];
-+ jffs2_block_refile(c, jeb, REFILE_ANYWAY);
++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
+
-+ *retlen = 0;
-+ spin_unlock(&c->erase_completion_lock);
-+ goto exit;
-+ }
-+
-+ donelen += wbuf_retlen;
-+ c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
++struct mtd_blktrans_ops {
++ char *name;
++ int major;
++ int part_bits;
+
-+ if (remainder) {
-+ outvecs[splitvec].iov_base += split_ofs;
-+ outvecs[splitvec].iov_len = remainder;
-+ } else {
-+ splitvec++;
-+ }
++ /* 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);
+
-+ } else {
-+ splitvec = 0;
-+ }
++ /* Block layer ioctls */
++ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
++ int (*flush)(struct mtd_blktrans_dev *dev);
+
-+ /* Now splitvec points to the start of the bits we have to copy
-+ into the wbuf */
-+ wbuf_ptr = c->wbuf;
++ /* 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);
+
-+ 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;
++ /* 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);
+
-+ /* 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;
++ struct list_head devs;
++ struct list_head list;
++ struct module *owner;
+
-+ if (c->wbuf_len && ino)
-+ jffs2_wbuf_dirties_inode(c, ino);
++ struct mtd_blkcore_priv *blkcore_priv;
++};
+
-+ ret = 0;
-+
-+exit:
-+ up_write(&c->wbuf_sem);
-+ return ret;
-+}
++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);
++
+
-+/*
-+ * This is the entry for flash write.
-+ * Check, if we work on NAND FLASH, if so build an kvec 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)
++#endif /* __MTD_TRANS_H__ */
+--- linux-2.4.21/include/linux/mtd/cfi.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/cfi.h
+@@ -1,211 +1,86 @@
+
+ /* Common Flash Interface structures
+ * See http://support.intel.com/design/flash/technote/index.htm
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __MTD_CFI_H__
+ #define __MTD_CFI_H__
+
+ #include <linux/config.h>
++#include <linux/version.h>
+ #include <linux/delay.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/flashchip.h>
++#include <linux/mtd/map.h>
+ #include <linux/mtd/cfi_endian.h>
+
+-/*
+- * You can optimize the code size and performance by defining only
+- * the geometry(ies) available on your hardware.
+- * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width)
+- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
+- *
+- * By default, all (known) geometries are supported.
+- */
+-
+-#ifndef CONFIG_MTD_CFI_GEOMETRY
+-
+-/* The default case - support all but 64-bit, which has
+- a performance penalty */
+-
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#define CFIDEV_INTERLEAVE_4 (4)
+-
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#define CFIDEV_BUSWIDTH_4 (4)
+-
+-typedef __u32 cfi_word;
+-
+-#else
+-
+-/* Explicitly configured buswidth/interleave support */
+-
+ #ifdef CONFIG_MTD_CFI_I1
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I2
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I4
+-#define CFIDEV_INTERLEAVE_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I8
+-#define CFIDEV_INTERLEAVE_8 (8)
+-#endif
+-
+-#ifdef CONFIG_MTD_CFI_B1
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B2
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B4
+-#define CFIDEV_BUSWIDTH_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B8
+-#define CFIDEV_BUSWIDTH_8 (8)
+-#endif
+-
+-/* pick the largest necessary */
+-#ifdef CONFIG_MTD_CFI_B8
+-typedef __u64 cfi_word;
+-
+-/* This only works if asm/io.h is included first */
+-#ifndef __raw_readll
+-#define __raw_readll(addr) (*(volatile __u64 *)(addr))
+-#endif
+-#ifndef __raw_writell
+-#define __raw_writell(v, addr) (*(volatile __u64 *)(addr) = (v))
+-#endif
+-#define CFI_WORD_64
+-#else /* CONFIG_MTD_CFI_B8 */
+-/* All others can use 32-bits. It's probably more efficient than
+- the smaller types anyway */
+-typedef __u32 cfi_word;
+-#endif /* CONFIG_MTD_CFI_B8 */
+-
+-#endif
+-
+-/*
+- * The following macros are used to select the code to execute:
+- * cfi_buswidth_is_*()
+- * cfi_interleave_is_*()
+- * [where * is either 1, 2, 4, or 8]
+- * Those macros should be used with 'if' statements. If only one of few
+- * geometry arrangements are selected, they expand to constants thus allowing
+- * the compiler (most of them being 0) to optimize away all the unneeded code,
+- * while still validating the syntax (which is not possible with embedded
+- * #if ... #endif constructs).
+- * The exception to this is the 64-bit versions, which need an extension
+- * to the cfi_word type, and cause compiler warnings about shifts being
+- * out of range.
+- */
+-
+-#ifdef CFIDEV_INTERLEAVE_1
+-# ifdef CFIDEV_INTERLEAVE
+-# undef CFIDEV_INTERLEAVE
+-# define CFIDEV_INTERLEAVE (cfi->interleave)
+-# else
+-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
+-# endif
+-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
++#define cfi_interleave(cfi) 1
++#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
+ #else
+-# define cfi_interleave_is_1() (0)
++#define cfi_interleave_is_1(cfi) (0)
+ #endif
+
+-#ifdef CFIDEV_INTERLEAVE_2
+-# ifdef CFIDEV_INTERLEAVE
+-# undef CFIDEV_INTERLEAVE
+-# define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I2
++# ifdef cfi_interleave
++# undef cfi_interleave
++# define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
++# define cfi_interleave(cfi) 2
+ # endif
+-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
++#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
+ #else
+-# define cfi_interleave_is_2() (0)
++#define cfi_interleave_is_2(cfi) (0)
+ #endif
+
+-#ifdef CFIDEV_INTERLEAVE_4
+-# ifdef CFIDEV_INTERLEAVE
+-# undef CFIDEV_INTERLEAVE
+-# define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I4
++# ifdef cfi_interleave
++# undef cfi_interleave
++# define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
++# define cfi_interleave(cfi) 4
+ # endif
+-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
++#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
+ #else
+-# define cfi_interleave_is_4() (0)
++#define cfi_interleave_is_4(cfi) (0)
+ #endif
+
+-#ifdef CFIDEV_INTERLEAVE_8
+-# ifdef CFIDEV_INTERLEAVE
+-# undef CFIDEV_INTERLEAVE
+-# define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I8
++# ifdef cfi_interleave
++# undef cfi_interleave
++# define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
++# define cfi_interleave(cfi) 8
+ # endif
+-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
++#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
+ #else
+-# define cfi_interleave_is_8() (0)
++#define cfi_interleave_is_8(cfi) (0)
+ #endif
+
+-#ifndef CFIDEV_INTERLEAVE
+-#error You must define at least one interleave to support!
++static inline int cfi_interleave_supported(int i)
+{
-+ struct kvec vecs[1];
-+
-+ if (!jffs2_is_writebuffered(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);
++ switch (i) {
++#ifdef CONFIG_MTD_CFI_I1
++ case 1:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_1
+-# ifdef CFIDEV_BUSWIDTH
+-# undef CFIDEV_BUSWIDTH
+-# define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
+-# endif
+-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
+-#else
+-# define cfi_buswidth_is_1() (0)
++#ifdef CONFIG_MTD_CFI_I2
++ case 2:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_2
+-# ifdef CFIDEV_BUSWIDTH
+-# undef CFIDEV_BUSWIDTH
+-# define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
+-# endif
+-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
+-#else
+-# define cfi_buswidth_is_2() (0)
++#ifdef CONFIG_MTD_CFI_I4
++ case 4:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_4
+-# ifdef CFIDEV_BUSWIDTH
+-# undef CFIDEV_BUSWIDTH
+-# define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
+-# endif
+-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
+-#else
+-# define cfi_buswidth_is_4() (0)
++#ifdef CONFIG_MTD_CFI_I8
++ case 8:
+ #endif
++ return 1;
+
+-#ifdef CFIDEV_BUSWIDTH_8
+-# ifdef CFIDEV_BUSWIDTH
+-# undef CFIDEV_BUSWIDTH
+-# define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
+-# endif
+-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
+-#else
+-# define cfi_buswidth_is_8() (0)
+-#endif
++ default:
++ return 0;
++ }
+}
+
+-#ifndef CFIDEV_BUSWIDTH
+-#error You must define at least one bus width to support!
+-#endif
+
+ /* NB: these values must represents the number of bytes needed to meet the
+ * device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes.
+@@ -222,88 +97,138 @@
+
+ /* Basic Query Structure */
+ struct cfi_ident {
+- __u8 qry[3];
+- __u16 P_ID;
+- __u16 P_ADR;
+- __u16 A_ID;
+- __u16 A_ADR;
+- __u8 VccMin;
+- __u8 VccMax;
+- __u8 VppMin;
+- __u8 VppMax;
+- __u8 WordWriteTimeoutTyp;
+- __u8 BufWriteTimeoutTyp;
+- __u8 BlockEraseTimeoutTyp;
+- __u8 ChipEraseTimeoutTyp;
+- __u8 WordWriteTimeoutMax;
+- __u8 BufWriteTimeoutMax;
+- __u8 BlockEraseTimeoutMax;
+- __u8 ChipEraseTimeoutMax;
+- __u8 DevSize;
+- __u16 InterfaceDesc;
+- __u16 MaxBufWriteSize;
+- __u8 NumEraseRegions;
+- __u32 EraseRegionInfo[0]; /* Not host ordered */
++ uint8_t qry[3];
++ uint16_t P_ID;
++ uint16_t P_ADR;
++ uint16_t A_ID;
++ uint16_t A_ADR;
++ uint8_t VccMin;
++ uint8_t VccMax;
++ uint8_t VppMin;
++ uint8_t VppMax;
++ uint8_t WordWriteTimeoutTyp;
++ uint8_t BufWriteTimeoutTyp;
++ uint8_t BlockEraseTimeoutTyp;
++ uint8_t ChipEraseTimeoutTyp;
++ uint8_t WordWriteTimeoutMax;
++ uint8_t BufWriteTimeoutMax;
++ uint8_t BlockEraseTimeoutMax;
++ uint8_t ChipEraseTimeoutMax;
++ uint8_t DevSize;
++ uint16_t InterfaceDesc;
++ uint16_t MaxBufWriteSize;
++ uint8_t NumEraseRegions;
++ uint32_t EraseRegionInfo[0]; /* Not host ordered */
+ } __attribute__((packed));
+
+ /* Extended Query Structure for both PRI and ALT */
+
+ struct cfi_extquery {
+- __u8 pri[3];
+- __u8 MajorVersion;
+- __u8 MinorVersion;
++ uint8_t pri[3];
++ uint8_t MajorVersion;
++ uint8_t MinorVersion;
+ } __attribute__((packed));
+
+ /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
+
+ struct cfi_pri_intelext {
+- __u8 pri[3];
+- __u8 MajorVersion;
+- __u8 MinorVersion;
+- __u32 FeatureSupport;
+- __u8 SuspendCmdSupport;
+- __u16 BlkStatusRegMask;
+- __u8 VccOptimal;
+- __u8 VppOptimal;
+- __u8 NumProtectionFields;
+- __u16 ProtRegAddr;
+- __u8 FactProtRegSize;
+- __u8 UserProtRegSize;
++ uint8_t pri[3];
++ uint8_t MajorVersion;
++ uint8_t MinorVersion;
++ uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
++ block follows - FIXME - not currently supported */
++ uint8_t SuspendCmdSupport;
++ uint16_t BlkStatusRegMask;
++ uint8_t VccOptimal;
++ uint8_t VppOptimal;
++ uint8_t NumProtectionFields;
++ uint16_t ProtRegAddr;
++ uint8_t FactProtRegSize;
++ uint8_t UserProtRegSize;
++ uint8_t extra[0];
++} __attribute__((packed));
+
-+/*
-+ 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;
-+
-+ if (!jffs2_is_writebuffered(c))
-+ return c->mtd->read(c->mtd, ofs, len, retlen, buf);
-+
-+ /* Read flash */
-+ if (jffs2_cleanmarker_oob(c))
-+ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
-+ else
-+ ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
-+
-+ if ( (ret == -EBADMSG) && (*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;
-+ }
-+
-+ /* 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 (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
-+ return ret;
-+
-+ /* Lock only if we have reason to believe wbuf contains relevant data,
-+ so that checking an erased block during wbuf recovery space allocation
-+ does not deadlock. */
-+ down_read(&c->wbuf_sem);
++struct cfi_intelext_otpinfo {
++ uint32_t ProtRegAddr;
++ uint16_t FactGroups;
++ uint8_t FactProtRegSize;
++ uint16_t UserGroups;
++ uint8_t UserProtRegSize;
++} __attribute__((packed));
+
-+ if (ofs >= c->wbuf_ofs) {
-+ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
-+ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
-+ goto exit;
-+ 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 ? */
-+ goto exit;
-+ 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);
++struct cfi_intelext_blockinfo {
++ uint16_t NumIdentBlocks;
++ uint16_t BlockSize;
++ uint16_t MinBlockEraseCycles;
++ uint8_t BitsPerCell;
++ uint8_t BlockCap;
++} __attribute__((packed));
+
-+exit:
-+ up_read(&c->wbuf_sem);
-+ return ret;
-+}
++struct cfi_intelext_regioninfo {
++ uint16_t NumIdentPartitions;
++ uint8_t NumOpAllowed;
++ uint8_t NumOpAllowedSimProgMode;
++ uint8_t NumOpAllowedSimEraMode;
++ uint8_t NumBlockTypes;
++ struct cfi_intelext_blockinfo BlockTypes[1];
++} __attribute__((packed));
+
-+/*
-+ * 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;
++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
+
-+ /* allocate a buffer for all oob data in this sector */
-+ oob_size = c->mtd->oobsize;
-+ 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;
++struct cfi_pri_amdstd {
++ uint8_t pri[3];
++ uint8_t MajorVersion;
++ uint8_t MinorVersion;
++ uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
++ uint8_t EraseSuspend;
++ uint8_t BlkProt;
++ uint8_t TmpBlkUnprotect;
++ uint8_t BlkProtUnprot;
++ uint8_t SimultaneousOps;
++ uint8_t BurstMode;
++ uint8_t PageMode;
++ uint8_t VppMin;
++ uint8_t VppMax;
++ uint8_t TopBottom;
+ } __attribute__((packed));
+
+ struct cfi_pri_query {
+- __u8 NumFields;
+- __u32 ProtField[1]; /* Not host ordered */
++ uint8_t NumFields;
++ uint32_t ProtField[1]; /* Not host ordered */
+ } __attribute__((packed));
+
+ struct cfi_bri_query {
+- __u8 PageModeReadCap;
+- __u8 NumFields;
+- __u32 ConfField[1]; /* Not host ordered */
++ uint8_t PageModeReadCap;
++ uint8_t NumFields;
++ uint32_t ConfField[1]; /* Not host ordered */
+ } __attribute__((packed));
+
+-#define P_ID_NONE 0
+-#define P_ID_INTEL_EXT 1
+-#define P_ID_AMD_STD 2
+-#define P_ID_INTEL_STD 3
+-#define P_ID_AMD_EXT 4
+-#define P_ID_MITSUBISHI_STD 256
+-#define P_ID_MITSUBISHI_EXT 257
+-#define P_ID_RESERVED 65535
++#define P_ID_NONE 0x0000
++#define P_ID_INTEL_EXT 0x0001
++#define P_ID_AMD_STD 0x0002
++#define P_ID_INTEL_STD 0x0003
++#define P_ID_AMD_EXT 0x0004
++#define P_ID_WINBOND 0x0006
++#define P_ID_ST_ADV 0x0020
++#define P_ID_MITSUBISHI_STD 0x0100
++#define P_ID_MITSUBISHI_EXT 0x0101
++#define P_ID_SST_PAGE 0x0102
++#define P_ID_INTEL_PERFORMANCE 0x0200
++#define P_ID_INTEL_DATA 0x0210
++#define P_ID_RESERVED 0xffff
+
+
+ #define CFI_MODE_CFI 1
+ #define CFI_MODE_JEDEC 0
+
+ struct cfi_private {
+- __u16 cmdset;
++ uint16_t cmdset;
+ void *cmdset_priv;
+ int interleave;
+ int device_type;
+ int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */
+ int addr_unlock1;
+ int addr_unlock2;
+- int fast_prog;
+ struct mtd_info *(*cmdset_setup)(struct map_info *);
+ struct cfi_ident *cfiq; /* For now only one. We insist that all devs
+ must be of the same type. */
+@@ -314,107 +239,81 @@
+ 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.
+ */
+-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
++static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
+ {
+ return (cmd_ofs * type) * interleave;
+ }
+
+ /*
+- * Transforms the CFI command for the given geometry (bus width & interleave.
++ * Transforms the CFI command for the given geometry (bus width & interleave).
++ * It looks too long to be inline, but in the common case it should almost all
++ * get optimised away.
+ */
+-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
++static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
+ {
+- cfi_word val = 0;
++ map_word val = { {0} };
++ int wordwidth, words_per_bus, chip_mode, chips_per_word;
++ unsigned long onecmd;
++ int i;
+
+- if (cfi_buswidth_is_1()) {
+- /* 1 x8 device */
+- val = cmd;
+- } else if (cfi_buswidth_is_2()) {
+- if (cfi_interleave_is_1()) {
+- /* 1 x16 device in x16 mode */
+- val = cpu_to_cfi16(cmd);
+- } else if (cfi_interleave_is_2()) {
+- /* 2 (x8, x16 or x32) devices in x8 mode */
+- val = cpu_to_cfi16((cmd << 8) | cmd);
+- }
+- } else if (cfi_buswidth_is_4()) {
+- if (cfi_interleave_is_1()) {
+- /* 1 x32 device in x32 mode */
+- val = cpu_to_cfi32(cmd);
+- } else if (cfi_interleave_is_2()) {
+- /* 2 x16 device in x16 mode */
+- val = cpu_to_cfi32((cmd << 16) | cmd);
+- } else if (cfi_interleave_is_4()) {
+- /* 4 (x8, x16 or x32) devices in x8 mode */
+- val = (cmd << 16) | cmd;
+- val = cpu_to_cfi32((val << 8) | val);
+- }
+-#ifdef CFI_WORD_64
+- } else if (cfi_buswidth_is_8()) {
+- if (cfi_interleave_is_1()) {
+- /* 1 x64 device in x64 mode */
+- val = cpu_to_cfi64(cmd);
+- } else if (cfi_interleave_is_2()) {
+- /* 2 x32 device in x32 mode */
+- val = cmd;
+- val = cpu_to_cfi64((val << 32) | val);
+- } else if (cfi_interleave_is_4()) {
+- /* 4 (x16, x32 or x64) devices in x16 mode */
+- val = (cmd << 16) | cmd;
+- val = cpu_to_cfi64((val << 32) | val);
+- } else if (cfi_interleave_is_8()) {
+- /* 8 (x8, x16 or x32) devices in x8 mode */
+- val = (cmd << 8) | cmd;
+- val = (val << 16) | val;
+- val = (val << 32) | val;
+- val = cpu_to_cfi64(val);
+- }
+-#endif /* CFI_WORD_64 */
++ /* We do it this way to give the compiler a fighting chance
++ of optimising away all the crap for 'bankwidth' larger than
++ an unsigned long, in the common case where that support is
++ disabled */
++ if (map_bankwidth_is_large(map)) {
++ wordwidth = sizeof(unsigned long);
++ words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
++ } else {
++ wordwidth = map_bankwidth(map);
++ words_per_bus = 1;
+ }
+- return val;
+-}
+-#define CMD(x) cfi_build_cmd((x), map, cfi)
+
+-/*
+- * Read a value according to the bus width.
+- */
++ chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
++ chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+-{
+- if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
+- } else if (cfi_buswidth_is_2()) {
+- return map->read16(map, addr);
+- } else if (cfi_buswidth_is_4()) {
+- return map->read32(map, addr);
+- } else if (cfi_buswidth_is_8()) {
+- return map->read64(map, addr);
+- } else {
+- return 0;
++ /* First, determine what the bit-pattern should be for a single
++ device, according to chip mode and endianness... */
++ switch (chip_mode) {
++ default: BUG();
++ case 1:
++ onecmd = cmd;
++ break;
++ case 2:
++ onecmd = cpu_to_cfi16(cmd);
++ break;
++ case 4:
++ onecmd = cpu_to_cfi32(cmd);
++ break;
+ }
+-}
+
+-/*
+- * Write a value according to the bus width.
+- */
++ /* Now replicate it across the size of an unsigned long, or
++ just to the bus width as appropriate */
++ switch (chips_per_word) {
++ default: BUG();
++#if BITS_PER_LONG >= 64
++ case 8:
++ onecmd |= (onecmd << (chip_mode * 32));
++#endif
++ case 4:
++ onecmd |= (onecmd << (chip_mode * 16));
++ case 2:
++ onecmd |= (onecmd << (chip_mode * 8));
++ case 1:
++ ;
+ }
-+
-+ /* Special check for first page */
-+ 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;
+
+-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
+-{
+- if (cfi_buswidth_is_1()) {
+- map->write8(map, val, addr);
+- } else if (cfi_buswidth_is_2()) {
+- map->write16(map, val, addr);
+- } else if (cfi_buswidth_is_4()) {
+- map->write32(map, val, addr);
+- } else if (cfi_buswidth_is_8()) {
+- map->write64(map, val, addr);
++ /* And finally, for the multi-word case, replicate it
++ in all words in the structure */
++ for (i=0; i < words_per_bus; i++) {
++ val.x[i] = onecmd;
+ }
+
-+ if (buf[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;
-+ }
-+ }
++ return val;
+ }
++#define CMD(x) cfi_build_cmd((x), map, cfi)
+
+ /*
+ * Sends a CFI command to a bank of flash for the given geometry.
+@@ -423,50 +322,47 @@
+ * If prev_val is non-null, it will be set to the value at the command address,
+ * before the command was written.
+ */
+-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
++static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+ struct map_info *map, struct cfi_private *cfi,
+- int type, cfi_word *prev_val)
++ int type, map_word *prev_val)
+ {
+- cfi_word val;
+- __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
++ map_word val;
++ uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+
+ val = cfi_build_cmd(cmd, map, cfi);
+
+ if (prev_val)
+- *prev_val = cfi_read(map, addr);
++ *prev_val = map_read(map, addr);
+
+- cfi_write(map, val, addr);
++ map_write(map, val, addr);
+
+ return addr - base;
+ }
+
+-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
++static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
+ {
+- if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
+- } else if (cfi_buswidth_is_2()) {
+- return cfi16_to_cpu(map->read16(map, addr));
+- } else if (cfi_buswidth_is_4()) {
+- return cfi32_to_cpu(map->read32(map, addr));
+- } else if (cfi_buswidth_is_8()) {
+- return cfi64_to_cpu(map->read64(map, addr));
++ map_word val = map_read(map, addr);
+
-+ /* we know, we are aligned :) */
-+ for (page = oob_size; page < len; page += sizeof(long)) {
-+ unsigned long dat = *(unsigned long *)(&buf[page]);
-+ if(dat != -1) {
-+ ret = 1;
-+ goto out;
-+ }
++ if (map_bankwidth_is_1(map)) {
++ return val.x[0];
++ } else if (map_bankwidth_is_2(map)) {
++ return cfi16_to_cpu(val.x[0]);
+ } else {
+- return 0;
++ /* No point in a 64-bit byteswap since that would just be
++ swapping the responses from different chips, and we are
++ only interested in one chip (a representative sample) */
++ return cfi32_to_cpu(val.x[0]);
+ }
+ }
+
+ static inline void cfi_udelay(int us)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- unsigned long t = us * HZ / 1000000;
+- if (t) {
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- schedule_timeout(t);
+- return;
+- }
+-#endif
++ if (us >= 1000) {
++ msleep((us+999)/1000);
++ } else {
+ udelay(us);
+ cond_resched();
+ }
+ }
+
+ static inline void cfi_spin_lock(spinlock_t *mutex)
+@@ -479,5 +375,28 @@
+ spin_unlock_bh(mutex);
+ }
+
++struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
++ const char* name);
++struct cfi_fixup {
++ uint16_t mfr;
++ uint16_t id;
++ void (*fixup)(struct mtd_info *mtd, void* param);
++ void* param;
++};
+
-+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[2 * NAND_MAX_OOBSIZE];
-+ 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++) {
-+ /* Check first if the block is bad. */
-+ if (c->mtd->block_isbad (c->mtd, offset)) {
-+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
-+ return 2;
-+ }
-+ /*
-+ * 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 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;
-+}
++#define CFI_MFR_ANY 0xffff
++#define CFI_ID_ANY 0xffff
+
-+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
-+{
-+ struct jffs2_unknown_node n;
-+ int ret;
-+ size_t retlen;
++#define CFI_MFR_AMD 0x0001
++#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
+
-+ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
-+ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
-+ n.totlen = cpu_to_je32(8);
++void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
+
-+ 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;
-+}
++typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
++ unsigned long adr, int len, void *thunk);
+
-+/*
-+ * On NAND we try to mark this block bad. If the block was erased more
-+ * than MAX_ERASE_FAILURES we mark it finaly bad.
-+ * 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 cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
++ loff_t ofs, size_t len, void *thunk);
+
-+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
+
+ #endif /* __MTD_CFI_H__ */
+--- linux-2.4.21/include/linux/mtd/compatmac.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/compatmac.h
+@@ -1,583 +1,223 @@
+-
+ /*
+- * mtd/include/compatmac.h
+- *
+- * $Id$
++ * $Id$
+ *
+ * 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 <linux/config.h>
+-#include <linux/module.h>
+-#ifndef LINUX_VERSION_CODE
+ #include <linux/version.h>
+-#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 <linux/types.h> /* 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 <asm/segment.h>
+-# 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 <asm/byteorder.h>
+-# 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 <linux/compatmac.h>
+-#endif // LINUX_VERSION_CODE < 0x020100
+-
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-#include <linux/vmalloc.h>
+-#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 */
+-#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 <linux/poll.h>
+-#else
+-# define __USE_OLD_SELECT__
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
++#error "This kernel is too old: not supported by this file"
+ #endif
+
+-/* 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
++ /* O(1) scheduler stuff. */
+
+-/* changed the prototype of read/write */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
++#include <linux/sched.h>
++static inline void __recalc_sigpending(void)
+{
-+ int ret;
-+
-+ /* if the count is < max, we try to write the counter to the 2nd page oob area */
-+ if( ++jeb->bad_count < MAX_ERASE_FAILURES)
-+ return 0;
-+
-+ if (!c->mtd->block_markbad)
-+ return 1; // What else can we do?
-+
-+ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
-+ ret = c->mtd->block_markbad(c->mtd, bad_offset);
-+
-+ if (ret) {
-+ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
-+ return ret;
-+ }
-+ return 1;
++ recalc_sigpending(current);
+}
-+
-+#define NAND_JFFS2_OOB16_FSDALEN 8
-+
-+static struct nand_oobinfo jffs2_oobinfo_docecc = {
-+ .useecc = MTD_NANDECC_PLACE,
-+ .eccbytes = 6,
-+ .eccpos = {0,1,2,3,4,5}
-+};
-+
-+
-+int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
-+{
-+ struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
-+
-+ /* Do this only, if we have an oob buffer */
-+ if (!c->mtd->oobsize)
-+ return 0;
-+
-+ /* Cleanmarker is out-of-band, so inline size zero */
-+ c->cleanmarker_size = 0;
-+
-+ /* Should we use autoplacement ? */
-+ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
-+ D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
-+ /* Get the position of the free bytes */
-+ if (!oinfo->oobfree[0][1]) {
-+ printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
-+ return -ENOSPC;
-+ }
-+ c->fsdata_pos = oinfo->oobfree[0][0];
-+ c->fsdata_len = oinfo->oobfree[0][1];
-+ if (c->fsdata_len > 8)
-+ c->fsdata_len = 8;
-+ } else {
-+ /* This is just a legacy fallback and should go away soon */
-+ switch(c->mtd->ecctype) {
-+ case MTD_ECC_RS_DiskOnChip:
-+ printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
-+ c->oobinfo = &jffs2_oobinfo_docecc;
-+ c->fsdata_pos = 6;
-+ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
-+ c->badblock_pos = 15;
-+ break;
-+
-+ default:
-+ D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
-+ return -EINVAL;
-+ }
-+ }
-+ return 0;
++#undef recalc_sigpending
++#define recalc_sigpending() __recalc_sigpending ()
+
+-#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
++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+ #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 <linux/init.h>
+-#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 <linux/sched.h>
+
++#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
+-
+-
+-/* 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 <asm/byteorder.h>
+-
+-#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
++ daemonize();
+
+-#ifndef THIS_MODULE
+-#ifdef MODULE
+-#define THIS_MODULE (&__this_module)
+-#else
+-#define THIS_MODULE (NULL)
+-#endif
+-#endif
++ spin_lock_irq(¤t->sigmask_lock);
++ sigfillset(¤t->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(¤t->sigmask_lock);
+}
-+
-+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++#undef daemonize
++#define daemonize(fmt, ...) do { \
++ snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \
++ __daemonize_modvers(); \
++ } while(0)
+
+-#if LINUX_VERSION_CODE < 0x20300
+-#include <linux/interrupt.h>
+-#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 <asm/softirq.h>
+-#include <linux/spinlock.h>
+-#endif
++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
+{
-+ int res;
-+
-+ /* Initialise write buffer */
-+ init_rwsem(&c->wbuf_sem);
-+ c->wbuf_pagesize = c->mtd->oobblock;
-+ c->wbuf_ofs = 0xFFFFFFFF;
-+
-+ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+ if (!c->wbuf)
-+ return -ENOMEM;
-+
-+ res = jffs2_nand_set_oobinfo(c);
-+
-+#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 res;
++ unsigned long flags;
++ unsigned long ret;
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define set_current_state(state_value) \
+- do { current->state = (state_value); } while (0)
+-#endif
++ spin_lock_irqsave(¤t->sigmask_lock, flags);
++ ret = dequeue_signal(mask, info);
++ spin_unlock_irqrestore(¤t->sigmask_lock, flags);
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
++ return ret;
+}
-+
-+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
+
+- if (do_sync)
+- fsync_dev(dev);
++static inline int allow_signal(int sig)
+{
-+ kfree(c->wbuf);
-+}
-+
-+int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
-+ c->cleanmarker_size = 0; /* No cleanmarkers needed */
-+
-+ /* Initialize write buffer */
-+ init_rwsem(&c->wbuf_sem);
-+ c->wbuf_pagesize = c->sector_size;
-+ c->wbuf_ofs = 0xFFFFFFFF;
-+
-+ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+ if (!c->wbuf)
-+ return -ENOMEM;
-+
-+ printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
-+
-+ return 0;
-+}
-+
-+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
-+ kfree(c->wbuf);
-+}
-+
-+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
-+ /* Cleanmarker is actually larger on the flashes */
-+ c->cleanmarker_size = 16;
-+
-+ /* Initialize write buffer */
-+ init_rwsem(&c->wbuf_sem);
-+ c->wbuf_pagesize = c->mtd->eccsize;
-+ c->wbuf_ofs = 0xFFFFFFFF;
-+
-+ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+ if (!c->wbuf)
-+ return -ENOMEM;
-+
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+- invalidate_buffers(dev);
++ 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;
+ }
+-#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);
++static inline int disallow_signal(int sig)
++{
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+- if (sb)
+- res = invalidate_inodes(sb);
++ spin_lock_irq(¤t->sigmask_lock);
++ sigaddset(¤t->blocked, sig);
++ recalc_sigpending();
+
+- invalidate_buffers(dev);
+- return res;
++ current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
++ spin_unlock_irq(¤t->sigmask_lock);
+ return 0;
-+}
+ }
+
-+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
-+ kfree(c->wbuf);
-+}
---- linux-2.4.21/fs/jffs2/write.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/write.c
-@@ -1,154 +1,70 @@
- /*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
++#define PF_FREEZE 0
++#define refrigerator(x) do { ; } while(0)
+ #endif
- #include <linux/kernel.h>
- #include <linux/fs.h>
--#include <linux/jffs2.h>
-+#include <linux/crc32.h>
-+#include <linux/slab.h>
-+#include <linux/pagemap.h>
- #include <linux/mtd/mtd.h>
- #include "nodelist.h"
--#include "crc32.h"
-+#include "compr.h"
+-#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; })
++ /* Module bits */
--/* 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));
+-#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; })
++#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
- ic = jffs2_alloc_inode_cache();
- if (!ic) {
-- return ERR_PTR(-ENOMEM);
-- }
-- memset(ic, 0, sizeof(*ic));
--
-- inode = new_inode(sb);
--
-- if (!inode) {
-- jffs2_free_inode_cache(ic);
-- return ERR_PTR(-ENOMEM);
-+ return -ENOMEM;
- }
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
+-struct completion {
+- struct semaphore s;
+-};
-- /* Alloc jffs2_inode_info when that's split in 2.5 */
-+ memset(ic, 0, sizeof(*ic));
+-#define complete(c) up(&(c)->s)
+-#define wait_for_completion(c) down(&(c)->s)
+-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
++ /* Random filesystem stuff, only for JFFS2 really */
-- 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);
++#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,4,9)
+-/* This came later */
+-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
++#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
-
-- 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;
+-#include <linux/genhd.h>
-
-- insert_inode_hash(inode);
-+ f->inocache->ino = ++c->highest_ino;
-+ f->inocache->state = INO_STATE_PRESENT;
-
-- return inode;
+-static inline void add_gendisk(struct gendisk *gp)
+-{
+- gp->next = gendisk_head;
+- gendisk_head = gp;
-}
-+ ri->ino = cpu_to_je32(f->inocache->ino);
-
--/* 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)
+-
+-static inline void del_gendisk(struct gendisk *gp)
-{
-- unsigned long i;
-- size_t totlen = 0, thislen;
-- int ret = 0;
-+ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
-+ jffs2_add_ino_cache(c, f->inocache);
+- struct gendisk *gd, **gdp;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++#define get_seconds() CURRENT_TIME
++#endif
-- for (i=0; i<count; i++) {
-- ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
-- totlen += thislen;
-- if (ret || thislen != vecs[i].iov_len)
+- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+- if (*gdp == gp) {
+- gd = *gdp; *gdp = gd->next;
- break;
-- to += vecs[i].iov_len;
-- }
-- if (retlen)
-- *retlen = totlen;
-- return ret;
+- }
-}
-+ 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);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
++#define generic_file_readonly_mmap generic_file_mmap
+ #endif
-+ f->highest_version = 1;
-+ ri->version = cpu_to_je32(f->highest_version);
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
--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;
+-#define module_init(func) \
+-mod_init_t init_module(void) { \
+- return func(); \
+-}
++#include <linux/kmod.h>
++#include <linux/string.h>
+
+-#define module_exit(func) \
+-mod_exit_t cleanup_module(void) { \
+- return func(); \
++static inline char *strlcpy(char *dest, const char *src, int len)
++{
++ dest[len-1] = 0;
++ return strncpy(dest, src, len-1);
}
+-#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
--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)
+-/* 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 <linux/sched.h>
+-static inline void __recalc_sigpending(void)
++static inline int do_old_request_module(const char *mod)
{
- 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]);
- }
+- recalc_sigpending(current);
++ return request_module(mod);
}
+-#undef recalc_sigpending
+-#define recalc_sigpending() __recalc_sigpending ()
+-#endif
-
--
-+#endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+-#endif
++#undef request_module
++#define request_module(fmt, ...) \
++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
+
+-#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
++#endif /* 2.5.70 */
+
+-#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
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
+ #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)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
++#define kvec iovec
++#define __user
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+-#define rq_data_dir(x) ((x)->cmd)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
++#define msleep(x) \
++ set_current_state(TASK_UNINTERRUPTIBLE); \
++ schedule_timeout((x)*(HZ/1000));
+ #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))
+-
++#ifndef __iomem
++#define __iomem
+ #endif
+
+-/* Removed cos it broke stuff. Where is this required anyway?
+- * #ifndef QUEUE_EMPTY
+- * #define QUEUE_EMPTY (!CURRENT)
+- * #endif
++#ifndef list_for_each_entry_safe
++/**
++ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
++ * @pos: the type * to use as a loop counter.
++ * @n: another type * to use as temporary storage
++ * @head: the head for your list.
++ * @member: the name of the list_struct within the struct.
+ */
+-#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
++#define list_for_each_entry_safe(pos, n, head, member) \
++ for (pos = list_entry((head)->next, typeof(*pos), member), \
++ n = list_entry(pos->member.next, typeof(*pos), member); \
++ &pos->member != (head); \
++ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+-#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+-#define get_seconds() CURRENT_TIME
++#ifndef DEFINE_SPINLOCK
++#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
+ #endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
+-#define generic_file_readonly_mmap generic_file_mmap
++#ifndef DEFINE_RWLOCK
++#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
+ #endif
+
+ #endif /* __LINUX_MTD_COMPATMAC_H__ */
+--- linux-2.4.21/include/linux/mtd/doc2000.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/doc2000.h
+@@ -1,13 +1,21 @@
+-
+-/* Linux driver for Disk-On-Chip 2000 */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * Linux driver for Disk-On-Chip devices
++ *
++ * Copyright (C) 1999 Machine Vision Holdings, Inc.
++ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
++ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * Copyright (C) 2002-2003 SnapGear Inc
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_DOC2000_H__
+ #define __MTD_DOC2000_H__
+
+ #include <linux/mtd/mtd.h>
++#include <asm/semaphore.h>
+
+ #define DoC_Sig1 0
+ #define DoC_Sig2 1
+@@ -38,22 +46,51 @@
+ #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))
+-#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + (reg))
++#define ReadDOC_(adr, reg) readb((void __iomem *)(adr) + (reg))
++#define WriteDOC_(d, adr, reg) writeb(d, (void __iomem *)(adr) + (reg))
+ #define DOC_IOREMAP_LEN 0x2000
+
+ #endif
+@@ -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,18 +156,21 @@
+ #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
+
+ struct DiskOnChip {
+ unsigned long physadr;
+- unsigned long virtadr;
++ void __iomem *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;
+--- linux-2.4.21/include/linux/mtd/flashchip.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/flashchip.h
+@@ -6,7 +6,7 @@
+ *
+ * (C) 2000 Red Hat. GPLd.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
- /* 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 */
+@@ -29,6 +29,7 @@
+ FL_ERASE_SUSPENDED,
+ FL_WRITING,
+ FL_WRITING_TO_BUFFER,
++ FL_OTP_WRITE,
+ FL_WRITE_SUSPENDING,
+ FL_WRITE_SUSPENDED,
+ FL_PM_SUSPENDED,
+@@ -37,13 +38,16 @@
+ FL_LOCKING,
+ FL_UNLOCKING,
+ FL_POINT,
++ FL_XIP_WHILE_ERASING,
++ FL_XIP_WHILE_WRITING,
+ FL_UNKNOWN
+ } flstate_t;
+
+
+
+ /* NOTE: confusingly, this can be used to refer to more than one chip at a time,
+- if they're interleaved. */
++ if they're interleaved. This can even refer to individual partitions on
++ the same physical chip when present. */
+
+ struct flchip {
+ unsigned long start; /* Offset within the map */
+@@ -58,6 +62,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
+@@ -65,8 +74,17 @@
+ int word_write_time;
+ int buffer_write_time;
+ int erase_time;
++
++ void *priv;
+ };
--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)
++/* This is used to handle contention on write/erase operations
++ between partitions of the same physical chip. */
++struct flchip_shared {
++ spinlock_t lock;
++ struct flchip *writing;
++ struct flchip *erasing;
++};
- {
-- 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;
-- struct iovec vecs[2];
-+ size_t retlen;
-+ struct kvec 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;
+ #endif /* __MTD_FLASHCHIP_H__ */
+--- linux-2.4.21/include/linux/mtd/gen_probe.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/gen_probe.h
+@@ -1,7 +1,7 @@
+ /*
+ * (C) 2001, 2001 Red Hat, Inc.
+ * GPL'd
+- * $Id$
++ * $Id$
+ */
-- writecheck(c->mtd, flash_ofs);
-+ D1(writecheck(c, flash_ofs));
+ #ifndef __LINUX_MTD_GEN_PROBE_H__
+@@ -10,12 +10,12 @@
+ #include <linux/mtd/flashchip.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <linux/bitops.h>
-- 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,37 @@
- jffs2_free_raw_node_ref(raw);
- return ERR_PTR(-ENOMEM);
- }
-- raw->flash_offset = flash_ofs;
-- raw->totlen = PAD(ri->totlen);
-- raw->next_phys = NULL;
+ 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);
+ };
-- 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;
+ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/inftl.h
+@@ -0,0 +1,57 @@
++/*
++ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ *
++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ * $Id$
++ */
+
-+ /* 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;
++#ifndef __MTD_INFTL_H__
++#define __MTD_INFTL_H__
+
-+ if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
-+ BUG_ON(!retried);
-+ D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
-+ "highest version %d -> updating dnode\n",
-+ je32_to_cpu(ri->version), f->highest_version));
-+ ri->version = cpu_to_je32(++f->highest_version);
-+ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
-+ }
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include nftl-user.h instead?
++#endif
+
-+ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
-+ (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
+
- 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);
++#include <mtd/inftl-user.h>
+
- /* Mark the space as dirtied */
- if (retlen) {
- /* Doesn't belong to any inode */
-@@ -229,48 +162,98 @@
- 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];
++#ifndef INFTL_MAJOR
++#define INFTL_MAJOR 94
++#endif
++#define INFTL_PARTN_BITS 4
+
-+ retried = 1;
++#ifdef __KERNEL__
+
-+ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
-+
-+ ACCT_SANITY_CHECK(c,jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
++struct INFTLrecord {
++ struct mtd_blktrans_dev mbd;
++ __u16 MediaUnit;
++ __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;
++ struct nand_oobinfo oobinfo;
++};
+
-+ 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));
++int INFTL_mount(struct INFTLrecord *s);
++int INFTL_formatblock(struct INFTLrecord *s, int block);
+
-+ ACCT_SANITY_CHECK(c,jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
++#endif /* __KERNEL__ */
+
-+ 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);
++#endif /* __MTD_INFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/jedec.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/jedec.h
+@@ -7,14 +7,13 @@
+ *
+ * See the AMD flash databook for information on how to operate the interface.
+ *
+- * $Id$
++ * $Id$
+ */
- /* Link into per-inode list */
-+ spin_lock(&c->erase_completion_lock);
- raw->next_in_ino = f->inocache->nodes;
- f->inocache->nodes = raw;
-+ spin_unlock(&c->erase_completion_lock);
+ #ifndef __LINUX_MTD_JEDEC_H__
+ #define __LINUX_MTD_JEDEC_H__
-- 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);
-+ }
+ #include <linux/types.h>
+-#include <linux/mtd/map.h>
-- f->inocache->nodes = raw;
- return fn;
- }
+ #define MAX_JEDEC_CHIPS 16
--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;
-- struct iovec vecs[2];
-+ size_t retlen;
-+ struct kvec vecs[2];
-+ int retried = 0;
- int ret;
+--- linux-2.4.21/include/linux/mtd/map.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/map.h
+@@ -1,23 +1,176 @@
-- 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));
+ /* Overhauled routines for dealing with different mmap regions of flash */
+-/* $Id$ */
++/* $Id$ */
-- 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 +274,457 @@
- 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;
+ #ifndef __LINUX_MTD_MAP_H__
+ #define __LINUX_MTD_MAP_H__
-- 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;
+ #include <linux/config.h>
+ #include <linux/types.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/mtd/compatmac.h>
++#include <asm/unaligned.h>
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/bug.h>
+
-+ 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;
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++#define map_bankwidth(map) 1
++#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
++#define map_bankwidth_is_large(map) (0)
++#define map_words(map) (1)
++#define MAX_MAP_BANKWIDTH 1
++#else
++#define map_bankwidth_is_1(map) (0)
++#endif
+
-+ if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
-+ BUG_ON(!retried);
-+ D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
-+ "highest version %d -> updating dirent\n",
-+ je32_to_cpu(rd->version), f->highest_version));
-+ rd->version = cpu_to_je32(++f->highest_version);
-+ fd->version = je32_to_cpu(rd->version);
-+ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++# ifdef map_bankwidth
++# undef map_bankwidth
++# define map_bankwidth(map) ((map)->bankwidth)
++# else
++# define map_bankwidth(map) 2
++# define map_bankwidth_is_large(map) (0)
++# define map_words(map) (1)
++# endif
++#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 2
++#else
++#define map_bankwidth_is_2(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++# ifdef map_bankwidth
++# undef map_bankwidth
++# define map_bankwidth(map) ((map)->bankwidth)
++# else
++# define map_bankwidth(map) 4
++# define map_bankwidth_is_large(map) (0)
++# define map_words(map) (1)
++# endif
++#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 4
++#else
++#define map_bankwidth_is_4(map) (0)
++#endif
++
++/* ensure we never evaluate anything shorted than an unsigned long
++ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
++
++#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++# ifdef map_bankwidth
++# undef map_bankwidth
++# define map_bankwidth(map) ((map)->bankwidth)
++# if BITS_PER_LONG < 64
++# undef map_bankwidth_is_large
++# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++# undef map_words
++# define map_words(map) map_calc_words(map)
++# endif
++# else
++# define map_bankwidth(map) 8
++# define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
++# define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 8
++#else
++#define map_bankwidth_is_8(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++# ifdef map_bankwidth
++# undef map_bankwidth
++# define map_bankwidth(map) ((map)->bankwidth)
++# undef map_bankwidth_is_large
++# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++# undef map_words
++# define map_words(map) map_calc_words(map)
++# else
++# define map_bankwidth(map) 16
++# define map_bankwidth_is_large(map) (1)
++# define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 16
++#else
++#define map_bankwidth_is_16(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++# ifdef map_bankwidth
++# undef map_bankwidth
++# define map_bankwidth(map) ((map)->bankwidth)
++# undef map_bankwidth_is_large
++# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++# undef map_words
++# define map_words(map) map_calc_words(map)
++# else
++# define map_bankwidth(map) 32
++# define map_bankwidth_is_large(map) (1)
++# define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 32
++#else
++#define map_bankwidth_is_32(map) (0)
++#endif
++
++#ifndef map_bankwidth
++#error "No bus width supported. What's the point?"
++#endif
++
++static inline int map_bankwidth_supported(int w)
++{
++ switch (w) {
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++ case 1:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++ case 2:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++ case 4:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++ case 8:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++ case 16:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++ case 32:
++#endif
++ return 1;
++
++ default:
++ return 0;
+ }
++}
++
++#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
++
++typedef union {
++ unsigned long x[MAX_MAP_LONGS];
++} map_word;
+
+ /* 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
+ paging etc. correctly if your device needs it. Then you pass it off
+- to a chip driver which deals with a mapped device - generally either
+- do_cfi_probe() or do_ram_probe(), either of which will return a
+- struct mtd_info if they liked what they saw. At which point, you
+- fill in the mtd->module with your own module address, and register
+- it.
++ to a chip probe routine -- either JEDEC or CFI probe or both -- via
++ do_map_probe(). If a chip is recognised, the probe code will invoke the
++ appropriate chip driver (if present) and return a struct mtd_info.
++ At which point, you fill in the mtd->module with your own module
++ address, and register it with the MTD core code. Or you could partition
++ it and register the partitions instead, or keep it for your own private
++ use; whatever.
+
+ The mtd->priv field will point to the struct map_info, and any further
+ private data required by the chip driver is linked from the
+@@ -29,39 +182,45 @@
+ struct map_info {
+ char *name;
+ unsigned long size;
+- 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);
+- /* If it returned a 'long' I'd call it readl.
+- * It doesn't.
+- * I won't.
+- * dwmw2 */
++ unsigned long phys;
++#define NO_XIP (-1UL)
+
++ void __iomem *virt;
++ void *cached;
++
++ int bankwidth; /* in octets. This isn't necessarily the width
++ of actual bus cycles -- it's the repeat interval
++ in bytes, before you are talking to the first chip again.
++ */
+
-+ 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];
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++ map_word (*read)(struct map_info *, unsigned long);
+ 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);
+
-+ retried = 1;
++ void (*write)(struct map_info *, const map_word, unsigned long);
+ void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
-+ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
-+
-+ ACCT_SANITY_CHECK(c,jeb);
-+ D1(ACCT_PARANOIA_CHECK(jeb));
+- 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
++ /* It's possible for the map driver to use cached memory in its
++ copy_from implementation (and _only_ with copy_from). However,
++ when the chip driver knows some flash area has changed contents,
++ it will signal it to the map driver through this routine to let
++ the map driver invalidate the corresponding cache as needed.
++ If there is no cache to care about this can be set to NULL. */
++ void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
+
++ /* 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 */
+
-+ 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);
-+ }
+ 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 +233,193 @@
+ 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);
+
-+ 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);
++#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)
-+ spin_lock(&c->erase_completion_lock);
-+ raw->next_in_ino = f->inocache->nodes;
- f->inocache->nodes = raw;
-+ spin_unlock(&c->erase_completion_lock);
++#define INVALIDATE_CACHED_RANGE(map, from, size) \
++ do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
+
+-/*
+- * 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)
+
-+ if (retried) {
-+ ACCT_SANITY_CHECK(c,NULL);
++static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
+ {
+- struct map_info *map = mtd->priv;
++ int i;
++ for (i=0; i<map_words(map); i++) {
++ if (val1.x[i] != val2.x[i])
++ return 0;
+ }
++ return 1;
++}
+
+- if (map->fldrv->destroy)
+- map->fldrv->destroy(mtd);
+-#ifdef CONFIG_MODULES
+- if (map->fldrv->module)
+- __MOD_DEC_USE_COUNT(map->fldrv->module);
++static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
++{
++ map_word r;
++ int i;
+
- return fd;
- }
++ for (i=0; i<map_words(map); i++) {
++ r.x[i] = val1.x[i] & val2.x[i];
++ }
++ return r;
++}
+
-+/* 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)
++static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
+{
-+ int ret = 0;
-+ uint32_t writtenlen = 0;
++ map_word r;
++ int i;
+
-+ 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;
-+ uint16_t comprtype = JFFS2_COMPR_NONE;
-+ uint32_t phys_ofs, alloclen;
-+ uint32_t datalen, cdatalen;
-+ int retried = 0;
++ for (i=0; i<map_words(map); i++) {
++ r.x[i] = val1.x[i] & ~val2.x[i];
++ }
++ return r;
++}
+
-+ retry:
-+ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
++static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
++{
++ map_word r;
++ int i;
+
-+ 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);
++ for (i=0; i<map_words(map); i++) {
++ r.x[i] = val1.x[i] | val2.x[i];
++ }
++ return r;
++}
+
-+ comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
++#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
+
-+ 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));
++static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
++{
++ int i;
+
-+ 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 & 0xff;
-+ ri->usercompr = (comprtype >> 8 ) & 0xff;
-+ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
-+ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++ for (i=0; i<map_words(map); i++) {
++ if (val1.x[i] & val2.x[i])
++ return 1;
++ }
++ return 0;
++}
+
-+ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
++static inline map_word map_word_load(struct map_info *map, const void *ptr)
++{
++ map_word r;
+
-+ jffs2_free_comprbuf(comprbuf, buf);
++ if (map_bankwidth_is_1(map))
++ r.x[0] = *(unsigned char *)ptr;
++ else if (map_bankwidth_is_2(map))
++ r.x[0] = get_unaligned((uint16_t *)ptr);
++ else if (map_bankwidth_is_4(map))
++ r.x[0] = get_unaligned((uint32_t *)ptr);
++#if BITS_PER_LONG >= 64
++ else if (map_bankwidth_is_8(map))
++ r.x[0] = get_unaligned((uint64_t *)ptr);
+ #endif
+- kfree(mtd);
++ else if (map_bankwidth_is_large(map))
++ memcpy(r.x, ptr, map->bankwidth);
+
-+ 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);
++ return r;
+ }
+
+-#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 map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
++{
++ int i;
+
-+ 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;
++ if (map_bankwidth_is_large(map)) {
++ char *dest = (char *)&orig;
++ memcpy(dest+start, buf, len);
++ } else {
++ for (i=start; i < start+len; i++) {
++ int bitpos;
++#ifdef __LITTLE_ENDIAN
++ bitpos = i*8;
++#else /* __BIG_ENDIAN */
++ bitpos = (map_bankwidth(map)-1-i)*8;
++#endif
++ orig.x[0] &= ~(0xff << bitpos);
++ orig.x[0] |= buf[i-start] << bitpos;
+ }
-+ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
-+ writtenlen += datalen;
-+ offset += datalen;
-+ writelen -= datalen;
-+ buf += datalen;
+ }
-+ *retlen = writtenlen;
-+ return ret;
++ return orig;
+}
+
-+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)
++static inline map_word map_word_ff(struct map_info *map)
+{
-+ struct jffs2_raw_dirent *rd;
-+ struct jffs2_full_dnode *fn;
-+ struct jffs2_full_dirent *fd;
-+ uint32_t alloclen, phys_ofs;
-+ int ret;
++ map_word r;
++ int i;
+
-+ /* 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;
++ for (i=0; i<map_words(map); i++) {
++ r.x[i] = ~0UL;
+ }
++ return r;
++}
+
-+ ri->data_crc = cpu_to_je32(0);
-+ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
++{
++ map_word r;
+
-+ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
++ if (map_bankwidth_is_1(map))
++ r.x[0] = __raw_readb(map->virt + ofs);
++ else if (map_bankwidth_is_2(map))
++ r.x[0] = __raw_readw(map->virt + ofs);
++ else if (map_bankwidth_is_4(map))
++ r.x[0] = __raw_readl(map->virt + ofs);
++#if BITS_PER_LONG >= 64
++ else if (map_bankwidth_is_8(map))
++ r.x[0] = __raw_readq(map->virt + ofs);
++#endif
++ else if (map_bankwidth_is_large(map))
++ memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
+
-+ D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
-+ jemode_to_cpu(ri->mode)));
++ return r;
++}
+
-+ 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;
++static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
++{
++ if (map_bankwidth_is_1(map))
++ __raw_writeb(datum.x[0], map->virt + ofs);
++ else if (map_bankwidth_is_2(map))
++ __raw_writew(datum.x[0], map->virt + ofs);
++ else if (map_bankwidth_is_4(map))
++ __raw_writel(datum.x[0], map->virt + ofs);
++#if BITS_PER_LONG >= 64
++ else if (map_bankwidth_is_8(map))
++ __raw_writeq(datum.x[0], map->virt + ofs);
++#endif
++ else if (map_bankwidth_is_large(map))
++ memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
++ mb();
++}
+
-+ 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;
-+ }
++static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ if (map->cached)
++ memcpy(to, (char *)map->cached + from, len);
++ else
++ memcpy_fromio(to, map->virt + from, len);
++}
+
-+ rd = jffs2_alloc_raw_dirent();
-+ if (!rd) {
-+ /* Argh. Now we treat it like a normal delete */
-+ jffs2_complete_reservation(c);
-+ return -ENOMEM;
-+ }
++static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(map->virt + to, from, len);
++}
+
-+ down(&dir_f->sem);
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++#define map_read(map, ofs) (map)->read(map, ofs)
++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
-+ 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));
++extern void simple_map_init(struct map_info *);
++#define map_is_linear(map) (map->phys != NO_XIP)
+
-+ 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));
++#else
++#define map_read(map, ofs) inline_map_read(map, ofs)
++#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
+
-+ 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);
-+ }
++#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
++#define map_is_linear(map) ({ (void)(map); 1; })
++
++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
+
+ #endif /* __LINUX_MTD_MAP_H__ */
+--- linux-2.4.21/include/linux/mtd/mtd.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/mtd.h
+@@ -1,123 +1,45 @@
+-
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_MTD_H__
+ #define __MTD_MTD_H__
+
+-#ifdef __KERNEL__
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include mtd-user.h instead?
++#endif
+
+ #include <linux/config.h>
+ #include <linux/version.h>
+ #include <linux/types.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/module.h>
+ #include <linux/uio.h>
+
+-#endif /* __KERNEL__ */
+-
+-struct erase_info_user {
+- u_int32_t start;
+- u_int32_t length;
+-};
+-
+-struct mtd_oob_buf {
+- u_int32_t start;
+- u_int32_t length;
+- unsigned char *ptr;
+-};
+-
++#include <linux/mtd/compatmac.h>
++#include <mtd/mtd-abi.h>
+
+ #define MTD_CHAR_MAJOR 90
+ #define MTD_BLOCK_MAJOR 31
+ #define MAX_MTD_DEVICES 16
+
+-
+-
+-#define MTD_ABSENT 0
+-#define MTD_RAM 1
+-#define MTD_ROM 2
+-#define MTD_NORFLASH 3
+-#define MTD_NANDFLASH 4
+-#define MTD_PEROM 5
+-#define MTD_OTHER 14
+-#define MTD_UNKNOWN 15
+-
+-
+-
+-#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
+-#define MTD_SET_BITS 2 // Bits can be set
+-#define MTD_ERASEABLE 4 // Has an erase function
+-#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
+-#define MTD_VOLATILE 16 // Set for RAMs
+-#define MTD_XIP 32 // eXecute-In-Place possible
+-#define MTD_OOB 64 // Out-of-band data (NAND flash)
+-#define MTD_ECC 128 // Device capable of automatic ECC
+-
+-// Some common devices / combinations of capabilities
+-#define MTD_CAP_ROM 0
+-#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+-#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
+-#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+-#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
+-
+-
+-// Types of automatic ECC/Checksum available
+-#define MTD_ECC_NONE 0 // No automatic ECC available
+-#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
+-#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
+-
+-struct mtd_info_user {
+- u_char type;
+- u_int32_t flags;
+- u_int32_t size; // Total size of the MTD
+- u_int32_t erasesize;
+- u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
+- u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
+- u_int32_t ecctype;
+- u_int32_t eccsize;
+-};
+-
+-struct region_info_user {
+- u_int32_t offset; /* At which this region starts,
+- * from the beginning of the MTD */
+- u_int32_t erasesize; /* For this region */
+- u_int32_t numblocks; /* Number of blocks in this region */
+- u_int32_t regionindex;
+-};
+-
+-#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
+-#define MEMERASE _IOW('M', 2, struct erase_info_user)
+-#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
+-#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
+-#define MEMLOCK _IOW('M', 5, struct erase_info_user)
+-#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)
+-
+-#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;
+-
+- /* User-space ioctl definitions */
+-
+-
+-#else /* __KERNEL__ */
+-
+-
+ #define MTD_ERASE_PENDING 0x01
+ #define MTD_ERASING 0x02
+ #define MTD_ERASE_SUSPEND 0x04
+ #define MTD_ERASE_DONE 0x08
+ #define MTD_ERASE_FAILED 0x10
+
++/* If the erase fails, fail_addr might indicate exactly which block failed. If
++ fail_addr = 0xffffffff, the failure was not at the device level or was not
++ specific to any particular block. */
+ struct erase_info {
+ struct mtd_info *mtd;
+ u_int32_t addr;
+ u_int32_t len;
++ u_int32_t fail_addr;
+ u_long time;
+ u_long retries;
+ u_int dev;
+@@ -147,13 +69,18 @@
+
+ u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
+ u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
++ u_int32_t oobavail; // Number of bytes in OOB area available for fs
+ 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;
+
-+ /* Link the fd into the inode's list, obsoleting an old
-+ one if necessary. */
-+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ /* Data for variable erase regions. If numeraseregions is zero,
+ * it means that the whole device has erasesize as given above.
+ */
+@@ -163,7 +90,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 +102,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);
+@@ -187,24 +113,24 @@
+ * flash devices. The user data is one time programmable but the
+ * factory data is read only.
+ */
+- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
++ int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+ int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
+- /* This function is not yet implemented */
++ int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
++ int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++ int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
+
+- /* iovec-based read/write methods. We need these especially for NAND flash,
++ /* kvec-based read/write methods. We need these especially for NAND flash,
+ with its limited number of write cycles per erase.
+ NB: The 'count' parameter is the number of _vectors_, each of
+ which contains an (ofs, len) tuple.
+ */
+- 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);
+- 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);
++ int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
++ int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from,
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
++ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
++ int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to,
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+
+ /* Sync */
+ void (*sync) (struct mtd_info *mtd);
+@@ -217,7 +143,14 @@
+ int (*suspend) (struct mtd_info *mtd);
+ void (*resume) (struct mtd_info *mtd);
+
++ /* Bad block management functions */
++ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
++ int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
+
-+ jffs2_complete_reservation(c);
-+ up(&dir_f->sem);
+ void *priv;
+
-+ return 0;
-+}
++ struct module *owner;
++ int usecount;
+ };
+
+
+@@ -226,44 +159,27 @@
+ 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;
+-
+- ret = __get_mtd_device(mtd, num);
+-
+- if (ret && ret->module && !try_inc_mod_count(ret->module))
+- return NULL;
+-
+- return ret;
+-}
++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+
+-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;
+ };
+
+
+ extern void register_mtd_user (struct mtd_notifier *new);
+ extern int unregister_mtd_user (struct mtd_notifier *old);
+
+-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
++int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen);
+
+-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
++int default_mtd_readv(struct mtd_info *mtd, struct kvec *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 +192,17 @@
+ #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 */
+
+
-+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)
++#ifdef CONFIG_MTD_PARTITIONS
++void mtd_erase_callback(struct erase_info *instr);
++#else
++static inline void mtd_erase_callback(struct erase_info *instr)
+{
-+ struct jffs2_raw_dirent *rd;
-+ struct jffs2_full_dirent *fd;
-+ uint32_t alloclen, phys_ofs;
-+ int ret;
-+
-+ if (1 /* alternative branch needs testing */ ||
-+ !jffs2_can_mark_obsolete(c)) {
-+ /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
-+
-+ 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);
-+ } else {
-+ struct jffs2_full_dirent **prev = &dir_f->dents;
-+ uint32_t nhash = full_name_hash(name, namelen);
-+
-+ down(&dir_f->sem);
-+
-+ while ((*prev) && (*prev)->nhash <= nhash) {
-+ if ((*prev)->nhash == nhash &&
-+ !memcmp((*prev)->name, name, namelen) &&
-+ !(*prev)->name[namelen]) {
-+ struct jffs2_full_dirent *this = *prev;
-+
-+ D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
-+ this->ino, ref_offset(this->raw)));
-+
-+ *prev = this->next;
-+ jffs2_mark_node_obsolete(c, (this->raw));
-+ jffs2_free_full_dirent(this);
-+ break;
-+ }
-+ prev = &((*prev)->next);
-+ }
-+ 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) {
++ if (instr->callback)
++ instr->callback(instr);
++}
++#endif
+
+ /*
+ * Debugging macro and defines
+@@ -288,13 +214,13 @@
+
+ #ifdef CONFIG_MTD_DEBUG
+ #define DEBUG(n, args...) \
+- if (n <= CONFIG_MTD_DEBUG_VERBOSE) { \
++ do { \
++ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \
+ printk(KERN_INFO args); \
+- }
++ } while(0)
+ #else /* CONFIG_MTD_DEBUG */
+-#define DEBUG(n, args...)
+-#endif /* CONFIG_MTD_DEBUG */
++#define DEBUG(n, args...) do { } while(0)
+
+-#endif /* __KERNEL__ */
++#endif /* CONFIG_MTD_DEBUG */
+
+ #endif /* __MTD_MTD_H__ */
+--- linux-2.4.21/include/linux/mtd/nand.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand.h
+@@ -2,10 +2,10 @@
+ * linux/include/linux/mtd/nand.h
+ *
+ * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+- * Steven J. Hill <sjhill@cotw.com>
++ * Steven J. Hill <sjhill@realitydiluted.com>
+ * Thomas Gleixner <tglx@linutronix.de>
+ *
+- * $Id$
++ * $Id$
+ *
+ * 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
+@@ -44,27 +44,61 @@
+ * NAND_YAFFS_OOB
+ * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL
+ * Split manufacturer and device ID structures
++ *
++ * 02-08-2004 tglx added option field to nand structure for chip anomalities
++ * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id
++ * update of nand_chip structure description
++ * 01-17-2005 dmarlin added extended commands for AG-AND device and added option
++ * for BBT_AUTO_REFRESH.
++ * 01-20-2005 dmarlin added optional pointer to hardware specific callback for
++ * extra error status checks.
+ */
+ #ifndef __LINUX_MTD_NAND_H
+ #define __LINUX_MTD_NAND_H
+
+ #include <linux/config.h>
+-#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/spinlock.h>
++#include <linux/mtd/mtd.h>
+
+-/*
+- * Searches for a NAND device
++struct mtd_info;
++/* Scan and identify a NAND device */
++extern int nand_scan (struct mtd_info *mtd, int max_chips);
++/* Free resources held by the NAND device */
++extern void nand_release (struct mtd_info *mtd);
+
-+ down(&dead_f->sem);
++/* Read raw data from the device without ECC */
++extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
+
-+ if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
-+ 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);
-+ }
++/* The maximum number of NAND chips in an array */
++#define NAND_MAX_CHIPS 8
+
-+ jffs2_complete_reservation(c);
++/* This constant declares the max. oobsize / page, which
++ * is supported now. If you add a chip with bigger oobsize/page
++ * adjust this accordingly.
+ */
+-extern int nand_scan (struct mtd_info *mtd);
++#define NAND_MAX_OOBSIZE 64
+
+ /*
+ * Constants for hardware specific CLE/ALE/NCE function
+ */
++/* Select the chip by setting nCE to low */
+ #define NAND_CTL_SETNCE 1
++/* Deselect the chip by setting nCE to high */
+ #define NAND_CTL_CLRNCE 2
++/* Select the command latch by setting CLE to high */
+ #define NAND_CTL_SETCLE 3
++/* Deselect the command latch by setting CLE to low */
+ #define NAND_CTL_CLRCLE 4
++/* Select the address latch by setting ALE to high */
+ #define NAND_CTL_SETALE 5
++/* Deselect the address latch by setting ALE to low */
+ #define NAND_CTL_CLRALE 6
++/* Set write protection by setting WP to high. Not used! */
++#define NAND_CTL_SETWP 7
++/* Clear write protection by setting WP to low. Not used! */
++#define NAND_CTL_CLRWP 8
+
+ /*
+ * Standard NAND flash commands
+@@ -75,35 +109,132 @@
+ #define NAND_CMD_READOOB 0x50
+ #define NAND_CMD_ERASE1 0x60
+ #define NAND_CMD_STATUS 0x70
++#define NAND_CMD_STATUS_MULTI 0x71
+ #define NAND_CMD_SEQIN 0x80
+ #define NAND_CMD_READID 0x90
+ #define NAND_CMD_ERASE2 0xd0
+ #define NAND_CMD_RESET 0xff
+
++/* Extended commands for large page devices */
++#define NAND_CMD_READSTART 0x30
++#define NAND_CMD_CACHEDPROG 0x15
+
-+ return 0;
-+}
++/* Extended commands for AG-AND device */
++/*
++ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
++ * there is no way to distinguish that from NAND_CMD_READ0
++ * until the remaining sequence of commands has been completed
++ * so add a high order bit and mask it off in the command.
++ */
++#define NAND_CMD_DEPLETE1 0x100
++#define NAND_CMD_DEPLETE2 0x38
++#define NAND_CMD_STATUS_MULTI 0x71
++#define NAND_CMD_STATUS_ERROR 0x72
++/* multi-bank error status (banks 0-3) */
++#define NAND_CMD_STATUS_ERROR0 0x73
++#define NAND_CMD_STATUS_ERROR1 0x74
++#define NAND_CMD_STATUS_ERROR2 0x75
++#define NAND_CMD_STATUS_ERROR3 0x76
++#define NAND_CMD_STATUS_RESET 0x7f
++#define NAND_CMD_STATUS_CLEAR 0xff
+
++/* Status bits */
++#define NAND_STATUS_FAIL 0x01
++#define NAND_STATUS_FAIL_N1 0x02
++#define NAND_STATUS_TRUE_READY 0x20
++#define NAND_STATUS_READY 0x40
++#define NAND_STATUS_WP 0x80
+
-+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;
+ /*
+ * Constants for ECC_MODES
+- *
+- * NONE: No ECC
+- * SOFT: Software ECC 3 byte ECC per 256 Byte data
+- * HW3_256: Hardware ECC 3 byte ECC per 256 Byte data
+- * HW3_512: Hardware ECC 3 byte ECC per 512 Byte data
+- *
+- *
+-*/
++ */
+
-+ rd = jffs2_alloc_raw_dirent();
-+ if (!rd)
-+ return -ENOMEM;
++/* No ECC. Usage is not recommended ! */
+ #define NAND_ECC_NONE 0
++/* Software ECC 3 byte ECC per 256 Byte data */
+ #define NAND_ECC_SOFT 1
++/* Hardware ECC 3 byte ECC per 256 Byte data */
+ #define NAND_ECC_HW3_256 2
++/* Hardware ECC 3 byte ECC per 512 Byte data */
+ #define NAND_ECC_HW3_512 3
++/* Hardware ECC 3 byte ECC per 512 Byte data */
+ #define NAND_ECC_HW6_512 4
+-#define NAND_ECC_DISKONCHIP 5
++/* Hardware ECC 8 byte ECC per 512 Byte data */
++#define NAND_ECC_HW8_512 6
++/* Hardware ECC 12 byte ECC per 2048 Byte data */
++#define NAND_ECC_HW12_2048 7
+
+ /*
+ * Constants for Hardware ECC
+-*/
++ */
++/* Reset Hardware ECC for read */
+ #define NAND_ECC_READ 0
++/* Reset Hardware ECC for write */
+ #define NAND_ECC_WRITE 1
++/* Enable Hardware ECC before syndrom is read back from flash */
++#define NAND_ECC_READSYN 2
+
-+ 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);
++/* Bit mask for flags passed to do_nand_read_ecc */
++#define NAND_GET_DEVICE 0x80
+
-+ /* 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;
++/* Option constants for bizarre disfunctionality and real
++* features
++*/
++/* Chip can not auto increment pages */
++#define NAND_NO_AUTOINCR 0x00000001
++/* Buswitdh is 16 bit */
++#define NAND_BUSWIDTH_16 0x00000002
++/* Device supports partial programming without padding */
++#define NAND_NO_PADDING 0x00000004
++/* Chip has cache program function */
++#define NAND_CACHEPRG 0x00000008
++/* Chip has copy back function */
++#define NAND_COPYBACK 0x00000010
++/* AND Chip which has 4 banks and a confusing page / block
++ * assignment. See Renesas datasheet for further information */
++#define NAND_IS_AND 0x00000020
++/* Chip has a array of 4 pages which can be read without
++ * additional ready /busy waits */
++#define NAND_4PAGE_ARRAY 0x00000040
++/* Chip requires that BBT is periodically rewritten to prevent
++ * bits from adjacent blocks from 'leaking' in altering data.
++ * This happens with the Renesas AG-AND chips, possibly others. */
++#define BBT_AUTO_REFRESH 0x00000080
+
-+ rd->type = type;
++/* Options valid for Samsung large page devices */
++#define NAND_SAMSUNG_LP_OPTIONS \
++ (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
+
-+ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
-+ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++/* Macros to identify the above */
++#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
++#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
++#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
++#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+
-+ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
-+
-+ jffs2_free_raw_dirent(rd);
++/* Mask to zero out the chip options, which come from the id table */
++#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
+
-+ if (IS_ERR(fd)) {
-+ jffs2_complete_reservation(c);
-+ up(&dir_f->sem);
-+ return PTR_ERR(fd);
-+ }
++/* Non chip related options */
++/* Use a flash based bad block table. This option is passed to the
++ * default bad block table function. */
++#define NAND_USE_FLASH_BBT 0x00010000
++/* The hw ecc generator provides a syndrome instead a ecc value on read
++ * This can only work if we have the ecc bytes directly behind the
++ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
++#define NAND_HWECC_SYNDROME 0x00020000
++/* This option skips the bbt scan during initialization. */
++#define NAND_SKIP_BBTSCAN 0x00040000
+
-+ /* File it. This will mark the old one obsolete. */
-+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++/* Options set by nand scan */
++/* Nand scan has allocated oob_buf */
++#define NAND_OOBBUF_ALLOC 0x40000000
++/* Nand scan has allocated data_buf */
++#define NAND_DATABUF_ALLOC 0x80000000
+
-+ jffs2_complete_reservation(c);
-+ up(&dir_f->sem);
+
+ /*
++ * nand_state_t - chip states
+ * Enumeration for NAND flash chip state
+ */
+ typedef enum {
+@@ -111,73 +242,137 @@
+ FL_READING,
+ FL_WRITING,
+ FL_ERASING,
+- FL_SYNCING
++ FL_SYNCING,
++ FL_CACHEDPRG,
+ } nand_state_t;
+
++/* Keep gcc happy */
++struct nand_chip;
+
+-/*
+- * NAND Private Flash Chip Data
+- *
+- * Structure overview:
+- *
+- * IO_ADDR_R - address to read the 8 I/O lines of the flash device
+- *
+- * IO_ADDR_W - address to write the 8 I/O lines of the flash device
+- *
+- * hwcontrol - hardwarespecific function for accesing control-lines
+- *
+- * dev_ready - hardwarespecific function for accesing device ready/busy line
+- *
+- * waitfunc - hardwarespecific function for wait on ready
+- *
+- * calculate_ecc - function for ecc calculation or readback from ecc hardware
+- *
+- * correct_data - function for ecc correction, matching to ecc generator (sw/hw)
+- *
+- * enable_hwecc - function to enable (reset) hardware ecc generator
+- *
+- * eccmod - mode of ecc: see constants
+- *
+- * eccsize - databytes used per ecc-calculation
+- *
+- * chip_delay - chip dependent delay for transfering data from array to read regs (tR)
+- *
+- * chip_lock - spinlock used to protect access to this structure
+- *
+- * wq - wait queue to sleep on if a NAND operation is in progress
+- *
+- * state - give the current state of the NAND device
+- *
+- * page_shift - number of address bits in a page (column address bits)
+- *
+- * data_buf - data buffer passed to/from MTD user modules
+- *
+- * data_cache - data cache for redundant page access and shadow for
+- * ECC failure
+- *
+- * cache_page - number of last valid page in page_cache
++/**
++ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
++ * @lock: protection lock
++ * @active: the mtd device which holds the controller currently
+ */
++struct nand_hw_control {
++ spinlock_t lock;
++ struct nand_chip *active;
++};
+
-+ return 0;
-+}
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/writev.c
-@@ -0,0 +1,50 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001, 2002 Red Hat, Inc.
-+ *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * For licensing information, see the file 'LICENCE' in this directory.
-+ *
-+ * $Id$
++/**
++ * struct nand_chip - NAND Private Flash Chip Data
++ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
++ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
++ * @read_byte: [REPLACEABLE] read one byte from the chip
++ * @write_byte: [REPLACEABLE] write one byte to the chip
++ * @read_word: [REPLACEABLE] read one word from the chip
++ * @write_word: [REPLACEABLE] write one word to the chip
++ * @write_buf: [REPLACEABLE] write data from the buffer to the chip
++ * @read_buf: [REPLACEABLE] read data from the chip into the buffer
++ * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
++ * @select_chip: [REPLACEABLE] select chip nr
++ * @block_bad: [REPLACEABLE] check, if the block is bad
++ * @block_markbad: [REPLACEABLE] mark the block bad
++ * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
++ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
++ * If set to NULL no access to ready/busy is available and the ready/busy information
++ * is read from the chip status register
++ * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
++ * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
++ * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware
++ * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
++ * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
++ * be provided if a hardware ECC is available
++ * @erase_cmd: [INTERN] erase command write function, selectable due to AND support
++ * @scan_bbt: [REPLACEABLE] function to scan bad block table
++ * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
++ * @eccsize: [INTERN] databytes used per ecc-calculation
++ * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step
++ * @eccsteps: [INTERN] number of ecc calculation steps per page
++ * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
++ * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
++ * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
++ * @state: [INTERN] the current state of the NAND device
++ * @page_shift: [INTERN] number of address bits in a page (column address bits)
++ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
++ * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
++ * @chip_shift: [INTERN] number of address bits in one chip
++ * @data_buf: [INTERN] internal buffer for one page + oob
++ * @oob_buf: [INTERN] oob buffer for one eraseblock
++ * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
++ * @data_poi: [INTERN] pointer to a data buffer
++ * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
++ * special functionality. See the defines for further explanation
++ * @badblockpos: [INTERN] position of the bad block marker in the oob area
++ * @numchips: [INTERN] number of physical chips
++ * @chipsize: [INTERN] the size of one chip for multichip arrays
++ * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
++ * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
++ * @autooob: [REPLACEABLE] the default (auto)placement scheme
++ * @bbt: [INTERN] bad block table pointer
++ * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
++ * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
++ * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
++ * @priv: [OPTIONAL] pointer to private chip date
++ * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
++ * (determine if errors are correctable)
++ */
++
+ struct nand_chip {
+- unsigned long IO_ADDR_R;
+- unsigned long IO_ADDR_W;
+- void (*hwcontrol)(int cmd);
+- int (*dev_ready)(void);
++ void __iomem *IO_ADDR_R;
++ void __iomem *IO_ADDR_W;
++
++ u_char (*read_byte)(struct mtd_info *mtd);
++ void (*write_byte)(struct mtd_info *mtd, u_char byte);
++ u16 (*read_word)(struct mtd_info *mtd);
++ void (*write_word)(struct mtd_info *mtd, u16 word);
++
++ 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, loff_t ofs, int getchip);
++ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
++ 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);
++ int (*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);
++ void (*erase_cmd)(struct mtd_info *mtd, int page);
++ int (*scan_bbt)(struct mtd_info *mtd);
+ int eccmode;
+ int eccsize;
++ int eccbytes;
++ int eccsteps;
+ int chip_delay;
+ spinlock_t chip_lock;
+ wait_queue_head_t wq;
+ nand_state_t state;
+ int page_shift;
++ int phys_erase_shift;
++ int bbt_erase_shift;
++ int chip_shift;
+ u_char *data_buf;
++ u_char *oob_buf;
++ int oobdirty;
+ u_char *data_poi;
+- u_char *data_cache;
+- int cache_page;
++ unsigned int options;
++ int badblockpos;
++ int numchips;
++ unsigned long chipsize;
++ int pagemask;
++ int pagebuf;
++ struct nand_oobinfo *autooob;
++ uint8_t *bbt;
++ struct nand_bbt_descr *bbt_td;
++ struct nand_bbt_descr *bbt_md;
++ struct nand_bbt_descr *badblock_pattern;
++ struct nand_hw_control *controller;
++ void *priv;
++ int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
+ };
+
+ /*
+@@ -187,46 +382,35 @@
+ #define NAND_MFR_SAMSUNG 0xec
+ #define NAND_MFR_FUJITSU 0x04
+ #define NAND_MFR_NATIONAL 0x8f
++#define NAND_MFR_RENESAS 0x07
++#define NAND_MFR_STMICRO 0x20
+
+-/*
+- * NAND Flash Device ID Structure
+- *
+- * Structure overview:
+- *
+- * name - Identify the device type
+- *
+- * id - device ID code
+- *
+- * chipshift - total number of address bits for the device which
+- * is used to calculate address offsets and the total
+- * number of bytes the device is capable of.
+- *
+- * page256 - denotes if flash device has 256 byte pages or not.
+- *
+- * pageadrlen - number of bytes minus one needed to hold the
+- * complete address into the flash array. Keep in
+- * mind that when a read or write is done to a
+- * specific address, the address is input serially
+- * 8 bits at a time. This structure member is used
+- * by the read/write routines as a loop index for
+- * shifting the address out 8 bits at a time.
++/**
++ * struct nand_flash_dev - NAND Flash Device ID Structure
+ *
+- * erasesize - size of an erase block in the flash device.
++ * @name: Identify the device type
++ * @id: device ID code
++ * @pagesize: Pagesize in bytes. Either 256 or 512 or 0
++ * If the pagesize is 0, then the real pagesize
++ * and the eraseize are determined from the
++ * extended id bytes in the chip
++ * @erasesize: Size of an erase block in the flash device.
++ * @chipsize: Total chipsize in Mega Bytes
++ * @options: Bitfield to store chip relevant options
+ */
+ struct nand_flash_dev {
+- char * name;
++ char *name;
+ int id;
+- int chipshift;
++ unsigned long pagesize;
++ unsigned long chipsize;
+ unsigned long erasesize;
+- char page256;
++ unsigned long options;
+ };
+
+-/*
+- * NAND Flash Manufacturer ID Structure
+- *
+- * name - Manufacturer name
+- *
+- * id - manufacturer ID code of device.
++/**
++ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
++ * @name: Manufacturer name
++ * @id: manufacturer ID code of device.
+ */
+ struct nand_manufacturers {
+ int id;
+@@ -236,39 +420,88 @@
+ extern struct nand_flash_dev nand_flash_ids[];
+ extern struct nand_manufacturers nand_manuf_ids[];
+
+-/*
+-* Constants for oob configuration
+-*/
+-#define NAND_BADBLOCK_POS 5
++/**
++ * struct nand_bbt_descr - bad block table descriptor
++ * @options: options for this descriptor
++ * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
++ * when bbt is searched, then we store the found bbts pages here.
++ * Its an array and supports up to 8 chips now
++ * @offs: offset of the pattern in the oob area of the page
++ * @veroffs: offset of the bbt version counter in the oob are of the page
++ * @version: version read from the bbt page during scan
++ * @len: length of the pattern, if 0 no pattern check is performed
++ * @maxblocks: maximum number of blocks to search for a bbt. This number of
++ * blocks is reserved at the end of the device where the tables are
++ * written.
++ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
++ * bad) block in the stored bbt
++ * @pattern: pattern to identify bad block table or factory marked good /
++ * bad blocks, can be NULL, if len = 0
+ *
++ * Descriptor for the bad block table marker and the descriptor for the
++ * pattern which identifies good and bad blocks. The assumption is made
++ * that the pattern and the version count are always located in the oob area
++ * of the first block.
+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/mtd/mtd.h>
-+#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 kvec *vecs,
-+ unsigned long count, loff_t to, size_t *retlen)
-+{
-+ unsigned long i;
-+ size_t totlen = 0, thislen;
-+ int ret = 0;
-+
-+ for (i=0; i<count; i++) {
-+ if (!vecs[i].iov_len)
-+ continue;
-+ ret = mtd->write(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 kvec *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);
-+}
-+
---- linux-2.4.21/include/asm-arm/arch-pxa/hardware.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/hardware.h
-@@ -127,16 +127,20 @@
- * Implementation specifics
++struct nand_bbt_descr {
++ int options;
++ int pages[NAND_MAX_CHIPS];
++ int offs;
++ int veroffs;
++ uint8_t version[NAND_MAX_CHIPS];
++ int len;
++ int maxblocks;
++ int reserved_block_code;
++ uint8_t *pattern;
++};
+
+-#define NAND_NONE_OOB 0
+-#define NAND_JFFS2_OOB 1
+-#define NAND_YAFFS_OOB 2
++/* Options for the bad block table descriptors */
+
+-#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
++/* The number of bits used per block in the bbt on the device */
++#define NAND_BBT_NRBITS_MSK 0x0000000F
++#define NAND_BBT_1BIT 0x00000001
++#define NAND_BBT_2BIT 0x00000002
++#define NAND_BBT_4BIT 0x00000004
++#define NAND_BBT_8BIT 0x00000008
++/* The bad block table is in the last good block of the device */
++#define NAND_BBT_LASTBLOCK 0x00000010
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_ABSPAGE 0x00000020
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_SEARCH 0x00000040
++/* bbt is stored per chip on multichip devices */
++#define NAND_BBT_PERCHIP 0x00000080
++/* bbt has a version counter at offset veroffs */
++#define NAND_BBT_VERSION 0x00000100
++/* Create a bbt if none axists */
++#define NAND_BBT_CREATE 0x00000200
++/* Search good / bad pattern through all pages of a block */
++#define NAND_BBT_SCANALLPAGES 0x00000400
++/* Scan block empty during good / bad block scan */
++#define NAND_BBT_SCANEMPTY 0x00000800
++/* Write bbt if neccecary */
++#define NAND_BBT_WRITE 0x00001000
++/* Read and write back block contents when writing bbt */
++#define NAND_BBT_SAVECONTENT 0x00002000
++/* Search good / bad pattern on the first and the second page */
++#define NAND_BBT_SCAN2NDPAGE 0x00004000
+
+-#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
++/* The maximum number of blocks to scan for a bbt */
++#define NAND_BBT_SCAN_MAXBLOCKS 4
+
+-#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
++extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
++extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
++extern int nand_default_bbt (struct mtd_info *mtd);
++extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
++extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
++extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf, u_char * oob_buf,
++ struct nand_oobinfo *oobsel, int flags);
+
+-#define NAND_JFFS2_OOB8_FSDAPOS 6
+-#define NAND_JFFS2_OOB16_FSDAPOS 8
+-#define NAND_JFFS2_OOB8_FSDALEN 2
+-#define NAND_JFFS2_OOB16_FSDALEN 8
++/*
++* Constants for oob configuration
++*/
++#define NAND_SMALL_BADBLOCK_POS 5
++#define NAND_LARGE_BADBLOCK_POS 0
+
+ #endif /* __LINUX_MTD_NAND_H */
+--- linux-2.4.21/include/linux/mtd/nand_ecc.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand_ecc.h
+@@ -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$
++ * $Id$
+ *
+ * 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.
*/
--//#ifdef CONFIG_ARCH_LUBBOCK
-+#ifdef CONFIG_ARCH_LUBBOCK
- #include "lubbock.h"
--//#endif
-+#endif
+-/*
+- * 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;
--//#ifdef CONFIG_ARCH_PXA_IDP
-+#ifdef CONFIG_ARCH_PXA_IDP
- #include "idp.h"
--//#endif
-+#endif
+ /*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
++int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
--//#ifdef CONFIG_ARCH_PXA_CERF
-+#ifdef CONFIG_ARCH_PXA_CERF
- #include "cerf.h"
--//#endif
-+#endif
+ /*
+ * 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);
+
-+#ifdef CONFIG_ARCH_RAMSES
-+#include "ramses.h"
-+#endif
++#endif /* __MTD_NAND_ECC_H__ */
+--- linux-2.4.21/include/linux/mtd/nftl.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nftl.h
+@@ -1,81 +1,16 @@
+-
+-/* Defines for NAND Flash Translation Layer */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
++ */
- #endif /* _ASM_ARCH_HARDWARE_H */
---- linux-2.4.21/include/asm-arm/arch-pxa/irqs.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/irqs.h
-@@ -105,14 +105,13 @@
- #define S0_BVD1_STSCHG SA1111_IRQ(53)
- #define S1_BVD1_STSCHG SA1111_IRQ(54)
+ #ifndef __MTD_NFTL_H__
+ #define __MTD_NFTL_H__
--#define SA1111_IRQ_MAX SA1111_IRQ(54)
+-#ifndef __BOOT__
+ #include <linux/mtd/mtd.h>
+-#endif
+-
+-/* Block Control Information */
+-
+-struct nftl_bci {
+- unsigned char ECCSig[6];
+- __u8 Status;
+- __u8 Status1;
+-}__attribute__((packed));
+-
+-/* Unit Control Information */
+-
+-struct nftl_uci0 {
+- __u16 VirtUnitNum;
+- __u16 ReplUnitNum;
+- __u16 SpareVirtUnitNum;
+- __u16 SpareReplUnitNum;
+-} __attribute__((packed));
+-
+-struct nftl_uci1 {
+- __u32 WearInfo;
+- __u16 EraseMark;
+- __u16 EraseMark1;
+-} __attribute__((packed));
+-
+-struct nftl_uci2 {
+- __u16 FoldMark;
+- __u16 FoldMark1;
+- __u32 unused;
+-} __attribute__((packed));
+-
+-union nftl_uci {
+- struct nftl_uci0 a;
+- struct nftl_uci1 b;
+- struct nftl_uci2 c;
+-};
+-
+-struct nftl_oob {
+- struct nftl_bci b;
+- union nftl_uci u;
+-};
+-
+-/* NFTL Media Header */
+-
+-struct NFTLMediaHeader {
+- char DataOrgID[6];
+- __u16 NumEraseUnits;
+- __u16 FirstPhysicalEUN;
+- __u32 FormattedSize;
+- unsigned char UnitSizeFactor;
+-} __attribute__((packed));
+-
+-#define MAX_ERASE_ZONES (8192 - 512)
+-
+-#define ERASE_MARK 0x3c69
+-#define SECTOR_FREE 0xff
+-#define SECTOR_USED 0x55
+-#define SECTOR_IGNORE 0x11
+-#define SECTOR_DELETED 0x00
+-
+-#define FOLD_MARK_IN_PROGRESS 0x5555
+-
+-#define ZONE_GOOD 0xff
+-#define ZONE_BAD_ORIGINAL 0
+-#define ZONE_BAD_MARKED 7
++#include <linux/mtd/blktrans.h>
- #undef NR_IRQS
- #define NR_IRQS (SA1111_IRQ_MAX + 1)
+-#ifdef __KERNEL__
++#include <mtd/nftl-user.h>
- #endif // defined(CONFIG_SA1111)
+ /* these info are used in ReplUnitTable */
+ #define BLOCK_NIL 0xffff /* last block of a chain */
+@@ -84,8 +19,7 @@
+ #define BLOCK_RESERVED 0xfffc /* bios block or bad block */
--#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
-+#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
- #if CONFIG_SA1111
- #define LUBBOCK_IRQ(x) (SA1111_IRQ_MAX + 1 + (x))
- #else
-@@ -132,6 +131,3 @@
- #define NR_IRQS (LUBBOCK_LAST_IRQ + 1)
+ struct NFTLrecord {
+- struct mtd_info *mtd;
+- struct semaphore mutex;
++ struct mtd_blktrans_dev mbd;
+ __u16 MediaUnit, SpareMediaUnit;
+ __u32 EraseSize;
+ struct NFTLMediaHeader MediaHdr;
+@@ -97,13 +31,13 @@
+ __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 */
+ unsigned int nb_blocks; /* number of physical blocks */
+ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
+ struct erase_info instr;
++ struct nand_oobinfo oobinfo;
+ };
- #endif // CONFIG_ARCH_LUBBOCK
--
--
+ int NFTL_mount(struct NFTLrecord *s);
+@@ -114,9 +48,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__ */
-
---- linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h
-@@ -1051,6 +1051,7 @@
- #define PGSR1 __REG(0x40F00024) /* Power Manager GPIO Sleep State Register for GP[63-32] */
- #define PGSR2 __REG(0x40F00028) /* Power Manager GPIO Sleep State Register for GP[84-64] */
- #define RCSR __REG(0x40F00030) /* Reset Controller Status Register */
-+#define PMFW __REG(0x40F00034) /* Power Manager Fast-Sleep Wakeup Configuration Register */
+ #endif /* __MTD_NFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/partitions.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/partitions.h
+@@ -5,7 +5,7 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #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,26 @@
+ #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 *);
- #define PSSR_RDH (1 << 5) /* Read Disable Hold */
- #define PSSR_PH (1 << 4) /* Peripheral Control Hold */
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/arch-pxa/ramses.h
-@@ -0,0 +1,364 @@
-+/*
-+ * linux/include/asm-arm/arch-pxa/ramses.h
-+ *
-+ * 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.
-+ *
-+ * Copyright (c) 2002,2003 M&N Logistik-Lösungen Online GmbH
-+ *
-+ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
-+ * Initial code
-+ *
-+ * 2002-10-08: adaption from PXA IDP to Ramses
-+ *
-+ */
-+
-+
-+/*
-+ * Note: this file must be safe to include in assembly files
-+ */
-+
-+#define RAMSES_FLASH_PHYS (PXA_CS0_PHYS)
-+#define RAMSES_ALT_FLASH_PHYS (PXA_CS1_PHYS)
-+#define RAMSES_MEDIAQ_PHYS (PXA_CS3_PHYS)
-+#define RAMSES_CONTROL_PHYS (PXA_CS4_PHYS)
-+#define RAMSES_IDE_PHYS (PXA_CS5_PHYS + 0x03000000)
-+#define RAMSES_ETH_PHYS (PXA_CS5_PHYS + 0x03400000)
-+#define RAMSES_COREVOLT_PHYS (PXA_CS5_PHYS + 0x03800000)
-+#define RAMSES_CPLD_PHYS (PXA_CS5_PHYS + 0x03C00000)
-+
+/*
-+ * virtual memory map
-+ */
-+
-+#define RAMSES_IDE_BASE (0xf0000000)
-+#define RAMSES_IDE_SIZE (1*1024*1024)
-+#define RAMSES_ETH_BASE (RAMSES_IDE_BASE + RAMSES_IDE_SIZE)
-+#define RAMSES_ETH_SIZE (1*1024*1024)
-+#define RAMSES_COREVOLT_BASE (RAMSES_ETH_BASE + RAMSES_ETH_SIZE)
-+#define RAMSES_COREVOLT_SIZE (1*1024*1024)
-+#define RAMSES_CPLD_BASE (RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_SIZE)
-+#define RAMSES_CPLD_SIZE (1*1024*1024)
-+#define RAMSES_CONTROL_BASE (RAMSES_CPLD_BASE + RAMSES_CPLD_SIZE)
-+#define RAMSES_CONTROL_SIZE (1*1024*1024)
-+
-+#if (RAMSES_CONTROL_BASE + RAMSES_CONTROL_SIZE) > 0xfc000000
-+#error Your custom IO space is getting a bit large !!
-+#endif
-+
-+#define CPLD_P2V(x) ((x) - RAMSES_CPLD_PHYS + RAMSES_CPLD_BASE)
-+#define CPLD_V2P(x) ((x) - RAMSES_CPLD_BASE + RAMSES_CPLD_PHYS)
-+#define CTRL_P2V(x) ((x) - RAMSES_CONTROL_PHYS + RAMSES_CONTROL_BASE)
-+#define CTRL_V2P(x) ((x) - RAMSES_CONTROL_BASE + RAMSES_CONTROL_PHYS)
-+#define CORE_P2V(x) ((x) - RAMSES_COREVOLT_PHYS + RAMSES_COREVOLT_BASE)
-+#define CORE_V2P(x) ((x) - RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_PHYS)
-+
-+//smc91111 driver compatibility issue
-+#define ETH_BASE RAMSES_ETH_BASE
-+
-+#ifndef __ASSEMBLY__
-+# define __CPLD_REG(x) (*((volatile unsigned long *)CPLD_P2V(x)))
-+# define __CTRL_REG(x) (*((volatile unsigned long *)CTRL_P2V(x)))
-+# define __CORE_REG(x) (*((volatile unsigned long *)CORE_P2V(x)))
-+#else
-+# define __CPLD_REG(x) CPLD_P2V(x)
-+# define __CTRL_REG(x) CTRL_P2V(x)
-+# define __CORE_REG(x) CORE_P2V(x)
-+#endif
-+
-+/* CPLD addresses */
-+
-+#define RAMSES_CPLD_PERIPH_PWR_ (RAMSES_CPLD_PHYS + 0x04)
-+#define RAMSES_CPLD_PERIPH_PWR __CPLD_REG(RAMSES_CPLD_PERIPH_PWR_)
-+#define PER_RESET (1 << 4)
-+#define PER_PWR_EN (1 << 3)
-+#define USB_HOST_PWR_EN (1 << 2)
-+#define CORE_VAR_EN (1 << 0)
-+
-+#define RAMSES_CPLD_LED_CONTROL_ (RAMSES_CPLD_PHYS + 0x08)
-+#define RAMSES_CPLD_LED_CONTROL __CPLD_REG(RAMSES_CPLD_LED_CONTROL_)
-+#define CPLD_LED2 (1 << 6)
-+#define CPLD_LED1 (1 << 5)
-+#define GSM_ACTIVE (1 << 4)
-+
-+#define RAMSES_CPLD_KB_COL_HIGH_ (RAMSES_CPLD_PHYS + 0x0C)
-+#define RAMSES_CPLD_KB_COL_HIGH __CPLD_REG(RAMSES_CPLD_KB_COL_HIGH_)
-+// kbch(7)..kbch(13) on bit 0..6
-+
-+#define RAMSES_CPLD_KB_COL_LOW_ (RAMSES_CPLD_PHYS + 0x10)
-+#define RAMSES_CPLD_KB_COL_LOW __CPLD_REG(RAMSES_CPLD_KB_COL_LOW_)
-+// kbcl(0)..kbch(6) on bit 0..6
-+
-+#define RAMSES_CPLD_PCCARD_EN_ (RAMSES_CPLD_PHYS + 0x14)
-+#define RAMSES_CPLD_PCCARD_EN __CPLD_REG(RAMSES_CPLD_PCCARD_EN_)
-+#define PCC1_RESET (1 << 7)
-+#define PCC0_RESET (1 << 6)
-+#define PCC1_ENABLE (1 << 1)
-+#define PCC0_ENABLE (1 << 0)
-+
-+#define RAMSES_CPLD_PCCARD_PWR_ (RAMSES_CPLD_PHYS + 0x28)
-+#define RAMSES_CPLD_PCCARD_PWR __CPLD_REG(RAMSES_CPLD_PCCARD_PWR_)
-+#define PCC1_PWR3 (1 << 7)
-+#define PCC1_PWR2 (1 << 6)
-+#define PCC1_PWR1 (1 << 5)
-+#define PCC1_PWR0 (1 << 4)
-+#define PCC0_PWR3 (1 << 3)
-+#define PCC0_PWR2 (1 << 2)
-+#define PCC0_PWR1 (1 << 1)
-+#define PCC0_PWR0 (1 << 0)
-+
-+#define RAMSES_CPLD_MISC_CTRL_ (RAMSES_CPLD_PHYS + 0x2C)
-+#define RAMSES_CPLD_MISC_CTRL __CPLD_REG(RAMSES_CPLD_MISC_CTRL_)
-+#define RAMSES_IRDA_MD1 (1 << 5)
-+#define RAMSES_IRDA_MD0 (1 << 4)
-+#define RAMSES_FIR (1 << 3)
-+
-+#define RAMSES_CPLD_LCD_ (RAMSES_CPLD_PHYS + 0x30)
-+#define RAMSES_CPLD_LCD __CPLD_REG(RAMSES_CPLD_LCD_)
-+#define RAMSES_LCD_PINC (1 << 7)
-+#define RAMSES_LCD_PUP (1 << 6)
-+#define RAMSES_LCD_PCS (1 << 5)
-+#define RAMSES_LCD_DISPOFF (1 << 2)
-+#define RAMSES_LCD_VCC (1 << 0)
-+
-+#define RAMSES_CPLD_FLASH_WE_ (RAMSES_CPLD_PHYS + 0x34)
-+#define RAMSES_CPLD_FLASH_WE __CPLD_REG(RAMSES_CPLD_FLASH_WE_)
-+#define RAMSES_FLASH_WE (1 << 0)
-+
-+/* Read-Only registers */
-+
-+#define RAMSES_CPLD_KB_ROW_ (RAMSES_CPLD_PHYS + 0x50)
-+#define RAMSES_CPLD_KB_ROW __CPLD_REG(RAMSES_CPLD_KB_ROW_)
-+// kbr(0)..kbr(6) on bits 0..6
-+
-+#define RAMSES_CPLD_PCCARD0_STATUS_ (RAMSES_CPLD_PHYS + 0x54)
-+#define RAMSES_CPLD_PCCARD0_STATUS __CPLD_REG(RAMSES_CPLD_PCCARD0_STATUS_)
-+#define RAMSES_CPLD_PCCARD1_STATUS_ (RAMSES_CPLD_PHYS + 0x58)
-+#define RAMSES_CPLD_PCCARD1_STATUS __CPLD_REG(RAMSES_CPLD_PCCARD1_STATUS_)
-+#define _PCC_WRPROT (1 << 7)
-+#define _PCC_S16 (1 << 7)
-+#define _PCC_RESET (1 << 6)
-+#define _PCC_IRQ (1 << 5)
-+#define _PCC_INPACK (1 << 4)
-+#define PCC_BVD2 (1 << 3)
-+#define PCC_BVD1 (1 << 2)
-+#define PCC_VS2 (1 << 1)
-+#define PCC_VS1 (1 << 0)
-+
-+#define RAMSES_CPLD_MISC_STATUS_ (RAMSES_CPLD_PHYS + 0x5C)
-+#define RAMSES_CPLD_MISC_STATUS __CPLD_REG(RAMSES_CPLD_MISC_STATUS_)
-+#define RAMSES_MMC_WRPROT (1 << 7)
-+#define RAMSES_USB_OVERCURR (1 << 4)
-+#define RAMSES_CHG_STS (1 << 2)
-+#define RAMSES_WALL_IN (1 << 1)
-+#define RAMSES_USB_D_CON (1 << 0)
-+
-+#define RAMSES_CPLD_YEAR_ (RAMSES_CPLD_PHYS + 0x60)
-+#define RAMSES_CPLD_YEAR __CPLD_REG(RAMSES_CPLD_YEAR_)
-+
-+#define RAMSES_CPLD_MONTH_ (RAMSES_CPLD_PHYS + 0x64)
-+#define RAMSES_CPLD_MONTH __CPLD_REG(RAMSES_CPLD_MONTH_)
-+
-+#define RAMSES_CPLD_DAY_ (RAMSES_CPLD_PHYS + 0x68)
-+#define RAMSES_CPLD_DAY __CPLD_REG(RAMSES_CPLD_DAY_)
-+
-+#define RAMSES_CPLD_REV_ (RAMSES_CPLD_PHYS + 0x6C)
-+#define RAMSES_CPLD_REV __CPLD_REG(RAMSES_CPLD_REV_)
-+
-+#define RAMSES_CPLD_VSTAT_ (RAMSES_CPLD_PHYS + 0x7C)
-+#define RAMSES_CPLD_VSTAT __CPLD_REG(RAMSES_CPLD_VSTAT_)
-+#define RAMSES_BWE (1 << 1)
-+
-+
-+/* Flags for ramses_flags */
-+
-+#define RAMSES_FLAGS_LCD_FBTURN (1<<0)
-+/* MUST stay bit 0 */
-+#define RAMSES_FLAGS_SCANNER_BEAM (1<<1)
-+#define RAMSES_FLAGS_KEY_SCAN (1<<2)
-+#define RAMSES_FLAGS_KEY_SUSPEND (1<<3)
-+#define RAMSES_FLAGS_KEY_OFF (1<<4)
-+
-+
-+/* Offset in SMC EEPROM for LCD type */
-+#define RAMSES_LCD_TYPE_OFFSET 0x23
-+
-+
-+/* The control register on the I/O board */
-+
-+#define RAMSES_CONTROL_ (RAMSES_CONTROL_PHYS + 0)
-+#define RAMSES_CONTROL __CTRL_REG(RAMSES_CONTROL_)
-+// 5c00 = 0101 1100 0000 0000
-+#define RAMSES_CONTROL_SCANNER_TRIG_ (1 << 15)
-+#define RAMSES_CONTROL_SCANNER_WAKE_ (1 << 14)
-+#define RAMSES_CONTROL_SCANNER_PWR (1 << 13)
-+#define RAMSES_CONTROL_LED_BLUE_ (1 << 12)
-+
-+#define RAMSES_CONTROL_LED_ORANGE_ (1 << 11)
-+#define RAMSES_CONTROL_GSM_RESET (1 << 10)
-+#define RAMSES_CONTROL_GSM_BOOT (1 << 9)
-+#define RAMSES_CONTROL_GSM_PWR (1 << 8)
-+
-+#define RAMSES_CONTROL_POWEROFF (1 << 7)
-+#define RAMSES_CONTROL_USB_INTERN (1 << 6)
-+#define RAMSES_CONTROL_MMC_PWR (1 << 5)
-+#define RAMSES_CONTROL_UART_PWR (1 << 4)
-+
-+#define RAMSES_CONTROL_LCD_BLIGHT (1 << 3)
-+#define RAMSES_CONTROL_USB (1 << 2)
-+
-+#define RAMSES_POWER_OFF() { ramses_control_shadow |= RAMSES_CONTROL_POWEROFF; RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Active low
-+#define RAMSES_SCANNER_TRIG_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_TRIG_OFF() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_WAKE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_WAKE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_BLUE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_LED_BLUE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_BLUE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_LED_BLUE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_ORANGE_ON() { ramses_control_shadow &= ~RAMSES_CONTROL_LED_ORANGE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_ORANGE_OFF() { ramses_control_shadow |= RAMSES_CONTROL_LED_ORANGE_; RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Active high
-+#define RAMSES_SCANNER_ON() { ramses_control_shadow |= RAMSES_CONTROL_SCANNER_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_RESET_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_RESET; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_RESET_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_RESET; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_BOOT_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_BOOT; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_BOOT_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_BOOT; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_ON() { ramses_control_shadow |= RAMSES_CONTROL_GSM_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_INTERN() { ramses_control_shadow |= RAMSES_CONTROL_USB_INTERN; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_EXTERN() { ramses_control_shadow &= ~RAMSES_CONTROL_USB_INTERN; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_UART_ON() { ramses_control_shadow |= RAMSES_CONTROL_UART_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_UART_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_UART_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_MMC_ON() { ramses_control_shadow |= RAMSES_CONTROL_MMC_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_MMC_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_MMC_PWR; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LCD_BLIGHT_ON() { ramses_control_shadow |= RAMSES_CONTROL_LCD_BLIGHT; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LCD_BLIGHT_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_LCD_BLIGHT; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_BUS_ON() { ramses_control_shadow |= RAMSES_CONTROL_USB; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_BUS_OFF() { ramses_control_shadow &= ~RAMSES_CONTROL_USB; RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Corevolt settings
-+#define RAMSES_COREVOLT_ (RAMSES_COREVOLT_PHYS)
-+#define RAMSES_COREVOLT __CORE_REG(RAMSES_COREVOLT_)
-+
-+// Battery protocol
-+#define HDQ_TMP 0x02
-+#define HDQ_LMD 0x05
-+#define HDQ_VSB 0x0b
-+#define HDQ_CACT 0x0d
-+#define HDQ_SAEH 0x0f
-+#define HDQ_SAEL 0x10
-+#define HDQ_RCAC 0x11
-+#define HDQ_DCR 0x18
-+
-+
-+#ifndef __ASSEMBLY__
-+
-+/* Ramses specific functions */
-+void ramses_lcd_power_on(void);
-+void ramses_lcd_power_off(void);
-+void ramses_lcd_backlight_on(void);
-+void ramses_lcd_backlight_off(void);
-+void ramses_lcd_set_intensity(int i);
-+#ifdef OLDCODE
-+void ramses_lcd_set_pwm1(int p);
-+#endif
-+void ramses_lcd_set_brightness(int b);
-+void ramses_lcd_set_contrast(int c);
-+int ramses_lcd_get_intensity(void);
-+int ramses_lcd_get_brightness(void);
-+int ramses_lcd_get_contrast(void);
-+int ramses_hdq_get_reg(unsigned char reg);
-+void ramses_shut_off(void);
-+void ramses_set_corevolt(int volt);
-+
-+
-+/* shadow registers for write only registers */
-+extern u16 ramses_control_shadow;
-+extern int ramses_corevolt_shadow;
-+extern u16 ramses_lcd_type;
-+extern int ramses_lcd_pwm1_shadow;
-+
++ * Functions dealing with the various ways of partitioning the space
++ */
+
-+/* flag register for various settings */
-+extern unsigned int ramses_flags;
++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);
++};
+
-+/*
-+ * macros to write to write only register
-+ *
-+ * none of these macros are protected from
-+ * multiple drivers using them in interrupt context.
-+ */
++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 WRITE_RAMSES_CONTROL(value, mask) \
-+{\
-+ ramses_control_shadow = ((value & mask) | (ramses_control_shadow & ~mask));\
-+ RAMSES_CONTROL = ramses_control_shadow;\
-+}
-+#endif
++#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
+
+ #endif
+
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/physmap.h
+@@ -0,0 +1,61 @@
+/*
-+ * USB Host
++ * For boards with physically mapped flash and using
++ * drivers/mtd/maps/physmap.c mapping driver.
+ *
-+ * The SL811HS is selected with nCS3 and some address bits:
++ * $Id$
++ *
++ * 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.
+ *
-+ * 12 8 4
-+ * nA14, nCS[3], address mask 1011 1111 1111 0000 = xBf00
+ */
-+#define SL811HS_PHYS (PXA_CS3_PHYS+0xBFF0)
-+#define SL811HS_DATA (PXA_CS3_PHYS+0xBFF4)
-+
-+
-+
-+
-+
-+
-+
-+#define PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
-+
-+
-+
-+
-+
-+/* A listing of interrupts used by external hardware devices */
-+
-+#define TOUCH_PANEL_IRQ IRQ_GPIO(21)
-+#define TOUCH_PANEL_IRQ_EDGE GPIO_FALLING_EDGE
-+
-+#define ETHERNET_IRQ IRQ_GPIO(4)
-+#define ETHERNET_IRQ_EDGE GPIO_RISING_EDGE
+
-+#define CFCARD_CD_VALID IRQ_GPIO(8)
-+#define CFCARD_CD_VALID_EDGE GPIO_BOTH_EDGES
++#ifndef __LINUX_MTD_PHYSMAP__
+
-+#define CFCARD_RDYINT IRQ_GPIO(22)
++#include <linux/config.h>
+
-+#define RAMSES_KEYBOARD_IRQ IRQ_GPIO(3)
-+#define RAMSES_KEYBOARD_IRQ_EDGE GPIO_FALLING_EDGE
++#if defined(CONFIG_MTD_PHYSMAP)
+
-+#define SL811HS_IRQ IRQ_GPIO(32)
-+#define SL811HS_IRQ_EDGE GPIO_RISING_EDGE
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
+
+/*
-+ * Macros for LED Driver
++ * The map_info for physmap. Board can override size, buswidth, phys,
++ * (*set_vpp)(), etc in their initial setup routine.
+ */
-+
-+/* leds 0 = ON */
-+#define RAMSES_HB_LED (1<<5)
-+#define RAMSES_BUSY_LED (1<<6)
-+
-+#define RAMSES_LEDS_MASK (RAMSES_HB_LED | RAMSES_BUSY_LED)
-+
-+#define RAMSES_WRITE_LEDS(value) (RAMSES_CPLD_LED_CONTROL = ((RAMSES_CPLD_LED_CONTROL & ~(RAMSES_LEDS_MASK)) | value))
++extern struct map_info physmap_map;
+
+/*
-+ * macros for MTD driver
++ * Board needs to specify the exact mapping during their setup time.
+ */
++static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
++{
++ physmap_map.phys = addr;
++ physmap_map.size = size;
++ physmap_map.bankwidth = bankwidth;
++ physmap_map.set_vpp = set_vpp;
++}
+
-+#define FLASH_WRITE_PROTECT_DISABLE() ((RAMSES_CPLD_FLASH_WE) &= ~(0x1))
-+#define FLASH_WRITE_PROTECT_ENABLE() ((RAMSES_CPLD_FLASH_WE) |= (0x1))
-+
-+
---- linux-2.4.21/include/asm-arm/arch-pxa/time.h~pxa-timerint
-+++ linux-2.4.21/include/asm-arm/arch-pxa/time.h
-@@ -33,7 +33,7 @@
- /* IRQs are disabled before entering here from do_gettimeofday() */
- static unsigned long pxa_gettimeoffset (void)
- {
-- unsigned long ticks_to_match, elapsed, usec;
-+ long ticks_to_match, elapsed, usec;
-
- /* Get ticks before next timer match */
- ticks_to_match = OSMR0 - OSCR;
-@@ -41,6 +41,10 @@
- /* We need elapsed ticks since last match */
- elapsed = LATCH - ticks_to_match;
-
-+ /* don't get fooled by the workaround in pxa_timer_interrupt() */
-+ if (elapsed <= 0)
-+ return 0;
++#if defined(CONFIG_MTD_PARTITIONS)
+
- /* Now convert them to usec */
- usec = (unsigned long)(elapsed*tick)/LATCH;
-
-@@ -59,6 +63,15 @@
- * IRQs are disabled inside the loop to ensure coherence between
- * lost_ticks (updated in do_timer()) and the match reg value, so we
- * can use do_gettimeofday() from interrupt handlers.
-+ *
-+ * HACK ALERT: it seems that the PXA timer regs aren't updated right
-+ * away in all cases when a write occurs. We therefore compare with
-+ * 8 instead of 0 in the while() condition below to avoid missing a
-+ * match if OSCR has already reached the next OSMR value.
-+ * Experience has shown that up to 6 ticks are needed to work around
-+ * this problem, but let's use 8 to be conservative. Note that this
-+ * affect things only when the timer IRQ has been delayed by nearly
-+ * exactly one tick period which should be a pretty rare event.
- */
- do {
- do_leds();
-@@ -68,7 +81,7 @@
- OSSR = OSSR_M0; /* Clear match on timer 0 */
- next_match = (OSMR0 += LATCH);
- restore_flags( flags );
-- } while( (signed long)(next_match - OSCR) <= 0 );
-+ } while( (signed long)(next_match - OSCR) <= 8 );
- }
-
- extern inline void setup_timer (void)
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/bug.h
-@@ -0,0 +1 @@
-+/* dummy */
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/sl811-hw.h
-@@ -0,0 +1,202 @@
+/*
-+File: include/asm-arm/sl811-hw.h
-+
-+19.09.2003 hne@ist1.de
-+Use Kernel 2.4.20 and this source from 2.4.22
-+Splitt hardware depens into file sl811-x86.h and sl811-arm.h.
-+Functions as inline.
-+
-+23.09.2003 hne
-+Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h.
-+GPRD as parameter.
-+
-+24.09.2003 hne
-+Use Offset from ADDR to DATA instand of direct io.
-+
-+03.10.2003 hne
-+Low level only for port io into hardware-include.
-+*/
++ * 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);
+
-+#ifndef __LINUX_SL811_HW_H
-+#define __LINUX_SL811_HW_H
++#endif /* defined(CONFIG_MTD_PARTITIONS) */
++#endif /* defined(CONFIG_MTD) */
+
-+#ifdef CONFIG_X86
-+#define MAX_CONTROLERS 1 /* Max number of sl811 controllers */
-+ /* Always 1 for this architecture! */
++#endif /* __LINUX_MTD_PHYSMAP__ */
+
-+#define SIZEOF_IO_REGION 1 /* Size for request/release region */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/plat-ram.h
+@@ -0,0 +1,35 @@
++/* linux/include/mtd/plat-ram.h
++ *
++ * (c) 2004 Simtec Electronics
++ * http://www.simtec.co.uk/products/SWLINUX/
++ * Ben Dooks <ben@simtec.co.uk>
++ *
++ * Generic platform device based RAM map
++ *
++ * $Id$
++ *
++ * 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.
++ *
++ */
+
-+#define OFFSET_DATA_REG data_off /* Offset from ADDR_IO to DATA_IO (future) */
-+ /* Can change by arg */
++#ifndef __LINUX_MTD_PLATRAM_H
++#define __LINUX_MTD_PLATRAM_H __FILE__
+
-+static int io = 0xf100000e; /* Base addr_io */
-+static int data_off = 1; /* Offset from addr_io to addr_io */
-+static int irq = 44; /* also change gprd !!! */
-+static int gprd = 23; /* also change irq !!! */
++#define PLATRAM_RO (0)
++#define PLATRAM_RW (1)
+
-+MODULE_PARM(io,"i");
-+MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e");
-+MODULE_PARM(data_off,"i");
-+MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)");
-+MODULE_PARM(irq,"i");
-+MODULE_PARM_DESC(irq,"sl811 irq 44(default)");
-+MODULE_PARM(gprd,"i");
-+MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)");
-+#endif
++struct platdata_mtd_ram {
++ char *mapname;
++ char **probes;
++ struct mtd_partition *partitions;
++ int nr_partitions;
++ int bankwidth;
+
-+#ifdef CONFIG_ARCH_RAMSES
-+#define SIZEOF_IO_REGION 8 /* Size for request/release region */
-+static void *ramses_sl811hs; /* dynamically assign virtual address */
-+#endif
++ /* control callbacks */
+
++ void (*set_rw)(struct device *dev, int to);
++};
+
++#endif /* __LINUX_MTD_PLATRAM_H */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/xip.h
+@@ -0,0 +1,107 @@
+/*
-+ * Low level: Read from Data port [arm]
++ * MTD primitives for XIP support
++ *
++ * Author: Nicolas Pitre
++ * Created: Nov 2, 2004
++ * Copyright: (C) 2004 MontaVista Software, Inc.
++ *
++ * This XIP support for MTD has been loosely inspired
++ * by an earlier patch authored by David Woodhouse.
++ *
++ * 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$
+ */
-+static __u8 inline sl811_read_data (struct sl811_hc *hc)
-+{
-+ __u8 data;
-+ data = readb(hc->data_io);
-+ rmb();
-+//printk("%s: in %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+ return data;
-+}
+
-+/*
-+ * Low level: Write to index register [arm]
-+ */
-+static void inline sl811_write_index (struct sl811_hc *hc, __u8 index)
-+{
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
-+ writeb(index, hc->addr_io);
-+ wmb();
-+}
++#ifndef __LINUX_MTD_XIP_H__
++#define __LINUX_MTD_XIP_H__
+
-+/*
-+ * Low level: Write to Data port [arm]
-+ */
-+static void inline sl811_write_data (struct sl811_hc *hc, __u8 data)
-+{
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+ writeb(data, hc->data_io);
-+ wmb();
-+}
++#include <linux/config.h>
++
++#ifdef CONFIG_MTD_XIP
+
+/*
-+ * Low level: Write to index register and data port [arm]
++ * Function that are modifying the flash state away from array mode must
++ * obviously not be running from flash. The __xipram is therefore marking
++ * those functions so they get relocated to ram.
+ */
-+static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data)
-+{
-+ writeb(index, hc->addr_io);
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
-+ writeb(data, hc->data_io);
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+ wmb();
-+}
-+
++#define __xipram __attribute__ ((__section__ (".data")))
+
+/*
-+ * This function is board specific. It sets up the interrupt to
-+ * be an edge trigger and trigger on the rising edge
++ * We really don't want gcc to guess anything.
++ * We absolutely _need_ proper inlining.
+ */
-+static void inline sl811_init_irq(void)
-+{
-+#ifdef CONFIG_X86
-+ GPDR &= ~(1<<gprd);
-+ set_GPIO_IRQ_edge(1<<gprd, GPIO_RISING_EDGE);
-+#endif
-+#ifdef CONFIG_ARCH_PXA
-+ int irq_gpio_pin = IRQ_TO_GPIO_2_80(SL811HS_IRQ);
-+ GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
-+ set_GPIO_IRQ_edge(irq_gpio_pin, SL811HS_IRQ_EDGE);
-+#endif
-+}
++#include <linux/compiler.h>
+
-+/*****************************************************************
-+ *
-+ * Function Name: release_regions [arm]
-+ *
-+ * This function is board specific. It release all io address
-+ * from memory (if can).
-+ *
-+ * Input: struct sl811_hc * *
++/*
++ * Each architecture has to provide the following macros. They must access
++ * the hardware directly and not rely on any other (XIP) functions since they
++ * won't be available when used (flash not in array mode).
+ *
-+ * Return value : 0 = OK
++ * xip_irqpending()
+ *
-+ *****************************************************************/
-+static void inline sl811_release_regions(struct sl811_hc *hc)
-+{
-+#ifdef CONFIG_X86
-+ if (hc->addr_io)
-+ release_region(hc->addr_io, SIZEOF_IO_REGION);
-+ hc->addr_io = 0;
-+
-+ if (hc->data_io)
-+ release_region(hc->data_io, SIZEOF_IO_REGION);
-+ hc->data_io = 0;
-+#endif
-+#ifdef CONFIG_ARCH_RAMSES
-+ if (ramses_sl811hs) {
-+ iounmap(ramses_sl811hs);
-+ release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
-+ }
-+ hc->addr_io = 0;
-+ hc->data_io = 0;
-+ RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN;
-+ RAMSES_USB_BUS_OFF();
-+#endif
-+}
-+
-+/*****************************************************************
++ * return non zero when any hardware interrupt is pending.
+ *
-+ * Function Name: request_regions [arm]
++ * xip_currtime()
+ *
-+ * This function is board specific. It request all io address and
-+ * maps into memory (if can).
++ * return a platform specific time reference to be used with
++ * xip_elapsed_since().
+ *
-+ * Input: struct sl811_hc *
++ * xip_elapsed_since(x)
+ *
-+ * Return value : 0 = OK
++ * return in usecs the elapsed timebetween now and the reference x as
++ * returned by xip_currtime().
+ *
-+ *****************************************************************/
-+static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name)
-+{
-+#ifdef CONFIG_X86
-+ if (!request_region(addr_io, SIZEOF_IO_REGION, name)) {
-+ PDEBUG(3, "request address %d failed", addr_io);
-+ return -EBUSY;
-+ }
-+ hc->addr_io = addr_io;
++ * note 1: convertion to usec can be approximated, as long as the
++ * returned value is <= the real elapsed time.
++ * note 2: this should be able to cope with a few seconds without
++ * overflowing.
++ */
+
-+ if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) {
-+ PDEBUG(3, "request address %d failed", data_io);
-+ /* release_region(hc->addr_io, SIZEOF_IO_REGION); */
-+ return -EBUSY;
-+ }
-+ hc->data_io = data_io;
-+#endif
-+#ifdef CONFIG_ARCH_RAMSES
-+ RAMSES_USB_BUS_ON();
-+ RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN;
-+ mdelay(300);
++#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
+
-+ if (!request_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION, name)) {
-+ printk(KERN_ERR "unable to reserve region\n");
-+ return -EBUSY;
-+ } else {
-+ ramses_sl811hs = ioremap_nocache(SL811HS_PHYS, SIZEOF_IO_REGION);
-+ dbg("phys %p -> virt %p\n", SL811HS_PHYS, ramses_sl811hs);
-+ if (!ramses_sl811hs) {
-+ printk(KERN_ERR "unable to map region\n");
-+ release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
-+ return -EBUSY;
-+ }
-+ }
-+ hc->addr_io = (unsigned long) ramses_sl811hs;
-+ hc->data_io = (unsigned long) ramses_sl811hs+4;
++#include <asm/hardware.h>
++#ifdef CONFIG_ARCH_PXA
++#include <asm/arch/pxa-regs.h>
+#endif
+
-+ return 0;
-+}
++#define xip_irqpending() (ICIP & ICMR)
+
-+#endif // __LINUX_SL811_HW_H
---- linux-2.4.21/include/linux/apm_bios.h~pm
-+++ linux-2.4.21/include/linux/apm_bios.h
-@@ -16,6 +16,8 @@
- * General Public License for more details.
- */
-
-+#include <linux/pm-devices.h>
++/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
++#define xip_currtime() (OSCR)
++#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
+
- typedef unsigned short apm_event_t;
- typedef unsigned short apm_eventinfo_t;
-
-@@ -59,6 +61,16 @@
- };
-
- /*
-+ * Allow device specific code to register function which
-+ * gets the battery power status (see arch/arm/mach-pxa/apm.c).
-+ */
-+extern void apm_register_get_power_status( int (*fn)(u_char *ac_line_status,
-+ u_char *battery_status,
-+ u_char *battery_flag,
-+ u_char *battery_percentage,
-+ u_short *battery_life));
++#else
++
++#warning "missing IRQ and timer primitives for XIP MTD support"
++#warning "some of the XIP MTD support code will be disabled"
++#warning "your system will therefore be unresponsive when writing or erasing flash"
++
++#define xip_irqpending() (0)
++#define xip_currtime() (0)
++#define xip_elapsed_since(x) (0)
+
-+/*
- * The APM function codes
- */
- #define APM_FUNC_INST_CHECK 0x5300
-@@ -168,6 +180,7 @@
- /*
- * APM Device IDs
- */
-+#ifdef _i386_
- #define APM_DEVICE_BIOS 0x0000
- #define APM_DEVICE_ALL 0x0001
- #define APM_DEVICE_DISPLAY 0x0100
-@@ -181,6 +194,21 @@
- #define APM_DEVICE_OLD_ALL 0xffff
- #define APM_DEVICE_CLASS 0x00ff
- #define APM_DEVICE_MASK 0xff00
+#endif
+
+/*
-+ * APM devices IDs for non-x86
++ * xip_cpu_idle() is used when waiting for a delay equal or larger than
++ * the system timer tick period. This should put the CPU into idle mode
++ * to save power and to be woken up only when some interrupts are pending.
++ * As above, this should not rely upon standard kernel code.
+ */
-+#define APM_DEVICE_ALL PM_SYS_DEV
-+#define APM_DEVICE_DISPLAY PM_DISPLAY_DEV
-+#define APM_DEVICE_STORAGE PM_STORAGE_DEV
-+#define APM_DEVICE_PARALLEL PM_PARALLEL_DEV
-+#define APM_DEVICE_SERIAL PM_SERIAL_DEV
-+#define APM_DEVICE_NETWORK PM_NETWORK_DEV
-+#define APM_DEVICE_PCMCIA PM_PCMCIA_DEV
-+#define APM_DEVICE_BATTERY PM_BATTERY_DEV
-+#define APM_DEVICE_TPANEL PM_TPANEL_DEV
+
-
- #ifdef __KERNEL__
- /*
-@@ -214,5 +242,6 @@
-
- #define APM_IOC_STANDBY _IO('A', 1)
- #define APM_IOC_SUSPEND _IO('A', 2)
-+#define APM_IOC_SET_WAKEUP _IO('A', 3)
-
- #endif /* LINUX_APM_H */
---- linux-2.4.21/include/linux/crc32.h~mtd-cvs
-+++ linux-2.4.21/include/linux/crc32.h
-@@ -46,4 +46,25 @@
- return crc;
- }
-
--#endif /* _LINUX_CRC32_H */
-+#ifndef CRC32_H
-+#define CRC32_H
++#if defined(CONFIG_CPU_XSCALE)
++#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
++#else
++#define xip_cpu_idle() do { } while (0)
++#endif
+
-+/* $Id$ */
++#else
+
-+#include <linux/types.h>
++#define __xipram
+
-+extern const uint32_t crc32_table[256];
++#endif /* CONFIG_MTD_XIP */
+
-+/* Return a 32-bit CRC of the contents of the buffer. */
++#endif /* __LINUX_MTD_XIP_H__ */
+--- linux-2.4.21/include/linux/net.h~bluetooth
++++ linux-2.4.21/include/linux/net.h
+@@ -139,6 +139,7 @@
+ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+ extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
++extern struct socket *sockfd_lookup(int fd, int *err);
+
+ extern int net_ratelimit(void);
+ extern unsigned long net_random(void);
+--- /dev/null
++++ linux-2.4.21/include/linux/pm-devices.h
+@@ -0,0 +1,41 @@
++#ifndef _LINUX_PM_DEV_H
++#define _LINUX_PM_DEV_H
+
-+static inline uint32_t
-+crc32(uint32_t val, const void *ss, int len)
++/*
++ * Copyright 2002 Montavista Software (mlocke@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, 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.
++ */
++
++
++
++/*
++ * Device types
++ */
++enum
+{
-+ const unsigned char *s = ss;
-+ while (--len >= 0)
-+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
-+ return val;
-+}
++ PM_UNKNOWN_DEV = 0, /* generic */
++ PM_SYS_DEV, /* system device (fan, KB controller, ...) */
++ PM_PCI_DEV, /* PCI device */
++ PM_USB_DEV, /* USB device */
++ PM_SCSI_DEV, /* SCSI device */
++ PM_ISA_DEV, /* ISA device */
++ PM_MTD_DEV, /* Memory Technology Device */
++ PM_TPANEL_DEV, /* Memory Technology Device */
++ PM_STORAGE_DEV, /* Memory Technology Device */
++ PM_NETWORK_DEV, /* Memory Technology Device */
++ PM_PCMCIA_DEV, /* Memory Technology Device */
++ PM_DISPLAY_DEV, /* Memory Technology Device */
++ PM_SERIAL_DEV, /* Memory Technology Device */
++ PM_BATTERY_DEV, /* Memory Technology Device */
++};
+
+#endif
-+#endif
---- linux-2.4.21/include/linux/fs.h~mtd-cvs
-+++ linux-2.4.21/include/linux/fs.h
-@@ -1376,6 +1376,7 @@
- extern void iput(struct inode *);
- extern void force_delete(struct inode *);
- extern struct inode * igrab(struct inode *);
-+extern struct inode * ilookup(struct super_block *, unsigned long);
- extern ino_t iunique(struct super_block *, ino_t);
-
- typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
---- linux-2.4.21/include/linux/i2c-id.h~i2c-ds1337
-+++ linux-2.4.21/include/linux/i2c-id.h
-@@ -95,13 +95,14 @@
- #define I2C_DRIVERID_ADV717x 48 /* ADV 7175/7176 video encoder */
- #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_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */
-+#define I2C_DRIVERID_DS1337 52 /* DS1337 real time clock */
-
-
+--- linux-2.4.21/include/linux/pm.h~pm
++++ linux-2.4.21/include/linux/pm.h
+@@ -24,6 +24,7 @@
+ #ifdef __KERNEL__
--#define I2C_DRIVERID_DS1307 46 /* real time clock: DS1307 */
--#define I2C_DRIVERID_24LC64 47 /* EEprom 24LC64 */
--#define I2C_DRIVERID_FM24CLB4 48 /* EEprom FM24CLB4 */
-+//#define I2C_DRIVERID_DS1307 46 /* real time clock: DS1307 */
-+//#define I2C_DRIVERID_24LC64 47 /* EEprom 24LC64 */
-+//#define I2C_DRIVERID_FM24CLB4 48 /* EEprom FM24CLB4 */
+ #include <linux/config.h>
++#include <linux/pm-devices.h>
+ #include <linux/list.h>
- #define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */
- #define I2C_DRIVERID_EXP1 0xF1
---- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2.h
-@@ -1,50 +1,30 @@
/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
- *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in the
-+ * jffs2 directory.
- *
-- * $Id$
-+ * $Id$
- *
- */
-
- #ifndef __LINUX_JFFS2_H__
- #define __LINUX_JFFS2_H__
-
--#include <asm/types.h>
-+/* 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
-
-@@ -63,6 +43,8 @@
- #define JFFS2_COMPR_COPY 0x04
- #define JFFS2_COMPR_DYNRUBIN 0x05
- #define JFFS2_COMPR_ZLIB 0x06
-+#define JFFS2_COMPR_LZO 0x07
-+#define JFFS2_COMPR_LZARI 0x08
- /* Compatibility flags. */
- #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
- #define JFFS2_NODE_ACCURATE 0x2000
-@@ -78,16 +60,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)
+@@ -50,20 +51,6 @@
- // 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)
+ typedef int pm_request_t;
--/* Same as the non_ECC versions, but with extra space for real
-- * ECC instead of just the checksum. For use on NAND flash
+-/*
+- * Device types
- */
--//#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 +74,46 @@
- 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;
-+
- 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 +124,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 {
---- linux-2.4.21/include/linux/jffs2_fs_i.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2_fs_i.h
-@@ -1,22 +1,13 @@
--/* $Id$ */
-+/* $Id$ */
-
- #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 <linux/pipe_fs_i.h>
--#endif
-+#include <linux/version.h>
-+#include <linux/rbtree.h>
-+#include <asm/semaphore.h>
-
- 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 +17,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 +35,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;
+-enum
+-{
+- PM_UNKNOWN_DEV = 0, /* generic */
+- PM_SYS_DEV, /* system device (fan, KB controller, ...) */
+- PM_PCI_DEV, /* PCI device */
+- PM_USB_DEV, /* USB device */
+- PM_SCSI_DEV, /* SCSI device */
+- PM_ISA_DEV, /* ISA device */
+- PM_MTD_DEV, /* Memory Technology Device */
-};
-
--#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 */
--
---- linux-2.4.21/include/linux/jffs2_fs_sb.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2_fs_sb.h
-@@ -1,18 +1,23 @@
--/* $Id$ */
-+/* $Id$ */
-
- #ifndef _JFFS2_FS_SB
- #define _JFFS2_FS_SB
-
- #include <linux/types.h>
- #include <linux/spinlock.h>
-+#include <linux/workqueue.h>
- #include <linux/completion.h>
- #include <asm/semaphore.h>
-+#include <linux/timer.h>
-+#include <linux/wait.h>
- #include <linux/list.h>
--
--#define INOCACHE_HASHSIZE 1
-+#include <linux/rwsem.h>
+ typedef int pm_dev_t;
- #define JFFS2_SB_FLAG_RO 1
--#define JFFS2_SB_FLAG_MOUNTING 2
-+#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
-+#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/rbtree-24.h
+@@ -0,0 +1,133 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++
++ 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.
+
-+struct jffs2_inodirty;
-
- /* A struct for the overall file system control. Pointers to
- jffs2_sb_info structs are named `c' in the source code.
-@@ -21,36 +26,44 @@
- struct jffs2_sb_info {
- struct mtd_info *mtd;
-
-- __u32 highest_ino;
-+ uint32_t highest_ino;
-+ uint32_t checked_ino;
++ 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.
+
- 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;
-+ out-of-order writing of nodes. And GC. */
-+ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
-+ (i.e. zero for OOB CLEANMARKER */
-
-- __u32 nr_free_blocks;
-- __u32 nr_erasing_blocks;
-+ 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;
-
-- __u32 nr_blocks;
-+ uint32_t nr_free_blocks;
-+ uint32_t nr_erasing_blocks;
++ 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
+
-+ /* 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 */
++ linux/include/linux/rbtree.h
+
-+ uint32_t nospc_dirty_size;
++ 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...
+
-+ 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,35 @@
- 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];
++ 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.
+
-+ 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;
++-----------------------------------------------------------------------
++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;
+
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+ /* 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;
++ while (n)
++ {
++ page = rb_entry(n, struct page, rb_page_cache);
+
-+ struct rw_semaphore wbuf_sem; /* Protects the write buffer */
++ if (offset < page->offset)
++ n = n->rb_left;
++ else if (offset > page->offset)
++ n = n->rb_right;
++ else
++ return page;
++ }
++ return NULL;
++}
+
-+ /* 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 */
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/blktrans.h
-@@ -0,0 +1,72 @@
-+/*
-+ * $Id$
-+ *
-+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * Interface to Linux block layer for MTD 'translation layers'.
-+ *
-+ */
++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;
+
-+#ifndef __MTD_TRANS_H__
-+#define __MTD_TRANS_H__
++ while (*p)
++ {
++ parent = *p;
++ page = rb_entry(parent, struct page, rb_page_cache);
+
-+#include <asm/semaphore.h>
++ if (offset < page->offset)
++ p = &(*p)->rb_left;
++ else if (offset > page->offset)
++ p = &(*p)->rb_right;
++ else
++ return page;
++ }
+
-+struct hd_geometry;
-+struct mtd_info;
-+struct mtd_blktrans_ops;
-+struct file;
-+struct inode;
++ rb_link_node(node, parent, p);
+
-+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 */
-+};
++ return NULL;
++}
+
-+struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
++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;
++}
++-----------------------------------------------------------------------
++*/
+
-+struct mtd_blktrans_ops {
-+ char *name;
-+ int major;
-+ int part_bits;
++#ifndef _LINUX_RBTREE_H
++#define _LINUX_RBTREE_H
+
-+ /* 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);
++#include <linux/kernel.h>
++#include <linux/stddef.h>
+
-+ /* Block layer ioctls */
-+ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
-+ int (*flush)(struct mtd_blktrans_dev *dev);
++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;
+
-+ /* 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);
++typedef struct rb_root_s
++{
++ struct rb_node_s * rb_node;
++}
++rb_root_t;
+
-+ /* 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);
++#define RB_ROOT (rb_root_t) { NULL, }
++#define rb_entry(ptr, type, member) \
++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
-+ struct list_head devs;
-+ struct list_head list;
-+ struct module *owner;
++extern void rb_insert_color(rb_node_t *, rb_root_t *);
++extern void rb_erase(rb_node_t *, rb_root_t *);
+
-+ struct mtd_blkcore_priv *blkcore_priv;
-+};
++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;
+
-+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);
-+
++ *rb_link = node;
++}
+
-+#endif /* __MTD_TRANS_H__ */
---- linux-2.4.21/include/linux/mtd/cfi.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/cfi.h
-@@ -1,211 +1,86 @@
-
- /* Common Flash Interface structures
- * See http://support.intel.com/design/flash/technote/index.htm
-- * $Id$
-+ * $Id$
- */
-
- #ifndef __MTD_CFI_H__
- #define __MTD_CFI_H__
-
- #include <linux/config.h>
-+#include <linux/version.h>
- #include <linux/delay.h>
- #include <linux/types.h>
- #include <linux/interrupt.h>
- #include <linux/mtd/flashchip.h>
-+#include <linux/mtd/map.h>
- #include <linux/mtd/cfi_endian.h>
-
--/*
-- * You can optimize the code size and performance by defining only
-- * the geometry(ies) available on your hardware.
-- * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width)
-- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
-- *
-- * By default, all (known) geometries are supported.
-- */
++#endif /* _LINUX_RBTREE_H */
+--- linux-2.4.21/include/linux/rbtree.h~mtd-cvs
++++ linux-2.4.21/include/linux/rbtree.h
+@@ -1,133 +1,25 @@
+ /*
+- Red Black Trees
+- (C) 1999 Andrea Arcangeli <andrea@suse.de>
+-
+- 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 CONFIG_MTD_CFI_GEOMETRY
+- 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.
-
--/* The default case - support all but 64-bit, which has
-- a performance penalty */
+- 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
-
--#define CFIDEV_INTERLEAVE_1 (1)
--#define CFIDEV_INTERLEAVE_2 (2)
--#define CFIDEV_INTERLEAVE_4 (4)
+- linux/include/linux/rbtree.h
-
--#define CFIDEV_BUSWIDTH_1 (1)
--#define CFIDEV_BUSWIDTH_2 (2)
--#define CFIDEV_BUSWIDTH_4 (4)
+- 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...
-
--typedef __u32 cfi_word;
+- 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.
-
--#else
+------------------------------------------------------------------------
+-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;
-
--/* Explicitly configured buswidth/interleave support */
+- while (n)
+- {
+- page = rb_entry(n, struct page, rb_page_cache);
-
- #ifdef CONFIG_MTD_CFI_I1
--#define CFIDEV_INTERLEAVE_1 (1)
--#endif
--#ifdef CONFIG_MTD_CFI_I2
--#define CFIDEV_INTERLEAVE_2 (2)
--#endif
--#ifdef CONFIG_MTD_CFI_I4
--#define CFIDEV_INTERLEAVE_4 (4)
--#endif
--#ifdef CONFIG_MTD_CFI_I8
--#define CFIDEV_INTERLEAVE_8 (8)
--#endif
+- if (offset < page->offset)
+- n = n->rb_left;
+- else if (offset > page->offset)
+- n = n->rb_right;
+- else
+- return page;
+- }
+- return NULL;
+-}
-
--#ifdef CONFIG_MTD_CFI_B1
--#define CFIDEV_BUSWIDTH_1 (1)
--#endif
--#ifdef CONFIG_MTD_CFI_B2
--#define CFIDEV_BUSWIDTH_2 (2)
--#endif
--#ifdef CONFIG_MTD_CFI_B4
--#define CFIDEV_BUSWIDTH_4 (4)
--#endif
--#ifdef CONFIG_MTD_CFI_B8
--#define CFIDEV_BUSWIDTH_8 (8)
--#endif
+-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;
-
--/* pick the largest necessary */
--#ifdef CONFIG_MTD_CFI_B8
--typedef __u64 cfi_word;
+- while (*p)
+- {
+- parent = *p;
+- page = rb_entry(parent, struct page, rb_page_cache);
-
--/* This only works if asm/io.h is included first */
--#ifndef __raw_readll
--#define __raw_readll(addr) (*(volatile __u64 *)(addr))
--#endif
--#ifndef __raw_writell
--#define __raw_writell(v, addr) (*(volatile __u64 *)(addr) = (v))
--#endif
--#define CFI_WORD_64
--#else /* CONFIG_MTD_CFI_B8 */
--/* All others can use 32-bits. It's probably more efficient than
-- the smaller types anyway */
--typedef __u32 cfi_word;
--#endif /* CONFIG_MTD_CFI_B8 */
+- if (offset < page->offset)
+- p = &(*p)->rb_left;
+- else if (offset > page->offset)
+- p = &(*p)->rb_right;
+- else
+- return page;
+- }
-
--#endif
+- rb_link_node(node, parent, p);
-
--/*
-- * The following macros are used to select the code to execute:
-- * cfi_buswidth_is_*()
-- * cfi_interleave_is_*()
-- * [where * is either 1, 2, 4, or 8]
-- * Those macros should be used with 'if' statements. If only one of few
-- * geometry arrangements are selected, they expand to constants thus allowing
-- * the compiler (most of them being 0) to optimize away all the unneeded code,
-- * while still validating the syntax (which is not possible with embedded
-- * #if ... #endif constructs).
-- * The exception to this is the 64-bit versions, which need an extension
-- * to the cfi_word type, and cause compiler warnings about shifts being
-- * out of range.
-- */
+- return NULL;
+-}
-
--#ifdef CFIDEV_INTERLEAVE_1
--# ifdef CFIDEV_INTERLEAVE
--# undef CFIDEV_INTERLEAVE
--# define CFIDEV_INTERLEAVE (cfi->interleave)
--# else
--# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
--# endif
--# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
-+#define cfi_interleave(cfi) 1
-+#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
- #else
--# define cfi_interleave_is_1() (0)
-+#define cfi_interleave_is_1(cfi) (0)
- #endif
-
--#ifdef CFIDEV_INTERLEAVE_2
--# ifdef CFIDEV_INTERLEAVE
--# undef CFIDEV_INTERLEAVE
--# define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I2
-+# ifdef cfi_interleave
-+# undef cfi_interleave
-+# define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
-+# define cfi_interleave(cfi) 2
- # endif
--# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
-+#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
- #else
--# define cfi_interleave_is_2() (0)
-+#define cfi_interleave_is_2(cfi) (0)
- #endif
-
--#ifdef CFIDEV_INTERLEAVE_4
--# ifdef CFIDEV_INTERLEAVE
--# undef CFIDEV_INTERLEAVE
--# define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I4
-+# ifdef cfi_interleave
-+# undef cfi_interleave
-+# define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
-+# define cfi_interleave(cfi) 4
- # endif
--# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
-+#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
- #else
--# define cfi_interleave_is_4() (0)
-+#define cfi_interleave_is_4(cfi) (0)
- #endif
-
--#ifdef CFIDEV_INTERLEAVE_8
--# ifdef CFIDEV_INTERLEAVE
--# undef CFIDEV_INTERLEAVE
--# define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I8
-+# ifdef cfi_interleave
-+# undef cfi_interleave
-+# define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
-+# define cfi_interleave(cfi) 8
- # endif
--# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
-+#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
- #else
--# define cfi_interleave_is_8() (0)
-+#define cfi_interleave_is_8(cfi) (0)
- #endif
-
--#ifndef CFIDEV_INTERLEAVE
--#error You must define at least one interleave to support!
-+static inline int cfi_interleave_supported(int i)
-+{
-+ switch (i) {
-+#ifdef CONFIG_MTD_CFI_I1
-+ case 1:
- #endif
+-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;
+-}
+------------------------------------------------------------------------
+-*/
-
--#ifdef CFIDEV_BUSWIDTH_1
--# ifdef CFIDEV_BUSWIDTH
--# undef CFIDEV_BUSWIDTH
--# define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
--# endif
--# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
--#else
--# define cfi_buswidth_is_1() (0)
-+#ifdef CONFIG_MTD_CFI_I2
-+ case 2:
- #endif
+-#ifndef _LINUX_RBTREE_H
+-#define _LINUX_RBTREE_H
-
--#ifdef CFIDEV_BUSWIDTH_2
--# ifdef CFIDEV_BUSWIDTH
--# undef CFIDEV_BUSWIDTH
--# define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
--# endif
--# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
--#else
--# define cfi_buswidth_is_2() (0)
-+#ifdef CONFIG_MTD_CFI_I4
-+ case 4:
- #endif
+-#include <linux/kernel.h>
+-#include <linux/stddef.h>
-
--#ifdef CFIDEV_BUSWIDTH_4
--# ifdef CFIDEV_BUSWIDTH
--# undef CFIDEV_BUSWIDTH
--# define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
--# endif
--# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
--#else
--# define cfi_buswidth_is_4() (0)
-+#ifdef CONFIG_MTD_CFI_I8
-+ case 8:
- #endif
-+ return 1;
-
--#ifdef CFIDEV_BUSWIDTH_8
--# ifdef CFIDEV_BUSWIDTH
--# undef CFIDEV_BUSWIDTH
--# define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
--# endif
--# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
--#else
--# define cfi_buswidth_is_8() (0)
--#endif
-+ default:
-+ return 0;
-+ }
-+}
-
--#ifndef CFIDEV_BUSWIDTH
--#error You must define at least one bus width to support!
--#endif
+-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;
++ * 2.5 compatibility
++ * $Id$
++ */
- /* NB: these values must represents the number of bytes needed to meet the
- * device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes.
-@@ -222,88 +97,138 @@
+-typedef struct rb_root_s
+-{
+- struct rb_node_s * rb_node;
+-}
+-rb_root_t;
++#ifndef __MTD_COMPAT_RBTREE_H__
++#define __MTD_COMPAT_RBTREE_H__
- /* Basic Query Structure */
- struct cfi_ident {
-- __u8 qry[3];
-- __u16 P_ID;
-- __u16 P_ADR;
-- __u16 A_ID;
-- __u16 A_ADR;
-- __u8 VccMin;
-- __u8 VccMax;
-- __u8 VppMin;
-- __u8 VppMax;
-- __u8 WordWriteTimeoutTyp;
-- __u8 BufWriteTimeoutTyp;
-- __u8 BlockEraseTimeoutTyp;
-- __u8 ChipEraseTimeoutTyp;
-- __u8 WordWriteTimeoutMax;
-- __u8 BufWriteTimeoutMax;
-- __u8 BlockEraseTimeoutMax;
-- __u8 ChipEraseTimeoutMax;
-- __u8 DevSize;
-- __u16 InterfaceDesc;
-- __u16 MaxBufWriteSize;
-- __u8 NumEraseRegions;
-- __u32 EraseRegionInfo[0]; /* Not host ordered */
-+ uint8_t qry[3];
-+ uint16_t P_ID;
-+ uint16_t P_ADR;
-+ uint16_t A_ID;
-+ uint16_t A_ADR;
-+ uint8_t VccMin;
-+ uint8_t VccMax;
-+ uint8_t VppMin;
-+ uint8_t VppMax;
-+ uint8_t WordWriteTimeoutTyp;
-+ uint8_t BufWriteTimeoutTyp;
-+ uint8_t BlockEraseTimeoutTyp;
-+ uint8_t ChipEraseTimeoutTyp;
-+ uint8_t WordWriteTimeoutMax;
-+ uint8_t BufWriteTimeoutMax;
-+ uint8_t BlockEraseTimeoutMax;
-+ uint8_t ChipEraseTimeoutMax;
-+ uint8_t DevSize;
-+ uint16_t InterfaceDesc;
-+ uint16_t MaxBufWriteSize;
-+ uint8_t NumEraseRegions;
-+ uint32_t EraseRegionInfo[0]; /* Not host ordered */
- } __attribute__((packed));
+-#define RB_ROOT (rb_root_t) { NULL, }
+-#define rb_entry(ptr, type, member) \
+- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++#include <linux/version.h>
- /* Extended Query Structure for both PRI and ALT */
+-extern void rb_insert_color(rb_node_t *, rb_root_t *);
+-extern void rb_erase(rb_node_t *, rb_root_t *);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
++#include_next <linux/rbtree.h>
++#else
++#define rb_node_s rb_node
++#define rb_root_s rb_root
- struct cfi_extquery {
-- __u8 pri[3];
-- __u8 MajorVersion;
-- __u8 MinorVersion;
-+ uint8_t pri[3];
-+ uint8_t MajorVersion;
-+ uint8_t MinorVersion;
- } __attribute__((packed));
+-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;
++#include <linux/rbtree-24.h>
- /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
+- *rb_link = node;
+-}
++/* 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
- struct cfi_pri_intelext {
-- __u8 pri[3];
-- __u8 MajorVersion;
-- __u8 MinorVersion;
-- __u32 FeatureSupport;
-- __u8 SuspendCmdSupport;
-- __u16 BlkStatusRegMask;
-- __u8 VccOptimal;
-- __u8 VppOptimal;
-- __u8 NumProtectionFields;
-- __u16 ProtRegAddr;
-- __u8 FactProtRegSize;
-- __u8 UserProtRegSize;
-+ uint8_t pri[3];
-+ uint8_t MajorVersion;
-+ uint8_t MinorVersion;
-+ uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
-+ block follows - FIXME - not currently supported */
-+ uint8_t SuspendCmdSupport;
-+ uint16_t BlkStatusRegMask;
-+ uint8_t VccOptimal;
-+ uint8_t VppOptimal;
-+ uint8_t NumProtectionFields;
-+ uint16_t ProtRegAddr;
-+ uint8_t FactProtRegSize;
-+ uint8_t UserProtRegSize;
-+ uint8_t extra[0];
-+} __attribute__((packed));
+-#endif /* _LINUX_RBTREE_H */
++#endif /* __MTD_COMPAT_RBTREE_H__ */
+--- /dev/null
++++ linux-2.4.21/include/linux/rslib.h
+@@ -0,0 +1,105 @@
++/*
++ * include/linux/rslib.h
++ *
++ * Overview:
++ * Generic Reed Solomon encoder / decoder library
++ *
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * RS code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
++ * $Id$
++ *
++ * 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.
++ */
+
-+struct cfi_intelext_otpinfo {
-+ uint32_t ProtRegAddr;
-+ uint16_t FactGroups;
-+ uint8_t FactProtRegSize;
-+ uint16_t UserGroups;
-+ uint8_t UserProtRegSize;
-+} __attribute__((packed));
++#ifndef _RSLIB_H_
++#define _RSLIB_H_
+
-+struct cfi_intelext_blockinfo {
-+ uint16_t NumIdentBlocks;
-+ uint16_t BlockSize;
-+ uint16_t MinBlockEraseCycles;
-+ uint8_t BitsPerCell;
-+ uint8_t BlockCap;
-+} __attribute__((packed));
++#include <linux/list.h>
+
-+struct cfi_intelext_regioninfo {
-+ uint16_t NumIdentPartitions;
-+ uint8_t NumOpAllowed;
-+ uint8_t NumOpAllowedSimProgMode;
-+ uint8_t NumOpAllowedSimEraMode;
-+ uint8_t NumBlockTypes;
-+ struct cfi_intelext_blockinfo BlockTypes[1];
-+} __attribute__((packed));
++/**
++ * struct rs_control - rs control structure
++ *
++ * @mm: Bits per symbol
++ * @nn: Symbols per block (= (1<<mm)-1)
++ * @alpha_to: log lookup table
++ * @index_of: Antilog lookup table
++ * @genpoly: Generator polynomial
++ * @nroots: Number of generator roots = number of parity symbols
++ * @fcr: First consecutive root, index form
++ * @prim: Primitive element, index form
++ * @iprim: prim-th root of 1, index form
++ * @gfpoly: The primitive generator polynominal
++ * @users: Users of this structure
++ * @list: List entry for the rs control list
++*/
++struct rs_control {
++ int mm;
++ int nn;
++ uint16_t *alpha_to;
++ uint16_t *index_of;
++ uint16_t *genpoly;
++ int nroots;
++ int fcr;
++ int prim;
++ int iprim;
++ int gfpoly;
++ int users;
++ struct list_head list;
++};
+
-+/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
++/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit */
++#ifdef CONFIG_REED_SOLOMON_ENC8
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
++ uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC8
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
++ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++ uint16_t *corr);
++#endif
+
-+struct cfi_pri_amdstd {
-+ uint8_t pri[3];
-+ uint8_t MajorVersion;
-+ uint8_t MinorVersion;
-+ uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
-+ uint8_t EraseSuspend;
-+ uint8_t BlkProt;
-+ uint8_t TmpBlkUnprotect;
-+ uint8_t BlkProtUnprot;
-+ uint8_t SimultaneousOps;
-+ uint8_t BurstMode;
-+ uint8_t PageMode;
-+ uint8_t VppMin;
-+ uint8_t VppMax;
-+ uint8_t TopBottom;
- } __attribute__((packed));
++/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit */
++#ifdef CONFIG_REED_SOLOMON_ENC16
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
++ uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC16
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++ uint16_t *corr);
++#endif
++
++/* Create or get a matching rs control structure */
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
++ int nroots);
++
++/* Release a rs control structure */
++void free_rs(struct rs_control *rs);
++
++/** modulo replacement for galois field arithmetics
++ *
++ * @rs: the rs control structure
++ * @x: the value to reduce
++ *
++ * where
++ * rs->mm = number of bits per symbol
++ * rs->nn = (2^rs->mm) - 1
++ *
++ * Simple arithmetic modulo would return a wrong result for values
++ * >= 3 * rs->nn
++*/
++static inline int rs_modnn(struct rs_control *rs, int x)
++{
++ while (x >= rs->nn) {
++ x -= rs->nn;
++ x = (x >> rs->mm) + (x & rs->nn);
++ }
++ return x;
++}
++
++#endif
+--- linux-2.4.21/include/linux/soundcard.h~ucb1x00
++++ linux-2.4.21/include/linux/soundcard.h
+@@ -811,6 +811,7 @@
+ #define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
+ #define SOUND_MIXER_OUTSRC 0xfa /* Arg contains a bit for each input source to output */
+ #define SOUND_MIXER_OUTMASK 0xf9 /* Arg contains a bit for each supported input source to output */
++#define SOUND_MIXER_AC97 0xf8 /* directly access ac97 registers */
- struct cfi_pri_query {
-- __u8 NumFields;
-- __u32 ProtField[1]; /* Not host ordered */
-+ uint8_t NumFields;
-+ uint32_t ProtField[1]; /* Not host ordered */
- } __attribute__((packed));
+ /* Device mask bits */
- struct cfi_bri_query {
-- __u8 PageModeReadCap;
-- __u8 NumFields;
-- __u32 ConfField[1]; /* Not host ordered */
-+ uint8_t PageModeReadCap;
-+ uint8_t NumFields;
-+ uint32_t ConfField[1]; /* Not host ordered */
- } __attribute__((packed));
+@@ -874,6 +875,7 @@
+ #define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
+ #define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
+ #define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
++#define SOUND_MIXER_READ_AC97 MIXER_READ(SOUND_MIXER_AC97)
--#define P_ID_NONE 0
--#define P_ID_INTEL_EXT 1
--#define P_ID_AMD_STD 2
--#define P_ID_INTEL_STD 3
--#define P_ID_AMD_EXT 4
--#define P_ID_MITSUBISHI_STD 256
--#define P_ID_MITSUBISHI_EXT 257
--#define P_ID_RESERVED 65535
-+#define P_ID_NONE 0x0000
-+#define P_ID_INTEL_EXT 0x0001
-+#define P_ID_AMD_STD 0x0002
-+#define P_ID_INTEL_STD 0x0003
-+#define P_ID_AMD_EXT 0x0004
-+#define P_ID_WINBOND 0x0006
-+#define P_ID_ST_ADV 0x0020
-+#define P_ID_MITSUBISHI_STD 0x0100
-+#define P_ID_MITSUBISHI_EXT 0x0101
-+#define P_ID_SST_PAGE 0x0102
-+#define P_ID_INTEL_PERFORMANCE 0x0200
-+#define P_ID_INTEL_DATA 0x0210
-+#define P_ID_RESERVED 0xffff
+ #define MIXER_WRITE(dev) _SIOWR('M', dev, int)
+ #define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
+@@ -900,6 +902,7 @@
+ #define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
+ #define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
++#define SOUND_MIXER_WRITE_AC97 MIXER_WRITE(SOUND_MIXER_AC97)
- #define CFI_MODE_CFI 1
- #define CFI_MODE_JEDEC 0
+ typedef struct mixer_info
+ {
+--- /dev/null
++++ linux-2.4.21/include/linux/suspend.h
+@@ -0,0 +1,10 @@
++/* $Id$ */
++
++#ifndef __MTD_COMPAT_VERSION_H__
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#include_next <linux/suspend.h>
++#endif
++
++#endif /* __MTD_COMPAT_VERSION_H__ */
+--- linux-2.4.21/include/linux/tty.h~ramses-lcd
++++ linux-2.4.21/include/linux/tty.h
+@@ -10,8 +10,8 @@
+ * resizing).
+ */
+ #define MIN_NR_CONSOLES 1 /* must be at least 1 */
+-#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */
+-#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */
++#define MAX_NR_CONSOLES 3 /* serial lines start at 64 */
++#define MAX_NR_USER_CONSOLES 3 /* must be root to allocate above this */
+ /* Note: the ioctl VT_GETSTATE does not work for
+ consoles 16 and higher (since it returns a short) */
- struct cfi_private {
-- __u16 cmdset;
-+ uint16_t cmdset;
- void *cmdset_priv;
- int interleave;
- int device_type;
- int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */
- int addr_unlock1;
- int addr_unlock2;
-- int fast_prog;
- struct mtd_info *(*cmdset_setup)(struct map_info *);
- struct cfi_ident *cfiq; /* For now only one. We insist that all devs
- must be of the same type. */
-@@ -314,107 +239,81 @@
- struct flchip chips[0]; /* per-chip data structure for each chip */
- };
+--- /dev/null
++++ linux-2.4.21/include/linux/uinput.h
+@@ -0,0 +1,79 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#ifndef __UINPUT_H_
++#define __UINPUT_H_
++
++#ifdef __KERNEL__
++#define UINPUT_MINOR 223
++#define UINPUT_NAME "uinput"
++#define UINPUT_BUFFER_SIZE 16
++
++/* state flags => bit index for {set|clear|test}_bit ops */
++#define UIST_CREATED 0
++
++struct uinput_device {
++ struct input_dev *dev;
++ unsigned long state;
++ wait_queue_head_t waitq;
++ unsigned char ready,
++ head,
++ tail;
++ struct input_event buff[UINPUT_BUFFER_SIZE];
++};
++#endif /* __KERNEL__ */
++
++/* ioctl */
++#define UINPUT_IOCTL_BASE 'U'
++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++#ifndef NBITS
++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
++#endif /* NBITS */
++
++#define UINPUT_MAX_NAME_SIZE 80
++struct uinput_user_dev {
++ char name[UINPUT_MAX_NAME_SIZE];
++ unsigned short idbus;
++ unsigned short idvendor;
++ unsigned short idproduct;
++ unsigned short idversion;
++ int ff_effects_max;
++ int absmax[ABS_MAX + 1];
++ int absmin[ABS_MAX + 1];
++ int absfuzz[ABS_MAX + 1];
++ int absflat[ABS_MAX + 1];
++};
++#endif /* __UINPUT_H_ */
+--- linux-2.4.21/include/linux/usb.h~ramses-usb
++++ linux-2.4.21/include/linux/usb.h
+@@ -1079,7 +1079,7 @@
+ void usb_show_string(struct usb_device *dev, char *id, int index);
--#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
--
+ #ifdef DEBUG
+-#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
++#define dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
+ #else
+ #define dbg(format, arg...) do {} while (0)
+ #endif
+--- linux-2.4.21/include/linux/wireless.h~linux-iw241_we16-6
++++ linux-2.4.21/include/linux/wireless.h
+@@ -1,7 +1,7 @@
/*
- * Returns the command address according to the given geometry.
- */
--static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
-+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
- {
- return (cmd_ofs * type) * interleave;
- }
+ * This file define a set of standard wireless extensions
+ *
+- * Version : 15 12.7.02
++ * Version : 16 2.4.03
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -69,6 +69,8 @@
- /*
-- * Transforms the CFI command for the given geometry (bus width & interleave.
-+ * Transforms the CFI command for the given geometry (bus width & interleave).
-+ * It looks too long to be inline, but in the common case it should almost all
-+ * get optimised away.
+ /***************************** INCLUDES *****************************/
+
++/* To minimise problems in user space, I might remove those headers
++ * at some point. Jean II */
+ #include <linux/types.h> /* for "caddr_t" et al */
+ #include <linux/socket.h> /* for "struct sockaddr" et al */
+ #include <linux/if.h> /* for IFNAMSIZ and co... */
+@@ -80,7 +82,7 @@
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
*/
--static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
-+static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
- {
-- cfi_word val = 0;
-+ map_word val = { {0} };
-+ int wordwidth, words_per_bus, chip_mode, chips_per_word;
-+ unsigned long onecmd;
-+ int i;
+-#define WIRELESS_EXT 15
++#define WIRELESS_EXT 16
-- if (cfi_buswidth_is_1()) {
-- /* 1 x8 device */
-- val = cmd;
-- } else if (cfi_buswidth_is_2()) {
-- if (cfi_interleave_is_1()) {
-- /* 1 x16 device in x16 mode */
-- val = cpu_to_cfi16(cmd);
-- } else if (cfi_interleave_is_2()) {
-- /* 2 (x8, x16 or x32) devices in x8 mode */
-- val = cpu_to_cfi16((cmd << 8) | cmd);
-- }
-- } else if (cfi_buswidth_is_4()) {
-- if (cfi_interleave_is_1()) {
-- /* 1 x32 device in x32 mode */
-- val = cpu_to_cfi32(cmd);
-- } else if (cfi_interleave_is_2()) {
-- /* 2 x16 device in x16 mode */
-- val = cpu_to_cfi32((cmd << 16) | cmd);
-- } else if (cfi_interleave_is_4()) {
-- /* 4 (x8, x16 or x32) devices in x8 mode */
-- val = (cmd << 16) | cmd;
-- val = cpu_to_cfi32((val << 8) | val);
-- }
--#ifdef CFI_WORD_64
-- } else if (cfi_buswidth_is_8()) {
-- if (cfi_interleave_is_1()) {
-- /* 1 x64 device in x64 mode */
-- val = cpu_to_cfi64(cmd);
-- } else if (cfi_interleave_is_2()) {
-- /* 2 x32 device in x32 mode */
-- val = cmd;
-- val = cpu_to_cfi64((val << 32) | val);
-- } else if (cfi_interleave_is_4()) {
-- /* 4 (x16, x32 or x64) devices in x16 mode */
-- val = (cmd << 16) | cmd;
-- val = cpu_to_cfi64((val << 32) | val);
-- } else if (cfi_interleave_is_8()) {
-- /* 8 (x8, x16 or x32) devices in x8 mode */
-- val = (cmd << 8) | cmd;
-- val = (val << 16) | val;
-- val = (val << 32) | val;
-- val = cpu_to_cfi64(val);
-- }
--#endif /* CFI_WORD_64 */
-+ /* We do it this way to give the compiler a fighting chance
-+ of optimising away all the crap for 'bankwidth' larger than
-+ an unsigned long, in the common case where that support is
-+ disabled */
-+ if (map_bankwidth_is_large(map)) {
-+ wordwidth = sizeof(unsigned long);
-+ words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
-+ } else {
-+ wordwidth = map_bankwidth(map);
-+ words_per_bus = 1;
- }
-- return val;
--}
--#define CMD(x) cfi_build_cmd((x), map, cfi)
+ /*
+ * Changes :
+@@ -163,6 +165,16 @@
+ * - Add IW_TXPOW_RANGE for range of Tx Powers
+ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+ * - Add IW_MODE_MONITOR for passive monitor
++ *
++ * V15 to V16
++ * ----------
++ * - Increase the number of bitrates in iw_range to 32 (for 802.11g)
++ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
++ * - Reshuffle struct iw_range for increases, add filler
++ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
++ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
++ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
++ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ */
--/*
-- * Read a value according to the bus width.
-- */
-+ chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
-+ chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+ /**************************** CONSTANTS ****************************/
+@@ -196,9 +208,11 @@
+ /* SIOCGIWSTATS is strictly used between user space and the kernel, and
+ * is never passed to the driver (i.e. the driver will never see it). */
--static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
--{
-- if (cfi_buswidth_is_1()) {
-- return map->read8(map, addr);
-- } else if (cfi_buswidth_is_2()) {
-- return map->read16(map, addr);
-- } else if (cfi_buswidth_is_4()) {
-- return map->read32(map, addr);
-- } else if (cfi_buswidth_is_8()) {
-- return map->read64(map, addr);
-- } else {
-- return 0;
-+ /* First, determine what the bit-pattern should be for a single
-+ device, according to chip mode and endianness... */
-+ switch (chip_mode) {
-+ default: BUG();
-+ case 1:
-+ onecmd = cmd;
-+ break;
-+ case 2:
-+ onecmd = cpu_to_cfi16(cmd);
-+ break;
-+ case 4:
-+ onecmd = cpu_to_cfi32(cmd);
-+ break;
- }
--}
+-/* Mobile IP support (statistics per MAC address) */
++/* Spy support (statistics per MAC address - used for Mobile IP support) */
+ #define SIOCSIWSPY 0x8B10 /* set spy addresses */
+ #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
++#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */
++#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */
--/*
-- * Write a value according to the bus width.
-- */
-+ /* Now replicate it across the size of an unsigned long, or
-+ just to the bus width as appropriate */
-+ switch (chips_per_word) {
-+ default: BUG();
-+#if BITS_PER_LONG >= 64
-+ case 8:
-+ onecmd |= (onecmd << (chip_mode * 32));
-+#endif
-+ case 4:
-+ onecmd |= (onecmd << (chip_mode * 16));
-+ case 2:
-+ onecmd |= (onecmd << (chip_mode * 8));
-+ case 1:
-+ ;
-+ }
+ /* Access Point manipulation */
+ #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
+@@ -294,7 +308,7 @@
+ #define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
+ #define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
--static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
--{
-- if (cfi_buswidth_is_1()) {
-- map->write8(map, val, addr);
-- } else if (cfi_buswidth_is_2()) {
-- map->write16(map, val, addr);
-- } else if (cfi_buswidth_is_4()) {
-- map->write32(map, val, addr);
-- } else if (cfi_buswidth_is_8()) {
-- map->write64(map, val, addr);
-+ /* And finally, for the multi-word case, replicate it
-+ in all words in the structure */
-+ for (i=0; i < words_per_bus; i++) {
-+ val.x[i] = onecmd;
- }
-+
-+ return val;
- }
-+#define CMD(x) cfi_build_cmd((x), map, cfi)
+-#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */
++#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */
+
+ #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+
+@@ -306,13 +320,13 @@
+ /* ----------------------- OTHER CONSTANTS ----------------------- */
+
+ /* Maximum frequencies in the range struct */
+-#define IW_MAX_FREQUENCIES 16
++#define IW_MAX_FREQUENCIES 32
+ /* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+ /* Maximum bit rates in the range struct */
+-#define IW_MAX_BITRATES 8
++#define IW_MAX_BITRATES 32
+
+ /* Maximum tx powers in the range struct */
+ #define IW_MAX_TXPOWER 8
+@@ -320,8 +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 /* set */
+-#define IW_MAX_GET_SPY 64 /* get */
++#define IW_MAX_SPY 8
+
+ /* Maximum of address that you may get in the
+ list of access points in range */
+@@ -354,7 +367,8 @@
+ #define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
+ #define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
+ #define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
+-#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
++#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
++#define IW_ENCODE_TEMP 0x0400 /* Temporary key */
+
+ /* Power management flags available (along with the value, if any) */
+ #define IW_POWER_ON 0x0000 /* No details... */
+@@ -482,6 +496,17 @@
+ __u32 beacon; /* Missed beacons/superframe */
+ };
++/*
++ * Quality range (for spy threshold)
++ */
++struct iw_thrspy
++{
++ struct sockaddr addr; /* Source address (hw/mac) */
++ struct iw_quality qual; /* Quality of the link */
++ struct iw_quality low; /* Low threshold */
++ struct iw_quality high; /* High threshold */
++};
++
+ /* ------------------------ WIRELESS STATS ------------------------ */
/*
- * Sends a CFI command to a bank of flash for the given geometry.
-@@ -423,50 +322,47 @@
- * If prev_val is non-null, it will be set to the value at the command address,
- * before the command was written.
- */
--static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
-+static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
- struct map_info *map, struct cfi_private *cfi,
-- int type, cfi_word *prev_val)
-+ int type, map_word *prev_val)
- {
-- cfi_word val;
-- __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
-+ map_word val;
-+ uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+ * Wireless statistics (used for /proc/net/wireless)
+@@ -534,7 +559,7 @@
+ struct iw_quality qual; /* Quality part of statistics */
- val = cfi_build_cmd(cmd, map, cfi);
+ struct sockaddr ap_addr; /* Access point address */
+- struct sockaddr addr; /* Destination address (hw) */
++ struct sockaddr addr; /* Destination address (hw/mac) */
- if (prev_val)
-- *prev_val = cfi_read(map, addr);
-+ *prev_val = map_read(map, addr);
+ struct iw_param param; /* Other small parameters */
+ struct iw_point data; /* Other large parameters */
+@@ -582,17 +607,31 @@
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
-- cfi_write(map, val, addr);
-+ map_write(map, val, addr);
+- /* Frequency */
+- __u16 num_channels; /* Number of channels [0; num - 1] */
+- __u8 num_frequency; /* Number of entry in the list */
+- struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
+- /* Note : this frequency list doesn't need to fit channel numbers */
++ /* Old Frequency (backward compat - moved lower ) */
++ __u16 old_num_channels;
++ __u8 old_num_frequency;
++ /* Filler to keep "version" at the same offset */
++ __s32 old_freq[6];
- return addr - base;
- }
+ /* signal level threshold range */
+ __s32 sensitivity;
--static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
-+static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
- {
-- if (cfi_buswidth_is_1()) {
-- return map->read8(map, addr);
-- } else if (cfi_buswidth_is_2()) {
-- return cfi16_to_cpu(map->read16(map, addr));
-- } else if (cfi_buswidth_is_4()) {
-- return cfi32_to_cpu(map->read32(map, addr));
-- } else if (cfi_buswidth_is_8()) {
-- return cfi64_to_cpu(map->read64(map, addr));
-+ map_word val = map_read(map, addr);
+ /* Quality of link & SNR stuff */
++ /* Quality range (link, level, noise)
++ * If the quality is absolute, it will be in the range [0 ; max_qual],
++ * if the quality is dBm, it will be in the range [max_qual ; 0].
++ * Don't forget that we use 8 bit arithmetics... */
+ struct iw_quality max_qual; /* Quality of the link */
++ /* This should contain the average/typical values of the quality
++ * indicator. This should be the threshold between a "good" and
++ * a "bad" link (example : monitor going from green to orange).
++ * Currently, user space apps like quality monitors don't have any
++ * way to calibrate the measurement. With this, they can split
++ * the range between 0 and max_qual in different quality level
++ * (using a geometric subdivision centered on the average).
++ * I expect that people doing the user space apps will feedback
++ * us on which value we need to put in each driver... */
++ struct iw_quality avg_qual; /* Quality of the link */
+
+ /* Rates */
+ __u8 num_bitrates; /* Number of entries in the list */
+@@ -619,6 +658,8 @@
+ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
+ __u8 num_encoding_sizes; /* Number of entry in the list */
+ __u8 max_encoding_tokens; /* Max number of tokens */
++ /* For drivers that need a "login/passwd" form */
++ __u8 encoding_login_index; /* token index for login token */
+
+ /* Transmit power */
+ __u16 txpower_capa; /* What options are supported */
+@@ -638,18 +679,12 @@
+ __s32 min_r_time; /* Minimal retry lifetime */
+ __s32 max_r_time; /* Maximal retry lifetime */
+
+- /* Average quality of link & SNR */
+- struct iw_quality avg_qual; /* Quality of the link */
+- /* This should contain the average/typical values of the quality
+- * indicator. This should be the threshold between a "good" and
+- * a "bad" link (example : monitor going from green to orange).
+- * Currently, user space apps like quality monitors don't have any
+- * way to calibrate the measurement. With this, they can split
+- * the range between 0 and max_qual in different quality level
+- * (using a geometric subdivision centered on the average).
+- * I expect that people doing the user space apps will feedback
+- * us on which value we need to put in each driver...
+- */
++ /* Frequency */
++ __u16 num_channels; /* Number of channels [0; num - 1] */
++ __u8 num_frequency; /* Number of entry in the list */
++ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
++ /* Note : this frequency list doesn't need to fit channel numbers,
++ * because each entry contain its channel index */
+ };
+
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/workqueue.h
+@@ -0,0 +1,21 @@
++/*
++ * 2.5 compatibility
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_WORKQUEUE_H__
++#define __MTD_COMPAT_WORKQUEUE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
++#include_next <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#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__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/inftl-user.h
+@@ -0,0 +1,91 @@
++/*
++ * $Id$
++ *
++ * Parts of INFTL headers shared with userspace
++ *
++ */
++
++#ifndef __MTD_INFTL_USER_H__
++#define __MTD_INFTL_USER_H__
++
++#define OSAK_VERSION 0x5120
++#define PERCENTUSED 98
++
++#define SECTORSIZE 512
++
++/* Block Control Information */
++
++struct inftl_bci {
++ uint8_t ECCsig[6];
++ uint8_t Status;
++ uint8_t Status1;
++} __attribute__((packed));
++
++struct inftl_unithead1 {
++ uint16_t virtualUnitNo;
++ uint16_t prevUnitNo;
++ uint8_t ANAC;
++ uint8_t NACs;
++ uint8_t parityPerField;
++ uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unithead2 {
++ uint8_t parityPerField;
++ uint8_t ANAC;
++ uint16_t prevUnitNo;
++ uint16_t virtualUnitNo;
++ uint8_t NACs;
++ uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unittail {
++ uint8_t Reserved[4];
++ uint16_t EraseMark;
++ uint16_t 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
++
++#endif /* __MTD_INFTL_USER_H__ */
++
++
+--- /dev/null
++++ linux-2.4.21/include/mtd/jffs2-user.h
+@@ -0,0 +1,35 @@
++/*
++ * $Id$
++ *
++ * JFFS2 definitions for use in user space only
++ */
++
++#ifndef __JFFS2_USER_H__
++#define __JFFS2_USER_H__
++
++/* This file is blessed for inclusion by userspace */
++#include <linux/jffs2.h>
++#include <endian.h>
++#include <byteswap.h>
++
++#undef cpu_to_je16
++#undef cpu_to_je32
++#undef cpu_to_jemode
++#undef je16_to_cpu
++#undef je32_to_cpu
++#undef jemode_to_cpu
++
++extern int target_endian;
++
++#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
++#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
++
++#define cpu_to_je16(x) ((jint16_t){t16(x)})
++#define cpu_to_je32(x) ((jint32_t){t32(x)})
++#define cpu_to_jemode(x) ((jmode_t){t32(x)})
++
++#define je16_to_cpu(x) (t16((x).v16))
++#define je32_to_cpu(x) (t32((x).v32))
++#define jemode_to_cpu(x) (t32((x).m))
++
++#endif /* __JFFS2_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-abi.h
+@@ -0,0 +1,119 @@
++/*
++ * $Id$
++ *
++ * Portions of MTD ABI definition which are shared by kernel and user space
++ */
++
++#ifndef __MTD_ABI_H__
++#define __MTD_ABI_H__
++
++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
++ separate files was to avoid #ifdef __KERNEL__ */
++#define __user
++#endif
++
++struct erase_info_user {
++ uint32_t start;
++ uint32_t length;
++};
++
++struct mtd_oob_buf {
++ uint32_t start;
++ uint32_t length;
++ unsigned char __user *ptr;
++};
++
++#define MTD_ABSENT 0
++#define MTD_RAM 1
++#define MTD_ROM 2
++#define MTD_NORFLASH 3
++#define MTD_NANDFLASH 4
++#define MTD_PEROM 5
++#define MTD_DATAFLASH 6
++#define MTD_OTHER 14
++#define MTD_UNKNOWN 15
++
++#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
++#define MTD_SET_BITS 2 // Bits can be set
++#define MTD_ERASEABLE 4 // Has an erase function
++#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
++#define MTD_VOLATILE 16 // Set for RAMs
++#define MTD_XIP 32 // eXecute-In-Place possible
++#define MTD_OOB 64 // Out-of-band data (NAND flash)
++#define MTD_ECC 128 // Device capable of automatic ECC
++#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed
++
++// Some common devices / combinations of capabilities
++#define MTD_CAP_ROM 0
++#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
++#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
++#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
++#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
++
++
++// Types of automatic ECC/Checksum available
++#define MTD_ECC_NONE 0 // No automatic ECC available
++#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
++#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
++
++/* ECC byte placement */
++#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
++#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
++#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
++#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
++
++/* OTP mode selection */
++#define MTD_OTP_OFF 0
++#define MTD_OTP_FACTORY 1
++#define MTD_OTP_USER 2
++
++struct mtd_info_user {
++ uint8_t type;
++ uint32_t flags;
++ uint32_t size; // Total size of the MTD
++ uint32_t erasesize;
++ uint32_t oobblock; // Size of OOB blocks (e.g. 512)
++ uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
++ uint32_t ecctype;
++ uint32_t eccsize;
++};
++
++struct region_info_user {
++ uint32_t offset; /* At which this region starts,
++ * from the beginning of the MTD */
++ uint32_t erasesize; /* For this region */
++ uint32_t numblocks; /* Number of blocks in this region */
++ uint32_t regionindex;
++};
++
++struct otp_info {
++ uint32_t start;
++ uint32_t length;
++ uint32_t locked;
++};
+
-+ if (map_bankwidth_is_1(map)) {
-+ return val.x[0];
-+ } else if (map_bankwidth_is_2(map)) {
-+ return cfi16_to_cpu(val.x[0]);
- } else {
-- return 0;
-+ /* No point in a 64-bit byteswap since that would just be
-+ swapping the responses from different chips, and we are
-+ only interested in one chip (a representative sample) */
-+ return cfi32_to_cpu(val.x[0]);
- }
- }
-
- static inline void cfi_udelay(int us)
- {
--#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-- unsigned long t = us * HZ / 1000000;
-- if (t) {
-- set_current_state(TASK_UNINTERRUPTIBLE);
-- schedule_timeout(t);
-- return;
-- }
--#endif
-+ if (us >= 1000) {
-+ msleep((us+999)/1000);
-+ } else {
- udelay(us);
- cond_resched();
-+ }
- }
-
- static inline void cfi_spin_lock(spinlock_t *mutex)
-@@ -479,5 +375,28 @@
- spin_unlock_bh(mutex);
- }
-
-+struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
-+ const char* name);
-+struct cfi_fixup {
-+ uint16_t mfr;
-+ uint16_t id;
-+ void (*fixup)(struct mtd_info *mtd, void* param);
-+ void* param;
++#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
++#define MEMERASE _IOW('M', 2, struct erase_info_user)
++#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
++#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
++#define MEMLOCK _IOW('M', 5, struct erase_info_user)
++#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 MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
++#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
++#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
++#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
++#define OTPSELECT _IOR('M', 13, int)
++#define OTPGETREGIONCOUNT _IOW('M', 14, int)
++#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
++#define OTPLOCK _IOR('M', 16, struct otp_info)
++
++struct nand_oobinfo {
++ uint32_t useecc;
++ uint32_t eccbytes;
++ uint32_t oobfree[8][2];
++ uint32_t eccpos[32];
+};
+
-+#define CFI_MFR_ANY 0xffff
-+#define CFI_ID_ANY 0xffff
++#endif /* __MTD_ABI_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-user.h
+@@ -0,0 +1,20 @@
++/*
++ * $Id$
++ *
++ * MTD ABI header for use by user space only.
++ */
+
-+#define CFI_MFR_AMD 0x0001
-+#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
++#ifndef __MTD_USER_H__
++#define __MTD_USER_H__
+
-+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
++#include <stdint.h>
+
-+typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
-+ unsigned long adr, int len, void *thunk);
++/* This file is blessed for inclusion by userspace */
++#include <mtd/mtd-abi.h>
+
-+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
-+ loff_t ofs, size_t len, void *thunk);
++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;
+
-
- #endif /* __MTD_CFI_H__ */
---- linux-2.4.21/include/linux/mtd/compatmac.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/compatmac.h
-@@ -1,583 +1,223 @@
--
- /*
-- * mtd/include/compatmac.h
-- *
-- * $Id$
++#endif /* __MTD_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/nftl-user.h
+@@ -0,0 +1,76 @@
++/*
+ * $Id$
- *
- * 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 <linux/config.h>
--#include <linux/module.h>
--#ifndef LINUX_VERSION_CODE
- #include <linux/version.h>
--#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 <linux/types.h> /* 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 <asm/segment.h>
--# 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 <asm/byteorder.h>
--# 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 <linux/compatmac.h>
--#endif // LINUX_VERSION_CODE < 0x020100
--
--
--#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
--#include <linux/vmalloc.h>
--#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 */
--#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 <linux/poll.h>
--#else
--# define __USE_OLD_SELECT__
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
-+#error "This kernel is too old: not supported by this file"
- #endif
-
--/* 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
-+ /* O(1) scheduler stuff. */
-
--/* changed the prototype of read/write */
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
-+#include <linux/sched.h>
-+static inline void __recalc_sigpending(void)
-+{
-+ recalc_sigpending(current);
-+}
-+#undef recalc_sigpending
-+#define recalc_sigpending() __recalc_sigpending ()
-
--#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
-+#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
- #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 <linux/init.h>
--#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 <linux/sched.h>
-
-+#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
--
--
--/* 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 <asm/byteorder.h>
--
--#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
-+ daemonize();
-
--#ifndef THIS_MODULE
--#ifdef MODULE
--#define THIS_MODULE (&__this_module)
--#else
--#define THIS_MODULE (NULL)
--#endif
--#endif
-+ 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)
++ *
++ * Parts of NFTL headers shared with userspace
++ *
++ */
++
++#ifndef __MTD_NFTL_USER_H__
++#define __MTD_NFTL_USER_H__
++
++/* Block Control Information */
++
++struct nftl_bci {
++ unsigned char ECCSig[6];
++ uint8_t Status;
++ uint8_t Status1;
++}__attribute__((packed));
++
++/* Unit Control Information */
++
++struct nftl_uci0 {
++ uint16_t VirtUnitNum;
++ uint16_t ReplUnitNum;
++ uint16_t SpareVirtUnitNum;
++ uint16_t SpareReplUnitNum;
++} __attribute__((packed));
++
++struct nftl_uci1 {
++ uint32_t WearInfo;
++ uint16_t EraseMark;
++ uint16_t EraseMark1;
++} __attribute__((packed));
++
++struct nftl_uci2 {
++ uint16_t FoldMark;
++ uint16_t FoldMark1;
++ uint32_t unused;
++} __attribute__((packed));
++
++union nftl_uci {
++ struct nftl_uci0 a;
++ struct nftl_uci1 b;
++ struct nftl_uci2 c;
++};
++
++struct nftl_oob {
++ struct nftl_bci b;
++ union nftl_uci u;
++};
++
++/* NFTL Media Header */
++
++struct NFTLMediaHeader {
++ char DataOrgID[6];
++ uint16_t NumEraseUnits;
++ uint16_t FirstPhysicalEUN;
++ uint32_t FormattedSize;
++ unsigned char UnitSizeFactor;
++} __attribute__((packed));
++
++#define MAX_ERASE_ZONES (8192 - 512)
++
++#define ERASE_MARK 0x3c69
++#define SECTOR_FREE 0xff
++#define SECTOR_USED 0x55
++#define SECTOR_IGNORE 0x11
++#define SECTOR_DELETED 0x00
++
++#define FOLD_MARK_IN_PROGRESS 0x5555
++
++#define ZONE_GOOD 0xff
++#define ZONE_BAD_ORIGINAL 0
++#define ZONE_BAD_MARKED 7
++
++
++#endif /* __MTD_NFTL_USER_H__ */
+--- linux-2.4.21/include/net/bluetooth/bluetooth.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/bluetooth.h
+@@ -51,6 +51,8 @@
+ #define BTPROTO_SCO 2
+ #define BTPROTO_RFCOMM 3
+ #define BTPROTO_BNEP 4
++#define BTPROTO_CMTP 5
++#define BTPROTO_HIDP 6
+
+ #define SOL_HCI 0
+ #define SOL_L2CAP 6
+@@ -155,7 +157,7 @@
+ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+ int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
+ uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
+-int bluez_sock_w4_connect(struct sock *sk, int flags);
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
+
+ void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
+ struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
+--- linux-2.4.21/include/net/bluetooth/hci.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci.h
+@@ -50,6 +50,11 @@
+ #define HCI_RS232 4
+ #define HCI_PCI 5
+
++/* HCI device quirks */
++enum {
++ HCI_QUIRK_RESET_ON_INIT
++};
++
+ /* HCI device flags */
+ enum {
+ HCI_UP,
+@@ -160,6 +165,7 @@
+ #define HCI_LM_AUTH 0x0002
+ #define HCI_LM_ENCRYPT 0x0004
+ #define HCI_LM_TRUSTED 0x0008
++#define HCI_LM_RELIABLE 0x0010
+
+ /* ----- HCI Commands ----- */
+ /* OGF & OCF values */
+@@ -333,6 +339,8 @@
+ } __attribute__ ((packed)) status_bdaddr_rp;
+ #define STATUS_BDADDR_RP_SIZE 7
+
++#define OCF_INQUIRY_CANCEL 0x0002
++
+ #define OCF_LINK_KEY_REPLY 0x000B
+ #define OCF_LINK_KEY_NEG_REPLY 0x000C
+ typedef struct {
+@@ -459,6 +467,17 @@
+ } __attribute__ ((packed)) inquiry_info;
+ #define INQUIRY_INFO_SIZE 14
+
++#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_period_mode;
++ __u8 dev_class[3];
++ __u16 clock_offset;
++ __s8 rssi;
++} __attribute__ ((packed)) inquiry_info_with_rssi;
++#define INQUIRY_INFO_WITH_RSSI_SIZE 14
++
+ #define EVT_CONN_COMPLETE 0x03
+ typedef struct {
+ __u8 status;
+--- linux-2.4.21/include/net/bluetooth/hci_core.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci_core.h
+@@ -72,7 +72,9 @@
+ __u16 pkt_type;
+ __u16 link_policy;
+ __u16 link_mode;
+-
++
++ unsigned long quirks;
++
+ atomic_t cmd_cnt;
+ unsigned int acl_cnt;
+ unsigned int sco_cnt;
+@@ -167,6 +169,12 @@
+ c->list = NULL;
+ }
--#if LINUX_VERSION_CODE < 0x20300
--#include <linux/interrupt.h>
--#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 <asm/softirq.h>
--#include <linux/spinlock.h>
--#endif
-+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
+{
-+ unsigned long flags;
-+ unsigned long ret;
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
--#define set_current_state(state_value) \
-- do { current->state = (state_value); } while (0)
--#endif
-+ spin_lock_irqsave(¤t->sigmask_lock, flags);
-+ ret = dequeue_signal(mask, info);
-+ spin_unlock_irqrestore(¤t->sigmask_lock, flags);
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
--static inline int invalidate_device(kdev_t dev, int do_sync) {
-+ return ret;
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return (c->list == NULL);
+}
-
-- if (do_sync)
-- fsync_dev(dev);
-+static inline int allow_signal(int sig)
-+{
-+ if (sig < 1 || sig > _NSIG)
-+ return -EINVAL;
-
-- invalidate_buffers(dev);
-+ 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;
- }
--#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);
-+static inline int disallow_signal(int sig)
-+{
-+ if (sig < 1 || sig > _NSIG)
-+ return -EINVAL;
-
-- if (sb)
-- res = invalidate_inodes(sb);
-+ spin_lock_irq(¤t->sigmask_lock);
-+ sigaddset(¤t->blocked, sig);
-+ recalc_sigpending();
-
-- invalidate_buffers(dev);
-- return res;
-+ current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
-+ spin_unlock_irq(¤t->sigmask_lock);
-+ return 0;
- }
+
-+#define PF_FREEZE 0
-+#define refrigerator(x) do { ; } while(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; })
-+ /* Module bits */
-
--#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; })
-+#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,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);
-+ /* 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,4,9)
--/* This came later */
--#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
-+#define PageUptodate(x) Page_Uptodate(x)
- #endif
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
-- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
--
--#include <linux/genhd.h>
--
--static inline void add_gendisk(struct gendisk *gp)
--{
-- gp->next = gendisk_head;
-- gendisk_head = gp;
--}
--
--static inline void del_gendisk(struct gendisk *gp)
--{
-- struct gendisk *gd, **gdp;
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
-+#define get_seconds() CURRENT_TIME
-+#endif
-
-- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
-- if (*gdp == gp) {
-- gd = *gdp; *gdp = gd->next;
-- break;
-- }
--}
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
-+#define generic_file_readonly_mmap generic_file_mmap
- #endif
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
-
--#define module_init(func) \
--mod_init_t init_module(void) { \
-- return func(); \
--}
-+#include <linux/kmod.h>
-+#include <linux/string.h>
-
--#define module_exit(func) \
--mod_exit_t cleanup_module(void) { \
-- return func(); \
-+static inline char *strlcpy(char *dest, const char *src, int len)
-+{
-+ dest[len-1] = 0;
-+ return strncpy(dest, src, len-1);
- }
--#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 <linux/sched.h>
--static inline void __recalc_sigpending(void)
-+static inline int do_old_request_module(const char *mod)
+ static inline long inquiry_cache_age(struct hci_dev *hdev)
{
-- recalc_sigpending(current);
-+ return request_module(mod);
+ struct inquiry_cache *c = &hdev->inq_cache;
+@@ -282,10 +290,12 @@
+ static inline void hci_conn_put(struct hci_conn *conn)
+ {
+ if (atomic_dec_and_test(&conn->refcnt)) {
+- if (conn->type == SCO_LINK)
++ if (conn->type == ACL_LINK) {
++ unsigned long timeo = (conn->out) ?
++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
++ hci_conn_set_timer(conn, timeo);
++ } else
+ hci_conn_set_timer(conn, HZ / 100);
+- else if (conn->out)
+- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
+ }
}
--#undef recalc_sigpending
--#define recalc_sigpending() __recalc_sigpending ()
--#endif
--
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
--#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
--#endif
-+#undef request_module
-+#define request_module(fmt, ...) \
-+ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
--#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
-+#endif /* 2.5.70 */
+--- linux-2.4.21/include/net/bluetooth/l2cap.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/l2cap.h
+@@ -60,6 +60,7 @@
+ #define L2CAP_LM_AUTH 0x0002
+ #define L2CAP_LM_ENCRYPT 0x0004
+ #define L2CAP_LM_TRUSTED 0x0008
++#define L2CAP_LM_RELIABLE 0x0010
+
+ #define L2CAP_QOS 0x04
+ struct l2cap_qos {
+@@ -189,6 +190,14 @@
+ } __attribute__ ((packed)) l2cap_info_rsp;
+ #define L2CAP_INFO_RSP_SIZE 4
+
++/* info type */
++#define L2CAP_IT_CL_MTU 0x0001
++#define L2CAP_IT_FEAT_MASK 0x0002
++
++/* info result */
++#define L2CAP_IR_SUCCESS 0x0000
++#define L2CAP_IR_NOTSUPP 0x0001
++
+ /* ----- L2CAP connections ----- */
+ struct l2cap_chan_list {
+ struct sock *head;
+@@ -229,6 +238,7 @@
+ __u32 link_mode;
+
+ __u8 conf_state;
++ __u8 conf_retry;
+ __u16 conf_mtu;
+
+ __u8 ident;
+@@ -238,8 +248,11 @@
+ struct sock *prev_c;
+ };
--#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
-+#ifndef container_of
-+#define container_of(ptr, type, member) ({ \
-+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
-+ (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
+-#define CONF_REQ_SENT 0x01
+-#define CONF_INPUT_DONE 0x02
+-#define CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_REQ_SENT 0x01
++#define L2CAP_CONF_INPUT_DONE 0x02
++#define L2CAP_CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_MAX_RETRIES 2
++
++void l2cap_load(void);
--#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)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-+#define kvec iovec
-+#define __user
- #endif
+ #endif /* __L2CAP_H */
+--- linux-2.4.21/include/net/bluetooth/rfcomm.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/rfcomm.h
+@@ -167,8 +167,8 @@
+ int initiator;
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
--#define rq_data_dir(x) ((x)->cmd)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
-+#define msleep(x) \
-+ set_current_state(TASK_UNINTERRUPTIBLE); \
-+ schedule_timeout((x)*(HZ/1000));
- #endif
+ /* Default DLC parameters */
++ int cfc;
+ uint mtu;
+- uint credits;
--#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))
--
-+#ifndef __iomem
-+#define __iomem
- #endif
+ struct list_head dlcs;
+ };
+@@ -185,11 +185,12 @@
+ atomic_t refcnt;
+ u8 dlci;
+ u8 addr;
+-
+- uint mtu;
++ u8 priority;
+ u8 v24_sig;
++ u8 mscex;
+
+- uint credits;
++ uint mtu;
++ uint cfc;
+ uint rx_credits;
+ uint tx_credits;
+
+@@ -213,6 +214,16 @@
+ #define RFCOMM_SCHED_TIMEO 3
+ #define RFCOMM_SCHED_WAKEUP 31
+
++/* MSC exchange flags */
++#define RFCOMM_MSCEX_TX 1
++#define RFCOMM_MSCEX_RX 2
++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
++
++/* CFC states */
++#define RFCOMM_CFC_UNKNOWN -1
++#define RFCOMM_CFC_DISABLED 0
++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
++
+ extern struct task_struct *rfcomm_thread;
+ extern unsigned long rfcomm_event;
--/* Removed cos it broke stuff. Where is this required anyway?
-- * #ifndef QUEUE_EMPTY
-- * #define QUEUE_EMPTY (!CURRENT)
-- * #endif
-+#ifndef list_for_each_entry_safe
-+/**
-+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
-+ * @pos: the type * to use as a loop counter.
-+ * @n: another type * to use as temporary storage
-+ * @head: the head for your list.
-+ * @member: the name of the list_struct within the struct.
+--- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
++++ linux-2.4.21/include/net/iw_handler.h
+@@ -1,7 +1,7 @@
+ /*
+ * This file define the new driver API for Wireless Extensions
+ *
+- * Version : 4 21.6.02
++ * Version : 5 4.12.02
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -206,7 +206,7 @@
+ * will be needed...
+ * I just plan to increment with each new version.
*/
--#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
-+#define list_for_each_entry_safe(pos, n, head, member) \
-+ for (pos = list_entry((head)->next, typeof(*pos), member), \
-+ n = list_entry(pos->member.next, typeof(*pos), member); \
-+ &pos->member != (head); \
-+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
--#define PageUptodate(x) Page_Uptodate(x)
- #endif
-
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
--#define get_seconds() CURRENT_TIME
-+#ifndef DEFINE_SPINLOCK
-+#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
- #endif
--
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
--#define generic_file_readonly_mmap generic_file_mmap
-+#ifndef DEFINE_RWLOCK
-+#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
- #endif
+-#define IW_HANDLER_VERSION 4
++#define IW_HANDLER_VERSION 5
- #endif /* __LINUX_MTD_COMPATMAC_H__ */
---- linux-2.4.21/include/linux/mtd/doc2000.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/doc2000.h
-@@ -1,13 +1,21 @@
--
--/* Linux driver for Disk-On-Chip 2000 */
--/* (c) 1999 Machine Vision Holdings, Inc. */
--/* Author: David Woodhouse <dwmw2@mvhi.com> */
--/* $Id$ */
-+/*
-+ * Linux driver for Disk-On-Chip devices
-+ *
-+ * Copyright (C) 1999 Machine Vision Holdings, Inc.
-+ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
-+ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
-+ * Copyright (C) 2002-2003 SnapGear Inc
-+ *
-+ * $Id$
+ /*
+ * Changes :
+@@ -220,10 +220,18 @@
+ * V3 to V4
+ * --------
+ * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
+ *
-+ * Released under GPL
-+ */
-
- #ifndef __MTD_DOC2000_H__
- #define __MTD_DOC2000_H__
-
- #include <linux/mtd/mtd.h>
-+#include <asm/semaphore.h>
-
- #define DoC_Sig1 0
- #define DoC_Sig2 1
-@@ -38,22 +46,51 @@
- #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
++ * V4 to V5
++ * --------
++ * - Add new spy support : struct iw_spy_data & prototypes
*/
- #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))
--#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + (reg))
-+#define ReadDOC_(adr, reg) readb((void __iomem *)(adr) + (reg))
-+#define WriteDOC_(d, adr, reg) writeb(d, (void __iomem *)(adr) + (reg))
- #define DOC_IOREMAP_LEN 0x2000
-
- #endif
-@@ -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
+ /**************************** CONSTANTS ****************************/
+
++/* Enable enhanced spy support. Disable to reduce footprint */
++#define IW_WIRELESS_SPY
++#define IW_WIRELESS_THRSPY
+
- #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
+ /* Special error message for the driver to indicate that we
+ * should do a commit after return from the iw_handler */
+ #define EIWCOMMIT EINPROGRESS
+@@ -315,6 +323,9 @@
+ * We will automatically export that to user space... */
+ struct iw_priv_args * private_args;
-+#define DOC_FLASH_CE 0x80
-+#define DOC_FLASH_WP 0x40
-+#define DOC_FLASH_BANK 0x02
++ /* Driver enhanced spy support */
++ long spy_offset; /* Spy data offset */
+
- /* 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,18 +156,21 @@
- #define MAX_FLOORS 4
- #define MAX_CHIPS 4
+ /* In the long term, get_wireless_stats will move from
+ * 'struct net_device' to here, to minimise bloat. */
+ };
+@@ -350,6 +361,33 @@
--#define MAX_FLOORS_MIL 4
-+#define MAX_FLOORS_MIL 1
- #define MAX_CHIPS_MIL 1
+ /* Need to think of short header translation table. Later. */
-+#define MAX_FLOORS_MPLUS 2
-+#define MAX_CHIPS_MPLUS 1
++/* --------------------- ENHANCED SPY SUPPORT --------------------- */
++/*
++ * In the old days, the driver was handling spy support all by itself.
++ * Now, the driver can delegate this task to Wireless Extensions.
++ * It needs to include this struct in its private part and use the
++ * standard spy iw_handler.
++ */
+
- #define ADDR_COLUMN 1
- #define ADDR_PAGE 2
- #define ADDR_COLUMN_PAGE 3
++/*
++ * Instance specific spy data, i.e. addresses spied and quality for them.
++ */
++struct iw_spy_data
++{
++#ifdef IW_WIRELESS_SPY
++ /* --- Standard spy support --- */
++ int spy_number;
++ u_char spy_address[IW_MAX_SPY][ETH_ALEN];
++ struct iw_quality spy_stat[IW_MAX_SPY];
++#ifdef IW_WIRELESS_THRSPY
++ /* --- Enhanced spy support (event) */
++ struct iw_quality spy_thr_low; /* Low threshold */
++ struct iw_quality spy_thr_high; /* High threshold */
++ u_char spy_thr_under[IW_MAX_SPY];
++#endif /* IW_WIRELESS_THRSPY */
++#endif /* IW_WIRELESS_SPY */
++};
++
+ /**************************** PROTOTYPES ****************************/
+ /*
+ * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
+@@ -376,6 +414,31 @@
+ /* We may need a function to send a stream of events to user space.
+ * More on that later... */
- struct DiskOnChip {
- unsigned long physadr;
-- unsigned long virtadr;
-+ void __iomem *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;
---- linux-2.4.21/include/linux/mtd/flashchip.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/flashchip.h
-@@ -6,7 +6,7 @@
- *
- * (C) 2000 Red Hat. GPLd.
- *
-- * $Id$
-+ * $Id$
- *
- */
++/* Standard handler for SIOCSIWSPY */
++extern int iw_handler_set_spy(struct net_device * dev,
++ struct iw_request_info * info,
++ union iwreq_data * wrqu,
++ char * extra);
++/* Standard handler for SIOCGIWSPY */
++extern int iw_handler_get_spy(struct net_device * dev,
++ struct iw_request_info * info,
++ union iwreq_data * wrqu,
++ char * extra);
++/* Standard handler for SIOCSIWTHRSPY */
++extern int iw_handler_set_thrspy(struct net_device * dev,
++ struct iw_request_info *info,
++ union iwreq_data * wrqu,
++ char * extra);
++/* Standard handler for SIOCGIWTHRSPY */
++extern int iw_handler_get_thrspy(struct net_device * dev,
++ struct iw_request_info *info,
++ union iwreq_data * wrqu,
++ char * extra);
++/* Driver call to update spy records */
++extern void wireless_spy_update(struct net_device * dev,
++ unsigned char * address,
++ struct iw_quality * wstats);
++
+ /************************* INLINE FUNTIONS *************************/
+ /*
+ * Function that are so simple that it's more efficient inlining them
+--- linux-2.4.21/init/do_mounts.c~small-nocramdisk
++++ linux-2.4.21/init/do_mounts.c
+@@ -16,8 +16,6 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
-@@ -29,6 +29,7 @@
- FL_ERASE_SUSPENDED,
- FL_WRITING,
- FL_WRITING_TO_BUFFER,
-+ FL_OTP_WRITE,
- FL_WRITE_SUSPENDING,
- FL_WRITE_SUSPENDED,
- FL_PM_SUSPENDED,
-@@ -37,13 +38,16 @@
- FL_LOCKING,
- FL_UNLOCKING,
- FL_POINT,
-+ FL_XIP_WHILE_ERASING,
-+ FL_XIP_WHILE_WRITING,
- FL_UNKNOWN
- } flstate_t;
+-#define BUILD_CRAMDISK
+-
+ extern int get_filesystem_list(char * buf);
+ extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
+--- linux-2.4.21/kernel/ksyms.c~bluetooth
++++ linux-2.4.21/kernel/ksyms.c
+@@ -48,6 +48,7 @@
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
+ #include <linux/dnotify.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+
+ #if defined(CONFIG_PROC_FS)
+@@ -564,6 +565,13 @@
+ EXPORT_SYMBOL(strspn);
+ EXPORT_SYMBOL(strsep);
+
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
+ /* software interrupts */
+ EXPORT_SYMBOL(tasklet_hi_vec);
+ EXPORT_SYMBOL(tasklet_vec);
+@@ -585,6 +593,11 @@
+ EXPORT_SYMBOL(tasklist_lock);
+ EXPORT_SYMBOL(pidhash);
++#ifdef CONFIG_ARCH_RAMSES
++#include <asm/arch/ramses.h>
++EXPORT_SYMBOL(ramses_control_shadow);
++EXPORT_SYMBOL(ramses_flags);
++#endif
- /* NOTE: confusingly, this can be used to refer to more than one chip at a time,
-- if they're interleaved. */
-+ if they're interleaved. This can even refer to individual partitions on
-+ the same physical chip when present. */
+ /* debug */
+ EXPORT_SYMBOL(dump_stack);
+--- linux-2.4.21/kernel/pm.c~pm
++++ linux-2.4.21/kernel/pm.c
+@@ -234,7 +234,7 @@
+ struct list_head *entry;
+
+ down(&pm_devs_lock);
+- entry = pm_devs.next;
++ entry = (rqst==PM_RESUME) ? pm_devs.prev : pm_devs.next;
+ while (entry != &pm_devs) {
+ struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
+ if (dev->callback) {
+@@ -249,7 +249,7 @@
+ return status;
+ }
+ }
+- entry = entry->next;
++ entry = (rqst==PM_RESUME) ? entry->prev : entry->next;
+ }
+ up(&pm_devs_lock);
+ return 0;
+--- linux-2.4.21/lib/Config.in~mtd-cvs
++++ linux-2.4.21/lib/Config.in
+@@ -35,4 +35,25 @@
+ fi
+ fi
- struct flchip {
- unsigned long start; /* Offset within the map */
-@@ -58,6 +62,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;
++if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
++ "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
++ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
++ define_tristate CONFIG_REED_SOLOMON y
++ define_tristate CONFIG_REED_SOLOMON_DEC16 y
++else
++ if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
++ "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
++ "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
++ define_tristate CONFIG_REED_SOLOMON m
++ define_tristate CONFIG_REED_SOLOMON_DEC16 y
++ else
++ define_tristate CONFIG_REED_SOLOMON n
++ fi
++fi
+
- 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
-@@ -65,8 +74,17 @@
- int word_write_time;
- int buffer_write_time;
- int erase_time;
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++ "$CONFIG_HOTPLUG" = "y" ]; then
++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
+
-+ void *priv;
- };
+ endmenu
+--- linux-2.4.21/lib/Makefile~mtd-cvs
++++ linux-2.4.21/lib/Makefile
+@@ -8,11 +8,13 @@
-+/* This is used to handle contention on write/erase operations
-+ between partitions of the same physical chip. */
-+struct flchip_shared {
-+ spinlock_t lock;
-+ struct flchip *writing;
-+ struct flchip *erasing;
-+};
+ L_TARGET := lib.a
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o rbtree.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++ rbtree.o firmware_class.o
- #endif /* __MTD_FLASHCHIP_H__ */
---- linux-2.4.21/include/linux/mtd/gen_probe.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/gen_probe.h
-@@ -1,7 +1,7 @@
- /*
- * (C) 2001, 2001 Red Hat, Inc.
- * GPL'd
-- * $Id$
-+ * $Id$
- */
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
+ bust_spinlocks.o rbtree.o dump_stack.o
- #ifndef __LINUX_MTD_GEN_PROBE_H__
-@@ -10,12 +10,12 @@
- #include <linux/mtd/flashchip.h>
- #include <linux/mtd/map.h>
- #include <linux/mtd/cfi.h>
-+#include <linux/bitops.h>
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
- 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);
- };
+@@ -22,6 +24,9 @@
- struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+ subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
+ subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
++subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
++
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
+
+ # Include the subdirs, if necessary.
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
--- /dev/null
-+++ linux-2.4.21/include/linux/mtd/inftl.h
-@@ -0,0 +1,57 @@
++++ linux-2.4.21/lib/firmware_class.c
+@@ -0,0 +1,573 @@
+/*
-+ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ * firmware_class.c - Multi purpose firmware loading support
+ *
-+ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ *
-+ * $Id$
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
+ */
++/*
++ kernel/kmod.c
++ Kirk Petersen
+
-+#ifndef __MTD_INFTL_H__
-+#define __MTD_INFTL_H__
++ Reorganized not to be a daemon by Adam Richter, with guidance
++ from Greg Zornetzer.
+
-+#ifndef __KERNEL__
-+#error This is a kernel header. Perhaps include nftl-user.h instead?
-+#endif
++ Modified to avoid chroot and file sharing problems.
++ Mikael Pettersson
+
-+#include <linux/mtd/blktrans.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/nftl.h>
++ Limit the concurrent number of kmod modprobes to catch loops from
++ "modprobe needs a service that is in a module".
++ Keith Owens <kaos@ocs.com.au> December 1999
+
-+#include <mtd/inftl-user.h>
++ Unblock all signals when we exec a usermode process.
++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
++*/
++/*
++ * drivers/usb/usb.c
++ *
++ * (C) Copyright Linus Torvalds 1999
++ * (C) Copyright Johannes Erdfelt 1999-2001
++ * (C) Copyright Andreas Gal 1999
++ * (C) Copyright Gregory P. Smith 1999
++ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
++ * (C) Copyright Randy Dunlap 2000
++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
++ * (C) Copyright Yggdrasil Computing, Inc. 2000
++ * (usb_device_id matching changes by Adam J. Richter)
++ */
+
-+#ifndef INFTL_MAJOR
-+#define INFTL_MAJOR 94
-+#endif
-+#define INFTL_PARTN_BITS 4
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++#include <asm/hardirq.h>
+
-+#ifdef __KERNEL__
++#include "linux/firmware.h"
+
-+struct INFTLrecord {
-+ struct mtd_blktrans_dev mbd;
-+ __u16 MediaUnit;
-+ __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;
-+ struct nand_oobinfo oobinfo;
-+};
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
+
-+int INFTL_mount(struct INFTLrecord *s);
-+int INFTL_formatblock(struct INFTLrecord *s, int block);
++#define err(format, arg...) \
++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
+
-+#endif /* __KERNEL__ */
++static int loading_timeout = 10; /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
+
-+#endif /* __MTD_INFTL_H__ */
---- linux-2.4.21/include/linux/mtd/jedec.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/jedec.h
-@@ -7,14 +7,13 @@
- *
- * See the AMD flash databook for information on how to operate the interface.
- *
-- * $Id$
-+ * $Id$
- */
-
- #ifndef __LINUX_MTD_JEDEC_H__
- #define __LINUX_MTD_JEDEC_H__
-
- #include <linux/types.h>
--#include <linux/mtd/map.h>
-
- #define MAX_JEDEC_CHIPS 16
-
---- linux-2.4.21/include/linux/mtd/map.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/map.h
-@@ -1,23 +1,176 @@
-
- /* Overhauled routines for dealing with different mmap regions of flash */
--/* $Id$ */
-+/* $Id$ */
-
- #ifndef __LINUX_MTD_MAP_H__
- #define __LINUX_MTD_MAP_H__
-
- #include <linux/config.h>
- #include <linux/types.h>
--#include <linux/mtd/mtd.h>
--#include <linux/slab.h>
-+#include <linux/list.h>
-+#include <linux/mtd/compatmac.h>
-+#include <asm/unaligned.h>
-+#include <asm/system.h>
-+#include <asm/io.h>
-+#include <asm/bug.h>
++#ifdef CONFIG_HOTPLUG
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
-+#define map_bankwidth(map) 1
-+#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
-+#define map_bankwidth_is_large(map) (0)
-+#define map_words(map) (1)
-+#define MAX_MAP_BANKWIDTH 1
-+#else
-+#define map_bankwidth_is_1(map) (0)
-+#endif
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++ char *argv[3], **envp, *buf, *scratch;
++ int i = 0;
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
-+# ifdef map_bankwidth
-+# undef map_bankwidth
-+# define map_bankwidth(map) ((map)->bankwidth)
-+# else
-+# define map_bankwidth(map) 2
-+# define map_bankwidth_is_large(map) (0)
-+# define map_words(map) (1)
-+# endif
-+#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 2
-+#else
-+#define map_bankwidth_is_2(map) (0)
-+#endif
++ int retval = 0;
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
-+# ifdef map_bankwidth
-+# undef map_bankwidth
-+# define map_bankwidth(map) ((map)->bankwidth)
-+# else
-+# define map_bankwidth(map) 4
-+# define map_bankwidth_is_large(map) (0)
-+# define map_words(map) (1)
-+# endif
-+#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 4
-+#else
-+#define map_bankwidth_is_4(map) (0)
-+#endif
++ if (!hotplug_path[0])
++ return -ENOENT;
++ if (in_interrupt()) {
++ err("in_interrupt");
++ return -EFAULT;
++ }
++ if (!current->fs->root) {
++ warn("call_policy %s -- no FS yet", verb);
++ return -EPERM;
++ }
+
-+/* ensure we never evaluate anything shorted than an unsigned long
-+ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
++ err("unable to allocate envp");
++ return -ENOMEM;
++ }
++ if (!(buf = kmalloc(256, GFP_KERNEL))) {
++ kfree(envp);
++ err("unable to allocate buf");
++ return -ENOMEM;
++ }
+
-+#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
++ /* only one standardized param to hotplug command: type */
++ argv[0] = hotplug_path;
++ argv[1] = "firmware";
++ argv[2] = 0;
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
-+# ifdef map_bankwidth
-+# undef map_bankwidth
-+# define map_bankwidth(map) ((map)->bankwidth)
-+# if BITS_PER_LONG < 64
-+# undef map_bankwidth_is_large
-+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+# undef map_words
-+# define map_words(map) map_calc_words(map)
-+# endif
-+# else
-+# define map_bankwidth(map) 8
-+# define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
-+# define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 8
-+#else
-+#define map_bankwidth_is_8(map) (0)
++ /* minimal command environment */
++ envp[i++] = "HOME=/";
++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++#ifdef DEBUG
++ /* hint that policy agent should enter no-stdout debug mode */
++ envp[i++] = "DEBUG=kernel";
+#endif
++ scratch = buf;
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
-+# ifdef map_bankwidth
-+# undef map_bankwidth
-+# define map_bankwidth(map) ((map)->bankwidth)
-+# undef map_bankwidth_is_large
-+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+# undef map_words
-+# define map_words(map) map_calc_words(map)
-+# else
-+# define map_bankwidth(map) 16
-+# define map_bankwidth_is_large(map) (1)
-+# define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 16
-+#else
-+#define map_bankwidth_is_16(map) (0)
++ if (device) {
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
++ "DEVPATH=/driver/firmware/%s", device) + 1;
++ }
++
++ envp[i++] = scratch;
++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
++
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
++ "FIRMWARE=%s", name) + 1;
++
++ envp[i++] = 0;
++
++#ifdef DEBUG
++ dbg("firmware: %s %s %s", argv[0], argv[1], verb);
+#endif
+
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-+# ifdef map_bankwidth
-+# undef map_bankwidth
-+# define map_bankwidth(map) ((map)->bankwidth)
-+# undef map_bankwidth_is_large
-+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+# undef map_words
-+# define map_words(map) map_calc_words(map)
-+# else
-+# define map_bankwidth(map) 32
-+# define map_bankwidth_is_large(map) (1)
-+# define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 32
++ retval = call_usermodehelper(argv[0], argv, envp);
++ if (retval) {
++ printk("call_usermodehelper return %d\n", retval);
++ }
++
++ kfree(buf);
++ kfree(envp);
++ return retval;
++}
+#else
-+#define map_bankwidth_is_32(map) (0)
-+#endif
+
-+#ifndef map_bankwidth
-+#error "No bus width supported. What's the point?"
-+#endif
++static inline int
++call_helper(char *verb, const char *name, const char *device)
++{
++ return -ENOENT;
++}
++
++#endif /* CONFIG_HOTPLUG */
++
++struct firmware_priv {
++ struct completion completion;
++ struct proc_dir_entry *proc_dir;
++ struct proc_dir_entry *attr_data;
++ struct proc_dir_entry *attr_loading;
++ struct firmware *fw;
++ int loading;
++ int abort;
++ int alloc_size;
++ struct timer_list timeout;
++};
++
++static int
++firmware_timeout_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ return sprintf(buf, "%d\n", loading_timeout);
++}
++
++/**
++ * firmware_timeout_store:
++ * Description:
++ * Sets the number of seconds to wait for the firmware. Once
++ * this expires an error will be return to the driver and no
++ * firmware will be provided.
++ *
++ * Note: zero means 'wait for ever'
++ *
++ **/
++static int
++firmware_timeout_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ loading_timeout = simple_strtol(buf, NULL, 10);
++ return count;
++}
++
++static int
++firmware_loading_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ return sprintf(buf, "%d\n", fw_priv->loading);
++}
+
-+static inline int map_bankwidth_supported(int w)
++/**
++ * firmware_loading_store: - loading control file
++ * Description:
++ * The relevant values are:
++ *
++ * 1: Start a load, discarding any previous partial load.
++ * 0: Conclude the load and handle the data to the driver code.
++ * -1: Conclude the load with an error and discard any written data.
++ **/
++static int
++firmware_loading_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
+{
-+ switch (w) {
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++ struct firmware_priv *fw_priv = data;
++ int prev_loading = fw_priv->loading;
++
++ fw_priv->loading = simple_strtol(buf, NULL, 10);
++
++ switch (fw_priv->loading) {
++ case -1:
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++ break;
+ case 1:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
-+ case 2:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
-+ case 4:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
-+ case 8:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
-+ case 16:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-+ case 32:
-+#endif
-+ return 1;
++ kfree(fw_priv->fw->data);
++ fw_priv->fw->data = NULL;
++ fw_priv->fw->size = 0;
++ fw_priv->alloc_size = 0;
++ break;
++ case 0:
++ if (prev_loading == 1)
++ complete(&fw_priv->completion);
++ break;
++ }
+
-+ default:
++ return count;
++}
++
++static int
++firmware_data_read(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++
++ if (offset > fw->size)
+ return 0;
++ if (offset + count > fw->size)
++ count = fw->size - offset;
++
++ memcpy(buffer, fw->data + offset, count);
++ *start = (void *) ((long) count);
++ return count;
++}
++static int
++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
++{
++ u8 *new_data;
++ int new_size;
++
++ if (min_size <= fw_priv->alloc_size)
++ return 0;
++ if((min_size % PAGE_SIZE) == 0)
++ new_size = min_size;
++ else
++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
++ new_data = vmalloc(new_size);
++ if (!new_data) {
++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
++ /* Make sure that we don't keep incomplete data */
++ fw_priv->abort = 1;
++ return -ENOMEM;
++ }
++ fw_priv->alloc_size = new_size;
++ if (fw_priv->fw->data) {
++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
++ vfree(fw_priv->fw->data);
+ }
++ fw_priv->fw->data = new_data;
++ BUG_ON(min_size > fw_priv->alloc_size);
++ return 0;
+}
+
-+#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
++/**
++ * firmware_data_write:
++ *
++ * Description:
++ *
++ * Data written to the 'data' attribute will be later handled to
++ * the driver as a firmware image.
++ **/
++static int
++firmware_data_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++ int offset = file->f_pos;
++ int retval;
+
-+typedef union {
-+ unsigned long x[MAX_MAP_LONGS];
-+} map_word;
-
- /* 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
- paging etc. correctly if your device needs it. Then you pass it off
-- to a chip driver which deals with a mapped device - generally either
-- do_cfi_probe() or do_ram_probe(), either of which will return a
-- struct mtd_info if they liked what they saw. At which point, you
-- fill in the mtd->module with your own module address, and register
-- it.
-+ to a chip probe routine -- either JEDEC or CFI probe or both -- via
-+ do_map_probe(). If a chip is recognised, the probe code will invoke the
-+ appropriate chip driver (if present) and return a struct mtd_info.
-+ At which point, you fill in the mtd->module with your own module
-+ address, and register it with the MTD core code. Or you could partition
-+ it and register the partitions instead, or keep it for your own private
-+ use; whatever.
-
- The mtd->priv field will point to the struct map_info, and any further
- private data required by the chip driver is linked from the
-@@ -29,39 +182,45 @@
- struct map_info {
- char *name;
- unsigned long size;
-- 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);
-- /* If it returned a 'long' I'd call it readl.
-- * It doesn't.
-- * I won't.
-- * dwmw2 */
-+ unsigned long phys;
-+#define NO_XIP (-1UL)
-
-+ void __iomem *virt;
-+ void *cached;
++ retval = fw_realloc_buffer(fw_priv, offset + count);
++ if (retval) {
++ printk("%s: retval:%d\n", __FUNCTION__, retval);
++ return retval;
++ }
+
-+ int bankwidth; /* in octets. This isn't necessarily the width
-+ of actual bus cycles -- it's the repeat interval
-+ in bytes, before you are talking to the first chip again.
-+ */
++ memcpy(fw->data + offset, buffer, count);
+
-+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-+ map_word (*read)(struct map_info *, unsigned long);
- 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);
++ fw->size = max_t(size_t, offset + count, fw->size);
++ file->f_pos += count;
++ return count;
++}
+
-+ void (*write)(struct map_info *, const map_word, 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
-+ /* It's possible for the map driver to use cached memory in its
-+ copy_from implementation (and _only_ with copy_from). However,
-+ when the chip driver knows some flash area has changed contents,
-+ it will signal it to the map driver through this routine to let
-+ the map driver invalidate the corresponding cache as needed.
-+ If there is no cache to care about this can be set to NULL. */
-+ void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
-
-+ /* 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 */
++static void
++firmware_class_timeout(u_long data)
++{
++ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++}
++static int
++fw_setup_class_device(struct firmware_priv **fw_priv_p,
++ const char *fw_name, const char *device)
++{
++ int retval;
++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
++ GFP_KERNEL);
++ *fw_priv_p = fw_priv;
++ if (!fw_priv) {
++ retval = -ENOMEM;
++ goto out;
++ }
++ memset(fw_priv, 0, sizeof (*fw_priv));
+
- 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 +233,193 @@
- 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);
++ init_completion(&fw_priv->completion);
+
-+#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)
-
-+#define INVALIDATE_CACHED_RANGE(map, from, size) \
-+ do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
-
--/*
-- * 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)
++ fw_priv->timeout.function = firmware_class_timeout;
++ fw_priv->timeout.data = (u_long) fw_priv;
++ init_timer(&fw_priv->timeout);
+
-+static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
- {
-- struct map_info *map = mtd->priv;
-+ int i;
-+ for (i=0; i<map_words(map); i++) {
-+ if (val1.x[i] != val2.x[i])
-+ return 0;
++ retval = -EAGAIN;
++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
++ if (!fw_priv->proc_dir)
++ goto err_free_fw_priv;
++
++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_data)
++ goto err_remove_dir;
++
++ fw_priv->attr_data->read_proc = firmware_data_read;
++ fw_priv->attr_data->write_proc = firmware_data_write;
++ fw_priv->attr_data->data = fw_priv;
++
++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_loading)
++ goto err_remove_data;
++
++ fw_priv->attr_loading->read_proc = firmware_loading_show;
++ fw_priv->attr_loading->write_proc = firmware_loading_store;
++ fw_priv->attr_loading->data = fw_priv;
++
++ retval = 0;
++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
++ if (!fw_priv->fw) {
++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
++ __FUNCTION__);
++ retval = -ENOMEM;
++ goto err_remove_loading;
+ }
-+ return 1;
++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
++
++ goto out;
++
++err_remove_loading:
++ remove_proc_entry("loading", fw_priv->proc_dir);
++err_remove_data:
++ remove_proc_entry("data", fw_priv->proc_dir);
++err_remove_dir:
++ remove_proc_entry(device, proc_dir);
++err_free_fw_priv:
++ kfree(fw_priv);
++out:
++ return retval;
+}
-
-- if (map->fldrv->destroy)
-- map->fldrv->destroy(mtd);
--#ifdef CONFIG_MODULES
-- if (map->fldrv->module)
-- __MOD_DEC_USE_COUNT(map->fldrv->module);
-+static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
+{
-+ map_word r;
-+ int i;
++ remove_proc_entry("loading", fw_priv->proc_dir);
++ remove_proc_entry("data", fw_priv->proc_dir);
++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
++}
+
-+ for (i=0; i<map_words(map); i++) {
-+ r.x[i] = val1.x[i] & val2.x[i];
++/**
++ * request_firmware: - request firmware to hotplug and wait for it
++ * Description:
++ * @firmware will be used to return a firmware image by the name
++ * of @name for device @device.
++ *
++ * Should be called from user context where sleeping is allowed.
++ *
++ * @name will be use as $FIRMWARE in the hotplug environment and
++ * should be distinctive enough not to be confused with any other
++ * firmware image for this or any other device.
++ **/
++int
++request_firmware(const struct firmware **firmware, const char *name,
++ const char *device)
++{
++ struct firmware_priv *fw_priv;
++ int retval;
++
++ if (!firmware) {
++ retval = -EINVAL;
++ goto out;
+ }
-+ return r;
++ *firmware = NULL;
++
++ retval = fw_setup_class_device(&fw_priv, name, device);
++ if (retval)
++ goto out;
++
++ retval = call_helper("add", name, device);
++ if (retval)
++ goto out;
++ if (loading_timeout) {
++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
++ add_timer(&fw_priv->timeout);
++ }
++
++ wait_for_completion(&fw_priv->completion);
++
++ del_timer(&fw_priv->timeout);
++ fw_remove_class_device(fw_priv);
++
++ if (fw_priv->fw->size && !fw_priv->abort) {
++ *firmware = fw_priv->fw;
++ } else {
++ retval = -ENOENT;
++ vfree(fw_priv->fw->data);
++ kfree(fw_priv->fw);
++ }
++out:
++ kfree(fw_priv);
++ return retval;
+}
+
-+static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
++void
++release_firmware(const struct firmware *fw)
+{
-+ map_word r;
-+ int i;
-+
-+ for (i=0; i<map_words(map); i++) {
-+ r.x[i] = val1.x[i] & ~val2.x[i];
++ if (fw) {
++ vfree(fw->data);
++ kfree(fw);
+ }
-+ return r;
+}
+
-+static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
++/**
++ * register_firmware: - provide a firmware image for later usage
++ *
++ * Description:
++ * Make sure that @data will be available by requesting firmware @name.
++ *
++ * Note: This will not be possible until some kind of persistence
++ * is available.
++ **/
++void
++register_firmware(const char *name, const u8 *data, size_t size)
+{
-+ map_word r;
-+ int i;
++ /* This is meaningless without firmware caching, so until we
++ * decide if firmware caching is reasonable just leave it as a
++ * noop */
++}
+
-+ for (i=0; i<map_words(map); i++) {
-+ r.x[i] = val1.x[i] | val2.x[i];
-+ }
-+ return r;
++/* Async support */
++struct firmware_work {
++ struct tq_struct work;
++ struct module *module;
++ const char *name;
++ const char *device;
++ void *context;
++ void (*cont)(const struct firmware *fw, void *context);
++};
++
++static void
++request_firmware_work_func(void *arg)
++{
++ struct firmware_work *fw_work = arg;
++ const struct firmware *fw;
++ if (!arg)
++ return;
++ request_firmware(&fw, fw_work->name, fw_work->device);
++ fw_work->cont(fw, fw_work->context);
++ release_firmware(fw);
++ __MOD_DEC_USE_COUNT(fw_work->module);
++ kfree(fw_work);
+}
+
-+#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
++/**
++ * request_firmware_nowait:
++ *
++ * Description:
++ * Asynchronous variant of request_firmware() for contexts where
++ * it is not possible to sleep.
++ *
++ * @cont will be called asynchronously when the firmware request is over.
++ *
++ * @context will be passed over to @cont.
++ *
++ * @fw may be %NULL if firmware request fails.
++ *
++ **/
++int
++request_firmware_nowait(
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context))
++{
++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
++ GFP_ATOMIC);
++ if (!fw_work)
++ return -ENOMEM;
++ if (!try_inc_mod_count(module)) {
++ kfree(fw_work);
++ return -EFAULT;
++ }
+
-+static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
++ *fw_work = (struct firmware_work) {
++ .module = module,
++ .name = name,
++ .device = device,
++ .context = context,
++ .cont = cont,
++ };
++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
++
++ schedule_task(&fw_work->work);
++ return 0;
++}
++
++static int __init
++firmware_class_init(void)
+{
-+ int i;
++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
++ if (!proc_dir)
++ return -EAGAIN;
++ proc_dir_timeout = create_proc_entry("timeout",
++ 0644 | S_IFREG, proc_dir);
++ if (!proc_dir_timeout) {
++ remove_proc_entry("driver/firmware", NULL);
++ return -EAGAIN;
++ }
++ proc_dir_timeout->read_proc = firmware_timeout_show;
++ proc_dir_timeout->write_proc = firmware_timeout_store;
++ return 0;
++}
++static void __exit
++firmware_class_exit(void)
++{
++ remove_proc_entry("timeout", proc_dir);
++ remove_proc_entry("driver/firmware", NULL);
++}
+
-+ for (i=0; i<map_words(map); i++) {
-+ if (val1.x[i] & val2.x[i])
-+ return 1;
++module_init(firmware_class_init);
++module_exit(firmware_class_exit);
++
++#ifndef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/Makefile
+@@ -0,0 +1,12 @@
++#
++# This is a modified version of reed solomon lib,
++#
++
++O_TARGET:= reed_solomon.o
++
++export-objs := rslib.o
++
++obj-y := rslib.o
++obj-m := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/decode_rs.c
+@@ -0,0 +1,272 @@
++/*
++ * lib/reed_solomon/decode_rs.c
++ *
++ * Overview:
++ * Generic Reed Solomon encoder / decoder library
++ *
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
++
++/* Generic data width independent code which is included by the
++ * wrappers.
++ */
++{
++ int deg_lambda, el, deg_omega;
++ int i, j, r, k, pad;
++ int nn = rs->nn;
++ int nroots = rs->nroots;
++ int fcr = rs->fcr;
++ int prim = rs->prim;
++ int iprim = rs->iprim;
++ uint16_t *alpha_to = rs->alpha_to;
++ uint16_t *index_of = rs->index_of;
++ uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
++ /* Err+Eras Locator poly and syndrome poly The maximum value
++ * of nroots is 8. So the necessary stack size will be about
++ * 220 bytes max.
++ */
++ uint16_t lambda[nroots + 1], syn[nroots];
++ uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
++ uint16_t root[nroots], reg[nroots + 1], loc[nroots];
++ int count = 0;
++ uint16_t msk = (uint16_t) rs->nn;
++
++ /* Check length parameter for validity */
++ pad = nn - nroots - len;
++ if (pad < 0 || pad >= nn)
++ return -ERANGE;
++
++ /* Does the caller provide the syndrome ? */
++ if (s != NULL)
++ goto decode;
++
++ /* form the syndromes; i.e., evaluate data(x) at roots of
++ * g(x) */
++ for (i = 0; i < nroots; i++)
++ syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
++
++ for (j = 1; j < len; j++) {
++ for (i = 0; i < nroots; i++) {
++ if (syn[i] == 0) {
++ syn[i] = (((uint16_t) data[j]) ^
++ invmsk) & msk;
++ } else {
++ syn[i] = ((((uint16_t) data[j]) ^
++ invmsk) & msk) ^
++ alpha_to[rs_modnn(rs, index_of[syn[i]] +
++ (fcr + i) * prim)];
++ }
++ }
++ }
++
++ for (j = 0; j < nroots; j++) {
++ for (i = 0; i < nroots; i++) {
++ if (syn[i] == 0) {
++ syn[i] = ((uint16_t) par[j]) & msk;
++ } else {
++ syn[i] = (((uint16_t) par[j]) & msk) ^
++ alpha_to[rs_modnn(rs, index_of[syn[i]] +
++ (fcr+i)*prim)];
++ }
++ }
++ }
++ s = syn;
++
++ /* Convert syndromes to index form, checking for nonzero condition */
++ syn_error = 0;
++ for (i = 0; i < nroots; i++) {
++ syn_error |= s[i];
++ s[i] = index_of[s[i]];
++ }
++
++ if (!syn_error) {
++ /* if syndrome is zero, data[] is a codeword and there are no
++ * errors to correct. So return data[] unmodified
++ */
++ count = 0;
++ goto finish;
++ }
++
++ decode:
++ memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
++ lambda[0] = 1;
++
++ if (no_eras > 0) {
++ /* Init lambda to be the erasure locator polynomial */
++ lambda[1] = alpha_to[rs_modnn(rs,
++ prim * (nn - 1 - eras_pos[0]))];
++ for (i = 1; i < no_eras; i++) {
++ u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
++ for (j = i + 1; j > 0; j--) {
++ tmp = index_of[lambda[j - 1]];
++ if (tmp != nn) {
++ lambda[j] ^=
++ alpha_to[rs_modnn(rs, u + tmp)];
++ }
++ }
++ }
++ }
++
++ for (i = 0; i < nroots + 1; i++)
++ b[i] = index_of[lambda[i]];
++
++ /*
++ * Begin Berlekamp-Massey algorithm to determine error+erasure
++ * locator polynomial
++ */
++ r = no_eras;
++ el = no_eras;
++ while (++r <= nroots) { /* r is the step number */
++ /* Compute discrepancy at the r-th step in poly-form */
++ discr_r = 0;
++ for (i = 0; i < r; i++) {
++ if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
++ discr_r ^=
++ alpha_to[rs_modnn(rs,
++ index_of[lambda[i]] +
++ s[r - i - 1])];
++ }
++ }
++ discr_r = index_of[discr_r]; /* Index form */
++ if (discr_r == nn) {
++ /* 2 lines below: B(x) <-- x*B(x) */
++ memmove (&b[1], b, nroots * sizeof (b[0]));
++ b[0] = nn;
++ } else {
++ /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
++ t[0] = lambda[0];
++ for (i = 0; i < nroots; i++) {
++ if (b[i] != nn) {
++ t[i + 1] = lambda[i + 1] ^
++ alpha_to[rs_modnn(rs, discr_r +
++ b[i])];
++ } else
++ t[i + 1] = lambda[i + 1];
++ }
++ if (2 * el <= r + no_eras - 1) {
++ el = r + no_eras - el;
++ /*
++ * 2 lines below: B(x) <-- inv(discr_r) *
++ * lambda(x)
++ */
++ for (i = 0; i <= nroots; i++) {
++ b[i] = (lambda[i] == 0) ? nn :
++ rs_modnn(rs, index_of[lambda[i]]
++ - discr_r + nn);
++ }
++ } else {
++ /* 2 lines below: B(x) <-- x*B(x) */
++ memmove(&b[1], b, nroots * sizeof(b[0]));
++ b[0] = nn;
++ }
++ memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
++ }
++ }
++
++ /* Convert lambda to index form and compute deg(lambda(x)) */
++ deg_lambda = 0;
++ for (i = 0; i < nroots + 1; i++) {
++ lambda[i] = index_of[lambda[i]];
++ if (lambda[i] != nn)
++ deg_lambda = i;
++ }
++ /* Find roots of error+erasure locator polynomial by Chien search */
++ memcpy(®[1], &lambda[1], nroots * sizeof(reg[0]));
++ count = 0; /* Number of roots of lambda(x) */
++ for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
++ q = 1; /* lambda[0] is always 0 */
++ for (j = deg_lambda; j > 0; j--) {
++ if (reg[j] != nn) {
++ reg[j] = rs_modnn(rs, reg[j] + j);
++ q ^= alpha_to[reg[j]];
++ }
++ }
++ if (q != 0)
++ continue; /* Not a root */
++ /* store root (index-form) and error location number */
++ root[count] = i;
++ loc[count] = k;
++ /* If we've already found max possible roots,
++ * abort the search to save time
++ */
++ if (++count == deg_lambda)
++ break;
++ }
++ if (deg_lambda != count) {
++ /*
++ * deg(lambda) unequal to number of roots => uncorrectable
++ * error detected
++ */
++ count = -1;
++ goto finish;
+ }
-+ return 0;
-+}
-+
-+static inline map_word map_word_load(struct map_info *map, const void *ptr)
-+{
-+ map_word r;
-+
-+ if (map_bankwidth_is_1(map))
-+ r.x[0] = *(unsigned char *)ptr;
-+ else if (map_bankwidth_is_2(map))
-+ r.x[0] = get_unaligned((uint16_t *)ptr);
-+ else if (map_bankwidth_is_4(map))
-+ r.x[0] = get_unaligned((uint32_t *)ptr);
-+#if BITS_PER_LONG >= 64
-+ else if (map_bankwidth_is_8(map))
-+ r.x[0] = get_unaligned((uint64_t *)ptr);
- #endif
-- kfree(mtd);
-+ else if (map_bankwidth_is_large(map))
-+ memcpy(r.x, ptr, map->bankwidth);
-+
-+ return r;
- }
-
--#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 map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
-+{
-+ int i;
-+
-+ if (map_bankwidth_is_large(map)) {
-+ char *dest = (char *)&orig;
-+ memcpy(dest+start, buf, len);
-+ } else {
-+ for (i=start; i < start+len; i++) {
-+ int bitpos;
-+#ifdef __LITTLE_ENDIAN
-+ bitpos = i*8;
-+#else /* __BIG_ENDIAN */
-+ bitpos = (map_bankwidth(map)-1-i)*8;
-+#endif
-+ orig.x[0] &= ~(0xff << bitpos);
-+ orig.x[0] |= buf[i-start] << bitpos;
++ /*
++ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
++ * x**nroots). in index form. Also find deg(omega).
++ */
++ deg_omega = deg_lambda - 1;
++ for (i = 0; i <= deg_omega; i++) {
++ tmp = 0;
++ for (j = i; j >= 0; j--) {
++ if ((s[i - j] != nn) && (lambda[j] != nn))
++ tmp ^=
++ alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
+ }
++ omega[i] = index_of[tmp];
+ }
-+ return orig;
-+}
+
-+static inline map_word map_word_ff(struct map_info *map)
-+{
-+ map_word r;
-+ int i;
++ /*
++ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
++ * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
++ */
++ for (j = count - 1; j >= 0; j--) {
++ num1 = 0;
++ for (i = deg_omega; i >= 0; i--) {
++ if (omega[i] != nn)
++ num1 ^= alpha_to[rs_modnn(rs, omega[i] +
++ i * root[j])];
++ }
++ num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
++ den = 0;
+
-+ for (i=0; i<map_words(map); i++) {
-+ r.x[i] = ~0UL;
++ /* lambda[i+1] for i even is the formal derivative
++ * lambda_pr of lambda[i] */
++ for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
++ if (lambda[i + 1] != nn) {
++ den ^= alpha_to[rs_modnn(rs, lambda[i + 1] +
++ i * root[j])];
++ }
++ }
++ /* Apply error to data */
++ if (num1 != 0 && loc[j] >= pad) {
++ uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] +
++ index_of[num2] +
++ nn - index_of[den])];
++ /* Store the error correction pattern, if a
++ * correction buffer is available */
++ if (corr) {
++ corr[j] = cor;
++ } else {
++ /* If a data buffer is given and the
++ * error is inside the message,
++ * correct it */
++ if (data && (loc[j] < (nn - nroots)))
++ data[loc[j] - pad] ^= cor;
++ }
++ }
+ }
-+ return r;
-+}
-+
-+static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
-+{
-+ map_word r;
+
-+ if (map_bankwidth_is_1(map))
-+ r.x[0] = __raw_readb(map->virt + ofs);
-+ else if (map_bankwidth_is_2(map))
-+ r.x[0] = __raw_readw(map->virt + ofs);
-+ else if (map_bankwidth_is_4(map))
-+ r.x[0] = __raw_readl(map->virt + ofs);
-+#if BITS_PER_LONG >= 64
-+ else if (map_bankwidth_is_8(map))
-+ r.x[0] = __raw_readq(map->virt + ofs);
-+#endif
-+ else if (map_bankwidth_is_large(map))
-+ memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
++finish:
++ if (eras_pos != NULL) {
++ for (i = 0; i < count; i++)
++ eras_pos[i] = loc[i] - pad;
++ }
++ return count;
+
-+ return r;
+}
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/encode_rs.c
+@@ -0,0 +1,54 @@
++/*
++ * lib/reed_solomon/encode_rs.c
++ *
++ * Overview:
++ * Generic Reed Solomon encoder / decoder library
++ *
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
+
-+static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
++/* Generic data width independent code which is included by the
++ * wrappers.
++ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
++ */
+{
-+ if (map_bankwidth_is_1(map))
-+ __raw_writeb(datum.x[0], map->virt + ofs);
-+ else if (map_bankwidth_is_2(map))
-+ __raw_writew(datum.x[0], map->virt + ofs);
-+ else if (map_bankwidth_is_4(map))
-+ __raw_writel(datum.x[0], map->virt + ofs);
-+#if BITS_PER_LONG >= 64
-+ else if (map_bankwidth_is_8(map))
-+ __raw_writeq(datum.x[0], map->virt + ofs);
-+#endif
-+ else if (map_bankwidth_is_large(map))
-+ memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
-+ mb();
-+}
++ int i, j, pad;
++ int nn = rs->nn;
++ int nroots = rs->nroots;
++ uint16_t *alpha_to = rs->alpha_to;
++ uint16_t *index_of = rs->index_of;
++ uint16_t *genpoly = rs->genpoly;
++ uint16_t fb;
++ uint16_t msk = (uint16_t) rs->nn;
+
-+static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-+{
-+ if (map->cached)
-+ memcpy(to, (char *)map->cached + from, len);
-+ else
-+ memcpy_fromio(to, map->virt + from, len);
-+}
++ /* Check length parameter for validity */
++ pad = nn - nroots - len;
++ if (pad < 0 || pad >= nn)
++ return -ERANGE;
+
-+static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-+{
-+ memcpy_toio(map->virt + to, from, len);
++ for (i = 0; i < len; i++) {
++ fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
++ /* feedback term is non-zero */
++ if (fb != nn) {
++ for (j = 1; j < nroots; j++) {
++ par[j] ^= alpha_to[rs_modnn(rs, fb +
++ genpoly[nroots - j])];
++ }
++ }
++ /* Shift */
++ memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
++ if (fb != nn) {
++ par[nroots - 1] = alpha_to[rs_modnn(rs,
++ fb + genpoly[0])];
++ } else {
++ par[nroots - 1] = 0;
++ }
++ }
++ return 0;
+}
-+
-+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-+#define map_read(map, ofs) (map)->read(map, ofs)
-+#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
-+#define map_write(map, datum, ofs) (map)->write(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)
-+
-+#else
-+#define map_read(map, ofs) inline_map_read(map, ofs)
-+#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
-+#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
-+#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
-+
-+
-+#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
-+#define map_is_linear(map) ({ (void)(map); 1; })
-+
-+#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
-
- #endif /* __LINUX_MTD_MAP_H__ */
---- linux-2.4.21/include/linux/mtd/mtd.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/mtd.h
-@@ -1,123 +1,45 @@
--
--/* $Id$ */
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/rslib.c
+@@ -0,0 +1,335 @@
+/*
++ * lib/reed_solomon/rslib.c
++ *
++ * Overview:
++ * Generic Reed Solomon encoder / decoder library
++ *
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * Reed Solomon code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
+ * $Id$
+ *
-+ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ * 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.
++ *
++ * Description:
++ *
++ * The generic Reed Solomon library provides runtime configurable
++ * encoding / decoding of RS codes.
++ * Each user must call init_rs to get a pointer to a rs_control
++ * structure for the given rs parameters. This structure is either
++ * generated or a already available matching control structure is used.
++ * If a structure is generated then the polynomial arrays for
++ * fast encoding / decoding are built. This can take some time so
++ * make sure not to call this function from a time critical path.
++ * Usually a module / driver should initialize the necessary
++ * rs_control structure on module / driver init and release it
++ * on exit.
++ * The encoding puts the calculated syndrome into a given syndrome
++ * buffer.
++ * The decoding is a two step process. The first step calculates
++ * the syndrome over the received (data + syndrome) and calls the
++ * second stage, which does the decoding / error correction itself.
++ * Many hw encoders provide a syndrome calculation over the received
++ * data + syndrome and can call the second stage directly.
+ *
-+ * Released under GPL
+ */
-
- #ifndef __MTD_MTD_H__
- #define __MTD_MTD_H__
-
--#ifdef __KERNEL__
-+#ifndef __KERNEL__
-+#error This is a kernel header. Perhaps include mtd-user.h instead?
-+#endif
-
- #include <linux/config.h>
- #include <linux/version.h>
- #include <linux/types.h>
--#include <linux/mtd/compatmac.h>
- #include <linux/module.h>
- #include <linux/uio.h>
-
--#endif /* __KERNEL__ */
--
--struct erase_info_user {
-- u_int32_t start;
-- u_int32_t length;
--};
--
--struct mtd_oob_buf {
-- u_int32_t start;
-- u_int32_t length;
-- unsigned char *ptr;
--};
--
-+#include <linux/mtd/compatmac.h>
-+#include <mtd/mtd-abi.h>
-
- #define MTD_CHAR_MAJOR 90
- #define MTD_BLOCK_MAJOR 31
- #define MAX_MTD_DEVICES 16
-
--
--
--#define MTD_ABSENT 0
--#define MTD_RAM 1
--#define MTD_ROM 2
--#define MTD_NORFLASH 3
--#define MTD_NANDFLASH 4
--#define MTD_PEROM 5
--#define MTD_OTHER 14
--#define MTD_UNKNOWN 15
--
--
--
--#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
--#define MTD_SET_BITS 2 // Bits can be set
--#define MTD_ERASEABLE 4 // Has an erase function
--#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
--#define MTD_VOLATILE 16 // Set for RAMs
--#define MTD_XIP 32 // eXecute-In-Place possible
--#define MTD_OOB 64 // Out-of-band data (NAND flash)
--#define MTD_ECC 128 // Device capable of automatic ECC
--
--// Some common devices / combinations of capabilities
--#define MTD_CAP_ROM 0
--#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
--#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
--#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
--#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
--
--
--// Types of automatic ECC/Checksum available
--#define MTD_ECC_NONE 0 // No automatic ECC available
--#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
--#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
--
--struct mtd_info_user {
-- u_char type;
-- u_int32_t flags;
-- u_int32_t size; // Total size of the MTD
-- u_int32_t erasesize;
-- u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
-- u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
-- u_int32_t ecctype;
-- u_int32_t eccsize;
--};
--
--struct region_info_user {
-- u_int32_t offset; /* At which this region starts,
-- * from the beginning of the MTD */
-- u_int32_t erasesize; /* For this region */
-- u_int32_t numblocks; /* Number of blocks in this region */
-- u_int32_t regionindex;
--};
--
--#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
--#define MEMERASE _IOW('M', 2, struct erase_info_user)
--#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
--#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
--#define MEMLOCK _IOW('M', 5, struct erase_info_user)
--#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)
--
--#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;
--
-- /* User-space ioctl definitions */
--
--
--#else /* __KERNEL__ */
--
--
- #define MTD_ERASE_PENDING 0x01
- #define MTD_ERASING 0x02
- #define MTD_ERASE_SUSPEND 0x04
- #define MTD_ERASE_DONE 0x08
- #define MTD_ERASE_FAILED 0x10
-
-+/* If the erase fails, fail_addr might indicate exactly which block failed. If
-+ fail_addr = 0xffffffff, the failure was not at the device level or was not
-+ specific to any particular block. */
- struct erase_info {
- struct mtd_info *mtd;
- u_int32_t addr;
- u_int32_t len;
-+ u_int32_t fail_addr;
- u_long time;
- u_long retries;
- u_int dev;
-@@ -147,13 +69,18 @@
-
- u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
- u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
-+ u_int32_t oobavail; // Number of bytes in OOB area available for fs
- 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;
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/rslib.h>
++#include <linux/slab.h>
++#include <asm/semaphore.h>
+
- /* Data for variable erase regions. If numeraseregions is zero,
- * it means that the whole device has erasesize as given above.
- */
-@@ -163,7 +90,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 +102,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);
-@@ -187,24 +113,24 @@
- * flash devices. The user data is one time programmable but the
- * factory data is read only.
- */
-- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
--
-+ int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
--
-- /* This function is not yet implemented */
-+ int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
-+ int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-+ int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
-
-- /* iovec-based read/write methods. We need these especially for NAND flash,
-+ /* kvec-based read/write methods. We need these especially for NAND flash,
- with its limited number of write cycles per erase.
- NB: The 'count' parameter is the number of _vectors_, each of
- which contains an (ofs, len) tuple.
- */
-- 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);
-- 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);
-+ int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
-+ int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from,
-+ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-+ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
-+ int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to,
-+ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-
- /* Sync */
- void (*sync) (struct mtd_info *mtd);
-@@ -217,7 +143,14 @@
- int (*suspend) (struct mtd_info *mtd);
- void (*resume) (struct mtd_info *mtd);
-
-+ /* Bad block management functions */
-+ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
-+ int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
++/* This list holds all currently allocated rs control structures */
++static LIST_HEAD (rslist);
++/* Protection for the list */
++static DECLARE_MUTEX(rslistlock);
++
++/**
++ * rs_init - Initialize a Reed-Solomon codec
++ *
++ * @symsize: symbol size, bits (1-8)
++ * @gfpoly: Field generator polynomial coefficients
++ * @fcr: first root of RS code generator polynomial, index form
++ * @prim: primitive element to generate polynomial roots
++ * @nroots: RS code generator polynomial degree (number of roots)
++ *
++ * Allocate a control structure and the polynom arrays for faster
++ * en/decoding. Fill the arrays according to the given parameters
++ */
++static struct rs_control *rs_init(int symsize, int gfpoly, int fcr,
++ int prim, int nroots)
++{
++ struct rs_control *rs;
++ int i, j, sr, root, iprim;
+
- void *priv;
++ /* Allocate the control structure */
++ rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
++ if (rs == NULL)
++ return NULL;
+
-+ struct module *owner;
-+ int usecount;
- };
-
-
-@@ -226,44 +159,27 @@
- 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;
--
-- ret = __get_mtd_device(mtd, num);
--
-- if (ret && ret->module && !try_inc_mod_count(ret->module))
-- return NULL;
--
-- return ret;
--}
-+extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
-
--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;
- };
-
-
- extern void register_mtd_user (struct mtd_notifier *new);
- extern int unregister_mtd_user (struct mtd_notifier *old);
-
--int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
-+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
- unsigned long count, loff_t to, size_t *retlen);
-
--int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
-+int default_mtd_readv(struct mtd_info *mtd, struct kvec *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 +192,17 @@
- #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 */
++ INIT_LIST_HEAD(&rs->list);
+
++ rs->mm = symsize;
++ rs->nn = (1 << symsize) - 1;
++ rs->fcr = fcr;
++ rs->prim = prim;
++ rs->nroots = nroots;
++ rs->gfpoly = gfpoly;
+
-+#ifdef CONFIG_MTD_PARTITIONS
-+void mtd_erase_callback(struct erase_info *instr);
-+#else
-+static inline void mtd_erase_callback(struct erase_info *instr)
-+{
-+ if (instr->callback)
-+ instr->callback(instr);
-+}
-+#endif
-
- /*
- * Debugging macro and defines
-@@ -288,13 +214,13 @@
-
- #ifdef CONFIG_MTD_DEBUG
- #define DEBUG(n, args...) \
-- if (n <= CONFIG_MTD_DEBUG_VERBOSE) { \
-+ do { \
-+ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \
- printk(KERN_INFO args); \
-- }
-+ } while(0)
- #else /* CONFIG_MTD_DEBUG */
--#define DEBUG(n, args...)
--#endif /* CONFIG_MTD_DEBUG */
-+#define DEBUG(n, args...) do { } while(0)
-
--#endif /* __KERNEL__ */
-+#endif /* CONFIG_MTD_DEBUG */
-
- #endif /* __MTD_MTD_H__ */
---- linux-2.4.21/include/linux/mtd/nand.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nand.h
-@@ -2,10 +2,10 @@
- * linux/include/linux/mtd/nand.h
- *
- * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
-- * Steven J. Hill <sjhill@cotw.com>
-+ * Steven J. Hill <sjhill@realitydiluted.com>
- * Thomas Gleixner <tglx@linutronix.de>
- *
-- * $Id$
-+ * $Id$
- *
- * 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
-@@ -44,27 +44,61 @@
- * NAND_YAFFS_OOB
- * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL
- * Split manufacturer and device ID structures
-+ *
-+ * 02-08-2004 tglx added option field to nand structure for chip anomalities
-+ * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id
-+ * update of nand_chip structure description
-+ * 01-17-2005 dmarlin added extended commands for AG-AND device and added option
-+ * for BBT_AUTO_REFRESH.
-+ * 01-20-2005 dmarlin added optional pointer to hardware specific callback for
-+ * extra error status checks.
- */
- #ifndef __LINUX_MTD_NAND_H
- #define __LINUX_MTD_NAND_H
-
- #include <linux/config.h>
--#include <linux/sched.h>
-+#include <linux/wait.h>
-+#include <linux/spinlock.h>
-+#include <linux/mtd/mtd.h>
-
--/*
-- * Searches for a NAND device
-+struct mtd_info;
-+/* Scan and identify a NAND device */
-+extern int nand_scan (struct mtd_info *mtd, int max_chips);
-+/* Free resources held by the NAND device */
-+extern void nand_release (struct mtd_info *mtd);
++ /* Allocate the arrays */
++ rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++ if (rs->alpha_to == NULL)
++ goto errrs;
+
-+/* Read raw data from the device without ECC */
-+extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
++ rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++ if (rs->index_of == NULL)
++ goto erralp;
+
++ rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
++ if(rs->genpoly == NULL)
++ goto erridx;
+
-+/* The maximum number of NAND chips in an array */
-+#define NAND_MAX_CHIPS 8
++ /* Generate Galois field lookup tables */
++ rs->index_of[0] = rs->nn; /* log(zero) = -inf */
++ rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */
++ sr = 1;
++ for (i = 0; i < rs->nn; i++) {
++ rs->index_of[sr] = i;
++ rs->alpha_to[i] = sr;
++ sr <<= 1;
++ if (sr & (1 << symsize))
++ sr ^= gfpoly;
++ sr &= rs->nn;
++ }
++ /* If it's not primitive, exit */
++ if(sr != 1)
++ goto errpol;
+
-+/* This constant declares the max. oobsize / page, which
-+ * is supported now. If you add a chip with bigger oobsize/page
-+ * adjust this accordingly.
- */
--extern int nand_scan (struct mtd_info *mtd);
-+#define NAND_MAX_OOBSIZE 64
-
- /*
- * Constants for hardware specific CLE/ALE/NCE function
- */
-+/* Select the chip by setting nCE to low */
- #define NAND_CTL_SETNCE 1
-+/* Deselect the chip by setting nCE to high */
- #define NAND_CTL_CLRNCE 2
-+/* Select the command latch by setting CLE to high */
- #define NAND_CTL_SETCLE 3
-+/* Deselect the command latch by setting CLE to low */
- #define NAND_CTL_CLRCLE 4
-+/* Select the address latch by setting ALE to high */
- #define NAND_CTL_SETALE 5
-+/* Deselect the address latch by setting ALE to low */
- #define NAND_CTL_CLRALE 6
-+/* Set write protection by setting WP to high. Not used! */
-+#define NAND_CTL_SETWP 7
-+/* Clear write protection by setting WP to low. Not used! */
-+#define NAND_CTL_CLRWP 8
-
- /*
- * Standard NAND flash commands
-@@ -75,35 +109,132 @@
- #define NAND_CMD_READOOB 0x50
- #define NAND_CMD_ERASE1 0x60
- #define NAND_CMD_STATUS 0x70
-+#define NAND_CMD_STATUS_MULTI 0x71
- #define NAND_CMD_SEQIN 0x80
- #define NAND_CMD_READID 0x90
- #define NAND_CMD_ERASE2 0xd0
- #define NAND_CMD_RESET 0xff
-
-+/* Extended commands for large page devices */
-+#define NAND_CMD_READSTART 0x30
-+#define NAND_CMD_CACHEDPROG 0x15
++ /* Find prim-th root of 1, used in decoding */
++ for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
++ /* prim-th root of 1, index form */
++ rs->iprim = iprim / prim;
+
-+/* Extended commands for AG-AND device */
-+/*
-+ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
-+ * there is no way to distinguish that from NAND_CMD_READ0
-+ * until the remaining sequence of commands has been completed
-+ * so add a high order bit and mask it off in the command.
-+ */
-+#define NAND_CMD_DEPLETE1 0x100
-+#define NAND_CMD_DEPLETE2 0x38
-+#define NAND_CMD_STATUS_MULTI 0x71
-+#define NAND_CMD_STATUS_ERROR 0x72
-+/* multi-bank error status (banks 0-3) */
-+#define NAND_CMD_STATUS_ERROR0 0x73
-+#define NAND_CMD_STATUS_ERROR1 0x74
-+#define NAND_CMD_STATUS_ERROR2 0x75
-+#define NAND_CMD_STATUS_ERROR3 0x76
-+#define NAND_CMD_STATUS_RESET 0x7f
-+#define NAND_CMD_STATUS_CLEAR 0xff
++ /* Form RS code generator polynomial from its roots */
++ rs->genpoly[0] = 1;
++ for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
++ rs->genpoly[i + 1] = 1;
++ /* Multiply rs->genpoly[] by @**(root + x) */
++ for (j = i; j > 0; j--) {
++ if (rs->genpoly[j] != 0) {
++ rs->genpoly[j] = rs->genpoly[j -1] ^
++ rs->alpha_to[rs_modnn(rs,
++ rs->index_of[rs->genpoly[j]] + root)];
++ } else
++ rs->genpoly[j] = rs->genpoly[j - 1];
++ }
++ /* rs->genpoly[0] can never be zero */
++ rs->genpoly[0] =
++ rs->alpha_to[rs_modnn(rs,
++ rs->index_of[rs->genpoly[0]] + root)];
++ }
++ /* convert rs->genpoly[] to index form for quicker encoding */
++ for (i = 0; i <= nroots; i++)
++ rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
++ return rs;
+
-+/* Status bits */
-+#define NAND_STATUS_FAIL 0x01
-+#define NAND_STATUS_FAIL_N1 0x02
-+#define NAND_STATUS_TRUE_READY 0x20
-+#define NAND_STATUS_READY 0x40
-+#define NAND_STATUS_WP 0x80
++ /* Error exit */
++errpol:
++ kfree(rs->genpoly);
++erridx:
++ kfree(rs->index_of);
++erralp:
++ kfree(rs->alpha_to);
++errrs:
++ kfree(rs);
++ return NULL;
++}
+
- /*
- * Constants for ECC_MODES
-- *
-- * NONE: No ECC
-- * SOFT: Software ECC 3 byte ECC per 256 Byte data
-- * HW3_256: Hardware ECC 3 byte ECC per 256 Byte data
-- * HW3_512: Hardware ECC 3 byte ECC per 512 Byte data
-- *
-- *
--*/
++
++/**
++ * free_rs - Free the rs control structure, if its not longer used
++ *
++ * @rs: the control structure which is not longer used by the
++ * caller
+ */
++void free_rs(struct rs_control *rs)
++{
++ down(&rslistlock);
++ rs->users--;
++ if(!rs->users) {
++ list_del(&rs->list);
++ kfree(rs->alpha_to);
++ kfree(rs->index_of);
++ kfree(rs->genpoly);
++ kfree(rs);
++ }
++ up(&rslistlock);
++}
+
-+/* No ECC. Usage is not recommended ! */
- #define NAND_ECC_NONE 0
-+/* Software ECC 3 byte ECC per 256 Byte data */
- #define NAND_ECC_SOFT 1
-+/* Hardware ECC 3 byte ECC per 256 Byte data */
- #define NAND_ECC_HW3_256 2
-+/* Hardware ECC 3 byte ECC per 512 Byte data */
- #define NAND_ECC_HW3_512 3
-+/* Hardware ECC 3 byte ECC per 512 Byte data */
- #define NAND_ECC_HW6_512 4
--#define NAND_ECC_DISKONCHIP 5
-+/* Hardware ECC 8 byte ECC per 512 Byte data */
-+#define NAND_ECC_HW8_512 6
-+/* Hardware ECC 12 byte ECC per 2048 Byte data */
-+#define NAND_ECC_HW12_2048 7
-
- /*
- * Constants for Hardware ECC
--*/
++/**
++ * init_rs - Find a matching or allocate a new rs control structure
++ *
++ * @symsize: the symbol size (number of bits)
++ * @gfpoly: the extended Galois field generator polynomial coefficients,
++ * with the 0th coefficient in the low order bit. The polynomial
++ * must be primitive;
++ * @fcr: the first consecutive root of the rs code generator polynomial
++ * in index form
++ * @prim: primitive element to generate polynomial roots
++ * @nroots: RS code generator polynomial degree (number of roots)
+ */
-+/* Reset Hardware ECC for read */
- #define NAND_ECC_READ 0
-+/* Reset Hardware ECC for write */
- #define NAND_ECC_WRITE 1
-+/* Enable Hardware ECC before syndrom is read back from flash */
-+#define NAND_ECC_READSYN 2
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
++ int nroots)
++{
++ struct list_head *tmp;
++ struct rs_control *rs;
+
-+/* Bit mask for flags passed to do_nand_read_ecc */
-+#define NAND_GET_DEVICE 0x80
++ /* Sanity checks */
++ if (symsize < 1)
++ return NULL;
++ if (fcr < 0 || fcr >= (1<<symsize))
++ return NULL;
++ if (prim <= 0 || prim >= (1<<symsize))
++ return NULL;
++ if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
++ return NULL;
++
++ down(&rslistlock);
+
++ /* Walk through the list and look for a matching entry */
++ list_for_each(tmp, &rslist) {
++ rs = list_entry(tmp, struct rs_control, list);
++ if (symsize != rs->mm)
++ continue;
++ if (gfpoly != rs->gfpoly)
++ continue;
++ if (fcr != rs->fcr)
++ continue;
++ if (prim != rs->prim)
++ continue;
++ if (nroots != rs->nroots)
++ continue;
++ /* We have a matching one already */
++ rs->users++;
++ goto out;
++ }
+
-+/* Option constants for bizarre disfunctionality and real
-+* features
-+*/
-+/* Chip can not auto increment pages */
-+#define NAND_NO_AUTOINCR 0x00000001
-+/* Buswitdh is 16 bit */
-+#define NAND_BUSWIDTH_16 0x00000002
-+/* Device supports partial programming without padding */
-+#define NAND_NO_PADDING 0x00000004
-+/* Chip has cache program function */
-+#define NAND_CACHEPRG 0x00000008
-+/* Chip has copy back function */
-+#define NAND_COPYBACK 0x00000010
-+/* AND Chip which has 4 banks and a confusing page / block
-+ * assignment. See Renesas datasheet for further information */
-+#define NAND_IS_AND 0x00000020
-+/* Chip has a array of 4 pages which can be read without
-+ * additional ready /busy waits */
-+#define NAND_4PAGE_ARRAY 0x00000040
-+/* Chip requires that BBT is periodically rewritten to prevent
-+ * bits from adjacent blocks from 'leaking' in altering data.
-+ * This happens with the Renesas AG-AND chips, possibly others. */
-+#define BBT_AUTO_REFRESH 0x00000080
++ /* Create a new one */
++ rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
++ if (rs) {
++ rs->users = 1;
++ list_add(&rs->list, &rslist);
++ }
++out:
++ up(&rslistlock);
++ return rs;
++}
+
-+/* Options valid for Samsung large page devices */
-+#define NAND_SAMSUNG_LP_OPTIONS \
-+ (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
++#ifdef CONFIG_REED_SOLOMON_ENC8
++/**
++ * encode_rs8 - Calculate the parity for data values (8bit data width)
++ *
++ * @rs: the rs control structure
++ * @data: data field of a given type
++ * @len: data length
++ * @par: parity data, must be initialized by caller (usually all 0)
++ * @invmsk: invert data mask (will be xored on data)
++ *
++ * The parity uses a uint16_t data type to enable
++ * symbol size > 8. The calling code must take care of encoding of the
++ * syndrome result for storage itself.
++ */
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
++ uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs8);
++#endif
+
-+/* Macros to identify the above */
-+#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
-+#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
-+#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
-+#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
++#ifdef CONFIG_REED_SOLOMON_DEC8
++/**
++ * decode_rs8 - Decode codeword (8bit data width)
++ *
++ * @rs: the rs control structure
++ * @data: data field of a given type
++ * @par: received parity data field
++ * @len: data length
++ * @s: syndrome data field (if NULL, syndrome is calculated)
++ * @no_eras: number of erasures
++ * @eras_pos: position of erasures, can be NULL
++ * @invmsk: invert data mask (will be xored on data, not on parity!)
++ * @corr: buffer to store correction bitmask on eras_pos
++ *
++ * The syndrome and parity uses a uint16_t data type to enable
++ * symbol size > 8. The calling code must take care of decoding of the
++ * syndrome result and the received parity before calling this code.
++ */
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
++ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++ uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs8);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_ENC16
++/**
++ * encode_rs16 - Calculate the parity for data values (16bit data width)
++ *
++ * @rs: the rs control structure
++ * @data: data field of a given type
++ * @len: data length
++ * @par: parity data, must be initialized by caller (usually all 0)
++ * @invmsk: invert data mask (will be xored on data, not on parity!)
++ *
++ * Each field in the data array contains up to symbol size bits of valid data.
++ */
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
++ uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs16);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_DEC16
++/**
++ * decode_rs16 - Decode codeword (16bit data width)
++ *
++ * @rs: the rs control structure
++ * @data: data field of a given type
++ * @par: received parity data field
++ * @len: data length
++ * @s: syndrome data field (if NULL, syndrome is calculated)
++ * @no_eras: number of erasures
++ * @eras_pos: position of erasures, can be NULL
++ * @invmsk: invert data mask (will be xored on data, not on parity!)
++ * @corr: buffer to store correction bitmask on eras_pos
++ *
++ * Each field in the data array contains up to symbol size bits of valid data.
++ */
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++ uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs16);
++#endif
+
-+/* Mask to zero out the chip options, which come from the id table */
-+#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
++EXPORT_SYMBOL_GPL(init_rs);
++EXPORT_SYMBOL_GPL(free_rs);
+
-+/* Non chip related options */
-+/* Use a flash based bad block table. This option is passed to the
-+ * default bad block table function. */
-+#define NAND_USE_FLASH_BBT 0x00010000
-+/* The hw ecc generator provides a syndrome instead a ecc value on read
-+ * This can only work if we have the ecc bytes directly behind the
-+ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
-+#define NAND_HWECC_SYNDROME 0x00020000
-+/* This option skips the bbt scan during initialization. */
-+#define NAND_SKIP_BBTSCAN 0x00040000
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
++MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
+
-+/* Options set by nand scan */
-+/* Nand scan has allocated oob_buf */
-+#define NAND_OOBBUF_ALLOC 0x40000000
-+/* Nand scan has allocated data_buf */
-+#define NAND_DATABUF_ALLOC 0x80000000
+--- linux-2.4.21/mm/swap.c~swap-performance
++++ linux-2.4.21/mm/swap.c
+@@ -28,7 +28,7 @@
+ int page_cluster;
+
+ pager_daemon_t pager_daemon = {
+- 512, /* base number for calculating the number of tries */
++ 128, /* base number for calculating the number of tries */
+ SWAP_CLUSTER_MAX, /* minimum number of tries */
+ 8, /* do swap I/O in clusters of this size */
+ };
+--- linux-2.4.21/mm/vmalloc.c~vmalloc
++++ linux-2.4.21/mm/vmalloc.c
+@@ -183,6 +183,9 @@
+ return NULL;
+
+ size += PAGE_SIZE;
++#ifdef VMALLOC_ALIGN
++ size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
++#endif
+ if (!size) {
+ kfree (area);
+ return NULL;
+--- linux-2.4.21/net/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/net/bluetooth/Config.in
+@@ -13,6 +13,8 @@
+ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
+ source net/bluetooth/rfcomm/Config.in
+ source net/bluetooth/bnep/Config.in
++ source net/bluetooth/cmtp/Config.in
++ source net/bluetooth/hidp/Config.in
+ source drivers/bluetooth/Config.in
+ fi
+
+--- linux-2.4.21/net/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/net/bluetooth/Makefile
+@@ -5,7 +5,7 @@
+ O_TARGET := bluetooth.o
+
+ list-multi := bluez.o
+-export-objs := syms.o
++export-objs := syms.o l2cap.o
+
+ bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
+
+@@ -15,6 +15,8 @@
+
+ subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
+ subdir-$(CONFIG_BLUEZ_BNEP) += bnep
++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
++subdir-$(CONFIG_BLUEZ_HIDP) += hidp
+
+ ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
+ obj-y += rfcomm/rfcomm.o
+@@ -24,6 +26,14 @@
+ obj-y += bnep/bnep.o
+ endif
+
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
+
-
- /*
-+ * nand_state_t - chip states
- * Enumeration for NAND flash chip state
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
+ include $(TOPDIR)/Rules.make
+
+ bluez.o: $(bluez-objs)
+--- linux-2.4.21/net/bluetooth/af_bluetooth.c~bluetooth
++++ linux-2.4.21/net/bluetooth/af_bluetooth.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id$
*/
- typedef enum {
-@@ -111,73 +242,137 @@
- FL_READING,
- FL_WRITING,
- FL_ERASING,
-- FL_SYNCING
-+ FL_SYNCING,
-+ FL_CACHEDPRG,
- } nand_state_t;
+-#define VERSION "2.2"
++#define VERSION "2.4"
-+/* Keep gcc happy */
-+struct nand_chip;
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -57,7 +57,7 @@
+ #endif
--/*
-- * NAND Private Flash Chip Data
-- *
-- * Structure overview:
-- *
-- * IO_ADDR_R - address to read the 8 I/O lines of the flash device
-- *
-- * IO_ADDR_W - address to write the 8 I/O lines of the flash device
-- *
-- * hwcontrol - hardwarespecific function for accesing control-lines
-- *
-- * dev_ready - hardwarespecific function for accesing device ready/busy line
-- *
-- * waitfunc - hardwarespecific function for wait on ready
-- *
-- * calculate_ecc - function for ecc calculation or readback from ecc hardware
-- *
-- * correct_data - function for ecc correction, matching to ecc generator (sw/hw)
-- *
-- * enable_hwecc - function to enable (reset) hardware ecc generator
-- *
-- * eccmod - mode of ecc: see constants
-- *
-- * eccsize - databytes used per ecc-calculation
-- *
-- * chip_delay - chip dependent delay for transfering data from array to read regs (tR)
-- *
-- * chip_lock - spinlock used to protect access to this structure
-- *
-- * wq - wait queue to sleep on if a NAND operation is in progress
-- *
-- * state - give the current state of the NAND device
-- *
-- * page_shift - number of address bits in a page (column address bits)
-- *
-- * data_buf - data buffer passed to/from MTD user modules
-- *
-- * data_cache - data cache for redundant page access and shadow for
-- * ECC failure
-- *
-- * cache_page - number of last valid page in page_cache
-+/**
-+ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
-+ * @lock: protection lock
-+ * @active: the mtd device which holds the controller currently
- */
-+struct nand_hw_control {
-+ spinlock_t lock;
-+ struct nand_chip *active;
-+};
+ /* Bluetooth sockets */
+-#define BLUEZ_MAX_PROTO 5
++#define BLUEZ_MAX_PROTO 7
+ static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+ int bluez_sock_register(int proto, struct net_proto_family *ops)
+@@ -221,12 +221,11 @@
+ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+ {
+ struct sock *sk = sock->sk;
+- unsigned int mask;
++ unsigned int mask = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ poll_wait(file, sk->sleep, wait);
+- mask = 0;
+
+ if (sk->err || !skb_queue_empty(&sk->error_queue))
+ mask |= POLLERR;
+@@ -242,9 +241,11 @@
+ if (sk->state == BT_CLOSED)
+ mask |= POLLHUP;
+
+- if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
++ if (sk->state == BT_CONNECT ||
++ sk->state == BT_CONNECT2 ||
++ sk->state == BT_CONFIG)
+ return mask;
+-
+
-+/**
-+ * struct nand_chip - NAND Private Flash Chip Data
-+ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
-+ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
-+ * @read_byte: [REPLACEABLE] read one byte from the chip
-+ * @write_byte: [REPLACEABLE] write one byte to the chip
-+ * @read_word: [REPLACEABLE] read one word from the chip
-+ * @write_word: [REPLACEABLE] write one word to the chip
-+ * @write_buf: [REPLACEABLE] write data from the buffer to the chip
-+ * @read_buf: [REPLACEABLE] read data from the chip into the buffer
-+ * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
-+ * @select_chip: [REPLACEABLE] select chip nr
-+ * @block_bad: [REPLACEABLE] check, if the block is bad
-+ * @block_markbad: [REPLACEABLE] mark the block bad
-+ * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
-+ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
-+ * If set to NULL no access to ready/busy is available and the ready/busy information
-+ * is read from the chip status register
-+ * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
-+ * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
-+ * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware
-+ * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
-+ * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
-+ * be provided if a hardware ECC is available
-+ * @erase_cmd: [INTERN] erase command write function, selectable due to AND support
-+ * @scan_bbt: [REPLACEABLE] function to scan bad block table
-+ * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
-+ * @eccsize: [INTERN] databytes used per ecc-calculation
-+ * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step
-+ * @eccsteps: [INTERN] number of ecc calculation steps per page
-+ * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
-+ * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
-+ * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
-+ * @state: [INTERN] the current state of the NAND device
-+ * @page_shift: [INTERN] number of address bits in a page (column address bits)
-+ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
-+ * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
-+ * @chip_shift: [INTERN] number of address bits in one chip
-+ * @data_buf: [INTERN] internal buffer for one page + oob
-+ * @oob_buf: [INTERN] oob buffer for one eraseblock
-+ * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
-+ * @data_poi: [INTERN] pointer to a data buffer
-+ * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
-+ * special functionality. See the defines for further explanation
-+ * @badblockpos: [INTERN] position of the bad block marker in the oob area
-+ * @numchips: [INTERN] number of physical chips
-+ * @chipsize: [INTERN] the size of one chip for multichip arrays
-+ * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
-+ * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
-+ * @autooob: [REPLACEABLE] the default (auto)placement scheme
-+ * @bbt: [INTERN] bad block table pointer
-+ * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
-+ * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
-+ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
-+ * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
-+ * @priv: [OPTIONAL] pointer to private chip date
-+ * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
-+ * (determine if errors are correctable)
-+ */
-+
- struct nand_chip {
-- unsigned long IO_ADDR_R;
-- unsigned long IO_ADDR_W;
-- void (*hwcontrol)(int cmd);
-- int (*dev_ready)(void);
-+ void __iomem *IO_ADDR_R;
-+ void __iomem *IO_ADDR_W;
-+
-+ u_char (*read_byte)(struct mtd_info *mtd);
-+ void (*write_byte)(struct mtd_info *mtd, u_char byte);
-+ u16 (*read_word)(struct mtd_info *mtd);
-+ void (*write_word)(struct mtd_info *mtd, u16 word);
-+
-+ 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, loff_t ofs, int getchip);
-+ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
-+ 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);
-+ int (*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);
-+ void (*erase_cmd)(struct mtd_info *mtd, int page);
-+ int (*scan_bbt)(struct mtd_info *mtd);
- int eccmode;
- int eccsize;
-+ int eccbytes;
-+ int eccsteps;
- int chip_delay;
- spinlock_t chip_lock;
- wait_queue_head_t wq;
- nand_state_t state;
- int page_shift;
-+ int phys_erase_shift;
-+ int bbt_erase_shift;
-+ int chip_shift;
- u_char *data_buf;
-+ u_char *oob_buf;
-+ int oobdirty;
- u_char *data_poi;
-- u_char *data_cache;
-- int cache_page;
-+ unsigned int options;
-+ int badblockpos;
-+ int numchips;
-+ unsigned long chipsize;
-+ int pagemask;
-+ int pagebuf;
-+ struct nand_oobinfo *autooob;
-+ uint8_t *bbt;
-+ struct nand_bbt_descr *bbt_td;
-+ struct nand_bbt_descr *bbt_md;
-+ struct nand_bbt_descr *badblock_pattern;
-+ struct nand_hw_control *controller;
-+ void *priv;
-+ int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
- };
+ if (sock_writeable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+@@ -253,39 +254,35 @@
+ return mask;
+ }
- /*
-@@ -187,46 +382,35 @@
- #define NAND_MFR_SAMSUNG 0xec
- #define NAND_MFR_FUJITSU 0x04
- #define NAND_MFR_NATIONAL 0x8f
-+#define NAND_MFR_RENESAS 0x07
-+#define NAND_MFR_STMICRO 0x20
+-int bluez_sock_w4_connect(struct sock *sk, int flags)
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+ int err = 0;
--/*
-- * NAND Flash Device ID Structure
-- *
-- * Structure overview:
-- *
-- * name - Identify the device type
-- *
-- * id - device ID code
-- *
-- * chipshift - total number of address bits for the device which
-- * is used to calculate address offsets and the total
-- * number of bytes the device is capable of.
-- *
-- * page256 - denotes if flash device has 256 byte pages or not.
-- *
-- * pageadrlen - number of bytes minus one needed to hold the
-- * complete address into the flash array. Keep in
-- * mind that when a read or write is done to a
-- * specific address, the address is input serially
-- * 8 bits at a time. This structure member is used
-- * by the read/write routines as a loop index for
-- * shifting the address out 8 bits at a time.
-+/**
-+ * struct nand_flash_dev - NAND Flash Device ID Structure
- *
-- * erasesize - size of an erase block in the flash device.
-+ * @name: Identify the device type
-+ * @id: device ID code
-+ * @pagesize: Pagesize in bytes. Either 256 or 512 or 0
-+ * If the pagesize is 0, then the real pagesize
-+ * and the eraseize are determined from the
-+ * extended id bytes in the chip
-+ * @erasesize: Size of an erase block in the flash device.
-+ * @chipsize: Total chipsize in Mega Bytes
-+ * @options: Bitfield to store chip relevant options
- */
- struct nand_flash_dev {
-- char * name;
-+ char *name;
- int id;
-- int chipshift;
-+ unsigned long pagesize;
-+ unsigned long chipsize;
- unsigned long erasesize;
-- char page256;
-+ unsigned long options;
- };
+ BT_DBG("sk %p", sk);
--/*
-- * NAND Flash Manufacturer ID Structure
-- *
-- * name - Manufacturer name
-- *
-- * id - manufacturer ID code of device.
-+/**
-+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
-+ * @name: Manufacturer name
-+ * @id: manufacturer ID code of device.
- */
- struct nand_manufacturers {
- int id;
-@@ -236,39 +420,88 @@
- extern struct nand_flash_dev nand_flash_ids[];
- extern struct nand_manufacturers nand_manuf_ids[];
+ add_wait_queue(sk->sleep, &wait);
+- while (sk->state != BT_CONNECTED) {
++ while (sk->state != state) {
+ set_current_state(TASK_INTERRUPTIBLE);
++
+ if (!timeo) {
+ err = -EAGAIN;
+ break;
+ }
--/*
--* Constants for oob configuration
--*/
--#define NAND_BADBLOCK_POS 5
-+/**
-+ * struct nand_bbt_descr - bad block table descriptor
-+ * @options: options for this descriptor
-+ * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
-+ * when bbt is searched, then we store the found bbts pages here.
-+ * Its an array and supports up to 8 chips now
-+ * @offs: offset of the pattern in the oob area of the page
-+ * @veroffs: offset of the bbt version counter in the oob are of the page
-+ * @version: version read from the bbt page during scan
-+ * @len: length of the pattern, if 0 no pattern check is performed
-+ * @maxblocks: maximum number of blocks to search for a bbt. This number of
-+ * blocks is reserved at the end of the device where the tables are
-+ * written.
-+ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
-+ * bad) block in the stored bbt
-+ * @pattern: pattern to identify bad block table or factory marked good /
-+ * bad blocks, can be NULL, if len = 0
-+ *
-+ * Descriptor for the bad block table marker and the descriptor for the
-+ * pattern which identifies good and bad blocks. The assumption is made
-+ * that the pattern and the version count are always located in the oob area
-+ * of the first block.
-+ */
-+struct nand_bbt_descr {
-+ int options;
-+ int pages[NAND_MAX_CHIPS];
-+ int offs;
-+ int veroffs;
-+ uint8_t version[NAND_MAX_CHIPS];
-+ int len;
-+ int maxblocks;
-+ int reserved_block_code;
-+ uint8_t *pattern;
-+};
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
--#define NAND_NONE_OOB 0
--#define NAND_JFFS2_OOB 1
--#define NAND_YAFFS_OOB 2
-+/* Options for the bad block table descriptors */
+- err = 0;
+- if (sk->state == BT_CONNECTED)
+- break;
+-
+ if (sk->err) {
+ err = sock_error(sk);
+ break;
+ }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk->sleep, &wait);
+--- linux-2.4.21/net/bluetooth/bnep/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/core.c
+@@ -63,7 +63,7 @@
+ #define BT_DBG(D...)
+ #endif
--#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
-+/* The number of bits used per block in the bbt on the device */
-+#define NAND_BBT_NRBITS_MSK 0x0000000F
-+#define NAND_BBT_1BIT 0x00000001
-+#define NAND_BBT_2BIT 0x00000002
-+#define NAND_BBT_4BIT 0x00000004
-+#define NAND_BBT_8BIT 0x00000008
-+/* The bad block table is in the last good block of the device */
-+#define NAND_BBT_LASTBLOCK 0x00000010
-+/* The bbt is at the given page, else we must scan for the bbt */
-+#define NAND_BBT_ABSPAGE 0x00000020
-+/* The bbt is at the given page, else we must scan for the bbt */
-+#define NAND_BBT_SEARCH 0x00000040
-+/* bbt is stored per chip on multichip devices */
-+#define NAND_BBT_PERCHIP 0x00000080
-+/* bbt has a version counter at offset veroffs */
-+#define NAND_BBT_VERSION 0x00000100
-+/* Create a bbt if none axists */
-+#define NAND_BBT_CREATE 0x00000200
-+/* Search good / bad pattern through all pages of a block */
-+#define NAND_BBT_SCANALLPAGES 0x00000400
-+/* Scan block empty during good / bad block scan */
-+#define NAND_BBT_SCANEMPTY 0x00000800
-+/* Write bbt if neccecary */
-+#define NAND_BBT_WRITE 0x00001000
-+/* Read and write back block contents when writing bbt */
-+#define NAND_BBT_SAVECONTENT 0x00002000
-+/* Search good / bad pattern on the first and the second page */
-+#define NAND_BBT_SCAN2NDPAGE 0x00004000
+-#define VERSION "1.1"
++#define VERSION "1.2"
--#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
-+/* The maximum number of blocks to scan for a bbt */
-+#define NAND_BBT_SCAN_MAXBLOCKS 4
+ static LIST_HEAD(bnep_session_list);
+ static DECLARE_RWSEM(bnep_session_sem);
+@@ -113,13 +113,28 @@
+ return bnep_send(s, &rsp, sizeof(rsp));
+ }
--#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
-+extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
-+extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
-+extern int nand_default_bbt (struct mtd_info *mtd);
-+extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
-+extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
-+extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-+ size_t * retlen, u_char * buf, u_char * oob_buf,
-+ struct nand_oobinfo *oobsel, int flags);
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++static inline void bnep_set_default_proto_filter(struct bnep_session *s)
++{
++ /* (IPv4, ARP) */
++ s->proto_filter[0].start = htons(0x0800);
++ s->proto_filter[0].end = htons(0x0806);
++ /* (RARP, AppleTalk) */
++ s->proto_filter[1].start = htons(0x8035);
++ s->proto_filter[1].end = htons(0x80F3);
++ /* (IPX, IPv6) */
++ s->proto_filter[2].start = htons(0x8137);
++ s->proto_filter[2].end = htons(0x86DD);
++}
++#endif
++
+ static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
+ {
+ int n;
--#define NAND_JFFS2_OOB8_FSDAPOS 6
--#define NAND_JFFS2_OOB16_FSDAPOS 8
--#define NAND_JFFS2_OOB8_FSDALEN 2
--#define NAND_JFFS2_OOB16_FSDALEN 8
-+/*
-+* Constants for oob configuration
-+*/
-+#define NAND_SMALL_BADBLOCK_POS 5
-+#define NAND_LARGE_BADBLOCK_POS 0
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned(data));
+ data++; len -= 2;
- #endif /* __LINUX_MTD_NAND_H */
---- linux-2.4.21/include/linux/mtd/nand_ecc.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nand_ecc.h
-@@ -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$
-+ * $Id$
- *
- * 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.
- */
+@@ -141,9 +156,13 @@
+ BT_DBG("proto filter start %d end %d",
+ f[i].start, f[i].end);
+ }
++
+ if (i < BNEP_MAX_PROTO_FILTERS)
+ memset(f + i, 0, sizeof(*f));
+
++ if (n == 0)
++ bnep_set_default_proto_filter(s);
++
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
+ } else {
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
+@@ -160,7 +179,7 @@
+
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned((u16 *) data));
+ data += 2; len -= 2;
+
+@@ -214,7 +233,7 @@
+ int err = 0;
+
+ data++; len--;
+-
++
+ switch (cmd) {
+ case BNEP_CMD_NOT_UNDERSTOOD:
+ case BNEP_SETUP_CONN_REQ:
+@@ -268,13 +287,13 @@
+ /* Unknown extension, skip it. */
+ break;
+ }
+-
++
+ if (!skb_pull(skb, h->len)) {
+ err = -EILSEQ;
+ break;
+ }
+ } while (!err && (h->type & BNEP_EXT_HEADER));
+-
++
+ return err;
+ }
+
+@@ -300,7 +319,7 @@
+
+ if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+ goto badframe;
+-
++
+ if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
+ bnep_rx_control(s, skb->data, skb->len);
+ kfree_skb(skb);
+@@ -326,7 +345,7 @@
+ goto badframe;
+ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+ }
+-
++
+ /* We have to alloc new skb and copy data here :(. Because original skb
+ * may not be modified and because of the alignment requirements. */
+ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
+@@ -342,7 +361,7 @@
+ case BNEP_COMPRESSED:
+ memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
+ break;
+-
++
+ case BNEP_COMPRESSED_SRC_ONLY:
+ memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
+ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+@@ -362,7 +381,7 @@
--/*
-- * 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__
+ memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+ kfree_skb(skb);
+-
+
-+struct mtd_info;
+ s->stats.rx_packets++;
+ nskb->dev = dev;
+ nskb->ip_summed = CHECKSUM_UNNECESSARY;
+@@ -426,9 +445,9 @@
+ send:
+ iv[il++] = (struct iovec) { skb->data, skb->len };
+ len += skb->len;
+-
++
+ /* FIXME: linearize skb */
+-
++
+ s->msg.msg_iov = iv;
+ s->msg.msg_iovlen = il;
+ len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
+@@ -453,16 +472,16 @@
- /*
- * Calculate 3 byte ECC code for 256 byte block
- */
--void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
-+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+ BT_DBG("");
- /*
- * 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__ */
---- linux-2.4.21/include/linux/mtd/nftl.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nftl.h
-@@ -1,81 +1,16 @@
--
--/* Defines for NAND Flash Translation Layer */
--/* (c) 1999 Machine Vision Holdings, Inc. */
--/* Author: David Woodhouse <dwmw2@mvhi.com> */
--/* $Id$ */
-+/*
-+ * $Id$
-+ *
-+ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
-+ */
+- daemonize(); reparent_to_init();
++ daemonize(); reparent_to_init();
- #ifndef __MTD_NFTL_H__
- #define __MTD_NFTL_H__
+- sprintf(current->comm, "kbnepd %s", dev->name);
+-
+- sigfillset(¤t->blocked);
++ sprintf(current->comm, "kbnepd %s", dev->name);
++
++ sigfillset(¤t->blocked);
+ flush_signals(current);
--#ifndef __BOOT__
- #include <linux/mtd/mtd.h>
--#endif
--
--/* Block Control Information */
--
--struct nftl_bci {
-- unsigned char ECCSig[6];
-- __u8 Status;
-- __u8 Status1;
--}__attribute__((packed));
--
--/* Unit Control Information */
--
--struct nftl_uci0 {
-- __u16 VirtUnitNum;
-- __u16 ReplUnitNum;
-- __u16 SpareVirtUnitNum;
-- __u16 SpareReplUnitNum;
--} __attribute__((packed));
--
--struct nftl_uci1 {
-- __u32 WearInfo;
-- __u16 EraseMark;
-- __u16 EraseMark1;
--} __attribute__((packed));
--
--struct nftl_uci2 {
-- __u16 FoldMark;
-- __u16 FoldMark1;
-- __u32 unused;
--} __attribute__((packed));
--
--union nftl_uci {
-- struct nftl_uci0 a;
-- struct nftl_uci1 b;
-- struct nftl_uci2 c;
--};
--
--struct nftl_oob {
-- struct nftl_bci b;
-- union nftl_uci u;
--};
--
--/* NFTL Media Header */
--
--struct NFTLMediaHeader {
-- char DataOrgID[6];
-- __u16 NumEraseUnits;
-- __u16 FirstPhysicalEUN;
-- __u32 FormattedSize;
-- unsigned char UnitSizeFactor;
--} __attribute__((packed));
--
--#define MAX_ERASE_ZONES (8192 - 512)
--
--#define ERASE_MARK 0x3c69
--#define SECTOR_FREE 0xff
--#define SECTOR_USED 0x55
--#define SECTOR_IGNORE 0x11
--#define SECTOR_DELETED 0x00
--
--#define FOLD_MARK_IN_PROGRESS 0x5555
--
--#define ZONE_GOOD 0xff
--#define ZONE_BAD_ORIGINAL 0
--#define ZONE_BAD_MARKED 7
-+#include <linux/mtd/blktrans.h>
+ current->nice = -15;
--#ifdef __KERNEL__
-+#include <mtd/nftl-user.h>
+- set_fs(KERNEL_DS);
++ set_fs(KERNEL_DS);
- /* these info are used in ReplUnitTable */
- #define BLOCK_NIL 0xffff /* last block of a chain */
-@@ -84,8 +19,7 @@
- #define BLOCK_RESERVED 0xfffc /* bios block or bad block */
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(sk->sleep, &wait);
+@@ -477,13 +496,13 @@
- struct NFTLrecord {
-- struct mtd_info *mtd;
-- struct semaphore mutex;
-+ struct mtd_blktrans_dev mbd;
- __u16 MediaUnit, SpareMediaUnit;
- __u32 EraseSize;
- struct NFTLMediaHeader MediaHdr;
-@@ -97,13 +31,13 @@
- __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 */
- unsigned int nb_blocks; /* number of physical blocks */
- unsigned int nb_boot_blocks; /* number of blocks used by the bios */
- struct erase_info instr;
-+ struct nand_oobinfo oobinfo;
- };
+ if (sk->state != BT_CONNECTED)
+ break;
+-
++
+ // TX
+ while ((skb = skb_dequeue(&sk->write_queue)))
+ if (bnep_tx_frame(s, skb))
+ break;
+ netif_wake_queue(dev);
+-
++
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+@@ -547,28 +566,19 @@
+ s->sock = sock;
+ s->role = req->role;
+ s->state = BT_CONNECTED;
+-
++
+ s->msg.msg_flags = MSG_NOSIGNAL;
- int NFTL_mount(struct NFTLrecord *s);
-@@ -114,9 +48,7 @@
+ #ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+ /* Set default mc filter */
+ set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter);
#endif
+-
++
+ #ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+ /* Set default protocol filter */
+-
+- /* (IPv4, ARP) */
+- s->proto_filter[0].start = htons(0x0800);
+- s->proto_filter[0].end = htons(0x0806);
+- /* (RARP, AppleTalk) */
+- s->proto_filter[1].start = htons(0x8035);
+- s->proto_filter[1].end = htons(0x80F3);
+- /* (IPX, IPv6) */
+- s->proto_filter[2].start = htons(0x8137);
+- s->proto_filter[2].end = htons(0x86DD);
++ bnep_set_default_proto_filter(s);
+ #endif
+-
++
+ dev->init = bnep_net_init;
+ dev->priv = s;
+ err = register_netdev(dev);
+@@ -577,7 +587,7 @@
+ }
- #define MAX_NFTLS 16
--#define MAX_SECTORS_PER_UNIT 32
-+#define MAX_SECTORS_PER_UNIT 64
- #define NFTL_PARTN_BITS 4
-
--#endif /* __KERNEL__ */
--
- #endif /* __MTD_NFTL_H__ */
---- linux-2.4.21/include/linux/mtd/partitions.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/partitions.h
-@@ -5,7 +5,7 @@
- *
- * This code is GPL
- *
-- * $Id$
-+ * $Id$
- */
+ __bnep_link_session(s);
+-
++
+ err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (err < 0) {
+ /* Session thread start failed, gotta cleanup. */
+@@ -680,6 +690,8 @@
- #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 */
- };
+ static int __init bnep_init_module(void)
+ {
++ l2cap_load();
++
+ bnep_crc32_init();
+ bnep_sock_init();
-@@ -49,8 +50,26 @@
- #define MTDPART_SIZ_FULL (0)
+--- linux-2.4.21/net/bluetooth/bnep/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/sock.c
+@@ -55,36 +55,6 @@
+ #define BT_DBG( A... )
+ #endif
+-static inline struct socket *socki_lookup(struct inode *inode)
+-{
+- return &inode->u.socket_i;
+-}
+-
+-static struct socket *sockfd_lookup(int fd, int *err)
+-{
+- struct file *file;
+- struct inode *inode;
+- struct socket *sock;
+-
+- if (!(file = fget(fd))) {
+- *err = -EBADF;
+- return NULL;
+- }
+-
+- inode = file->f_dentry->d_inode;
+- if (!inode->i_sock || !(sock = socki_lookup(inode))) {
+- *err = -ENOTSOCK;
+- fput(file);
+- return NULL;
+- }
+-
+- if (sock->file != file) {
+- printk(KERN_ERR "socki_lookup: socket file changed!\n");
+- sock->file = file;
+- }
+- return sock;
+-}
+-
+ static int bnep_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
+@@ -124,8 +94,10 @@
+ if (!nsock)
+ return err;
--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 *);
+- if (nsock->sk->state != BT_CONNECTED)
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
+ return -EBADFD;
++ }
-+/*
-+ * Functions dealing with the various ways of partitioning the space
-+ */
+ err = bnep_add_connection(&ca, nsock);
+ if (!err) {
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Config.in
+@@ -0,0 +1,7 @@
++#
++# Bluetooth CMTP layer configuration
++#
++
++if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then
++ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
++fi
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth CMTP layer
++#
++
++O_TARGET := cmtp.o
++
++obj-y := core.o sock.o capi.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/capi.c
+@@ -0,0 +1,707 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <linux/capi.h>
++
++#include "../drivers/isdn/avmb1/capilli.h"
++#include "../drivers/isdn/avmb1/capicmd.h"
++#include "../drivers/isdn/avmb1/capiutil.h"
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define REVISION "1.0"
++
++#define CAPI_INTEROPERABILITY 0x20
++
++#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
++#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
++#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
++#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
++
++#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
++#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
++
++#define CAPI_FUNCTION_REGISTER 0
++#define CAPI_FUNCTION_RELEASE 1
++#define CAPI_FUNCTION_GET_PROFILE 2
++#define CAPI_FUNCTION_GET_MANUFACTURER 3
++#define CAPI_FUNCTION_GET_VERSION 4
++#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
++#define CAPI_FUNCTION_MANUFACTURER 6
++#define CAPI_FUNCTION_LOOPBACK 7
++
++static struct capi_driver_interface *di;
++
++
++#define CMTP_MSGNUM 1
++#define CMTP_APPLID 2
++#define CMTP_MAPPING 3
++
++static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
++{
++ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
++
++ BT_DBG("session %p application %p appl %d", session, app, appl);
++
++ if (!app)
++ return NULL;
++
++ memset(app, 0, sizeof(*app));
++
++ app->state = BT_OPEN;
++ app->appl = appl;
++
++ list_add_tail(&app->list, &session->applications);
++
++ return app;
++}
++
++static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
++{
++ BT_DBG("session %p application %p", session, app);
++
++ if (app) {
++ list_del(&app->list);
++ kfree(app);
++ }
++}
++
++static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
++{
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ switch (pattern) {
++ case CMTP_MSGNUM:
++ if (app->msgnum == value)
++ return app;
++ break;
++ case CMTP_APPLID:
++ if (app->appl == value)
++ return app;
++ break;
++ case CMTP_MAPPING:
++ if (app->mapping == value)
++ return app;
++ break;
++ }
++ }
++
++ return NULL;
++}
++
++static int cmtp_msgnum_get(struct cmtp_session *session)
++{
++ session->msgnum++;
++
++ if ((session->msgnum & 0xff) > 200)
++ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
++
++ return session->msgnum;
++}
++
++
++static void cmtp_send_interopmsg(struct cmtp_session *session,
++ __u8 subcmd, __u16 appl, __u16 msgnum,
++ __u16 function, unsigned char *buf, int len)
++{
++ struct sk_buff *skb;
++ unsigned char *s;
++
++ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
++
++ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for interoperability packet");
++ return;
++ }
++
++ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
++
++ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
++ capimsg_setu16(s, 2, appl);
++ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
++ capimsg_setu8 (s, 5, subcmd);
++ capimsg_setu16(s, 6, msgnum);
++
++ /* Interoperability selector (Bluetooth Device Management) */
++ capimsg_setu16(s, 8, 0x0001);
++
++ capimsg_setu8 (s, 10, 3 + len);
++ capimsg_setu16(s, 11, function);
++ capimsg_setu8 (s, 13, len);
++
++ if (len > 0)
++ memcpy(s + 14, buf, len);
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 appl, msgnum, func, info;
++ __u32 controller;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
++ case CAPI_CONF:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
++ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
++
++ switch (func) {
++ case CAPI_FUNCTION_REGISTER:
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
++ if (application) {
++ application->state = BT_CONNECTED;
++ application->msgnum = 0;
++ application->mapping = CAPIMSG_APPID(skb->data);
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_RELEASE:
++ appl = CAPIMSG_APPID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ application->state = BT_CLOSED;
++ application->msgnum = 0;
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_PROFILE:
++ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
++ session->ncontroller = controller;
++ wake_up_interruptible(&session->wait);
++ break;
++ }
++
++ if (!info && ctrl) {
++ memcpy(&ctrl->profile,
++ skb->data + CAPI_MSG_BASELEN + 11,
++ sizeof(capi_profile));
++ session->state = BT_CONNECTED;
++ ctrl->ready(ctrl);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_MANUFACTURER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
++
++ if (!info && ctrl) {
++ strncpy(ctrl->manu,
++ skb->data + CAPI_MSG_BASELEN + 15,
++ skb->data[CAPI_MSG_BASELEN + 14]);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_VERSION:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
++ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
++ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
++ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
++ strncpy(ctrl->serial,
++ skb->data + CAPI_MSG_BASELEN + 17,
++ skb->data[CAPI_MSG_BASELEN + 16]);
++ }
++
++ break;
++ }
++
++ break;
++
++ case CAPI_IND:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
++
++ if (func == CAPI_FUNCTION_LOOPBACK) {
++ appl = CAPIMSG_APPID(skb->data);
++ msgnum = CAPIMSG_MSGID(skb->data);
++ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
++ skb->data + CAPI_MSG_BASELEN + 6,
++ skb->data[CAPI_MSG_BASELEN + 5]);
++ }
++
++ break;
++ }
++
++ kfree_skb(skb);
++}
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 cmd, appl, info;
++ __u32 ncci, contr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
++ cmtp_recv_interopmsg(session, skb);
++ return;
++ }
++
++ if (session->flags & (1 << CMTP_LOOPBACK)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ appl = application->appl;
++ CAPIMSG_SETAPPID(skb->data, appl);
++ } else {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ if ((contr & 0x7f) == 0x01) {
++ contr = (contr & 0xffffff80) | session->num;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ if (!ctrl) {
++ BT_ERR("Can't find controller %d for message", session->num);
++ kfree_skb(skb);
++ return;
++ }
++
++ switch (cmd) {
++ case CAPI_CONNECT_B3_CONF:
++ ncci = CAPIMSG_NCCI(skb->data);
++ info = CAPIMSG_U16(skb->data, 12);
++
++ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
++
++ if (info == 0)
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_CONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
++
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_DISCONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
++
++ if (ncci == 0xffffffff)
++ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ ctrl->free_ncci(ctrl, appl, ncci);
++ break;
++
++ default:
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++ }
++}
++
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ scb->id = -1;
++ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
++
++ skb_queue_tail(&session->transmit, skb);
++
++ cmtp_schedule(session);
++}
++
++
++static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
++{
++ BT_DBG("ctrl %p data %p", ctrl, data);
++
++ return -EIO;
++}
++
++static void cmtp_reset_ctr(struct capi_ctr *ctrl)
++{
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->reseted(ctrl);
++}
++
++static void cmtp_remove_ctr(struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->suspend_output(ctrl);
++
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++}
++
++static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[8];
++ int err = 0, nconn, want = rp->level3cnt;
++
++ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
++ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
++
++ application = cmtp_application_add(session, appl);
++ if (!application) {
++ BT_ERR("Can't allocate memory for new application");
++ ctrl->appl_released(ctrl, appl);
++ return;
++ }
++
++ if (want < 0)
++ nconn = ctrl->profile.nbchannel * -want;
++ else
++ nconn = want;
++
++ if (nconn == 0)
++ nconn = ctrl->profile.nbchannel;
++
++ capimsg_setu16(buf, 0, nconn);
++ capimsg_setu16(buf, 2, rp->datablkcnt);
++ capimsg_setu16(buf, 4, rp->datablklen);
++
++ application->state = BT_CONFIG;
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
++ CAPI_FUNCTION_REGISTER, buf, 6);
++
++ add_wait_queue(&session->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (application->state == BT_CLOSED) {
++ err = -application->err;
++ break;
++ }
++
++ if (application->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ if (err) {
++ ctrl->appl_released(ctrl, appl);
++ cmtp_application_del(session, application);
++ return;
++ }
++
++ ctrl->appl_registered(ctrl, appl);
++}
++
++static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++
++ BT_DBG("ctrl %p appl %d", ctrl, appl);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if (!application) {
++ BT_ERR("Can't find application");
++ return;
++ }
++
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
++ CAPI_FUNCTION_RELEASE, NULL, 0);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (application->state == BT_CLOSED)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ cmtp_application_del(session, application);
++ ctrl->appl_released(ctrl, appl);
++}
++
++static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ __u16 appl;
++ __u32 contr;
++
++ BT_DBG("ctrl %p skb %p", ctrl, skb);
++
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if ((!application) || (application->state != BT_CONNECTED)) {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ CAPIMSG_SETAPPID(skb->data, application->mapping);
++
++ if ((contr & 0x7f) == session->num) {
++ contr = (contr & 0xffffff80) | 0x01;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static char *cmtp_procinfo(struct capi_ctr *ctrl)
++{
++ return "CAPI Message Transport Protocol";
++}
++
++static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++ int len = 0;
++
++ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
++ len += sprintf(page + len, "addr %s\n", session->name);
++ len += sprintf(page + len, "ctrl %d\n", session->num);
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
++ }
++
++ if (off + count >= len)
++ *eof = 1;
++
++ if (len < off)
++ return 0;
++
++ *start = page + off;
++
++ return ((count < len - off) ? count : len - off);
++}
++
++static struct capi_driver cmtp_driver = {
++ name: "cmtp",
++ revision: REVISION,
++ load_firmware: cmtp_load_firmware,
++ reset_ctr: cmtp_reset_ctr,
++ remove_ctr: cmtp_remove_ctr,
++ register_appl: cmtp_register_appl,
++ release_appl: cmtp_release_appl,
++ send_message: cmtp_send_message,
++ procinfo: cmtp_procinfo,
++ ctr_read_proc: cmtp_ctr_read_proc,
++
++ driver_read_proc: 0,
++ add_card: 0,
++};
++
++
++int cmtp_attach_device(struct cmtp_session *session)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[4];
++
++ BT_DBG("session %p", session);
++
++ capimsg_setu32(buf, 0, 0);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (session->ncontroller)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
+
-+struct mtd_part_parser {
++ if (!timeo)
++ return -ETIMEDOUT;
++
++ if (!session->ncontroller)
++ return -ENODEV;
++
++
++ if (session->ncontroller > 1)
++ BT_INFO("Setting up only CAPI controller 1");
++
++ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
++ BT_ERR("Can't attach new controller");
++ return -EBUSY;
++ }
++
++ session->num = session->ctrl->cnr;
++
++ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
++
++ capimsg_setu32(buf, 0, 1);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_VERSION, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ return 0;
++}
++
++void cmtp_detach_device(struct cmtp_session *session)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++
++ BT_DBG("session %p ctrl %p", session, ctrl);
++
++ if (!ctrl)
++ return;
++
++ ctrl->reseted(ctrl);
++
++ di->detach_ctr(ctrl);
++}
++
++int cmtp_init_capi(void)
++{
++ if (!(di = attach_capi_driver(&cmtp_driver))) {
++ BT_ERR("Can't attach CAPI driver");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_capi(void)
++{
++ detach_capi_driver(&cmtp_driver);
++}
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/cmtp.h
+@@ -0,0 +1,138 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __CMTP_H
++#define __CMTP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#define BTNAMSIZ 18
++
++/* CMTP ioctl defines */
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define CMTP_LOOPBACK 0
++
++struct cmtp_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++};
++
++struct cmtp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct cmtp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ int num;
++};
++
++struct cmtp_connlist_req {
++ __u32 cnum;
++ struct cmtp_conninfo *ci;
++};
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
++int cmtp_del_connection(struct cmtp_conndel_req *req);
++int cmtp_get_connlist(struct cmtp_connlist_req *req);
++int cmtp_get_conninfo(struct cmtp_conninfo *ci);
++
++/* CMTP session defines */
++#define CMTP_INTEROP_TIMEOUT (HZ * 5)
++#define CMTP_INITIAL_MSGNUM 0xff00
++
++struct cmtp_session {
+ struct list_head list;
-+ struct module *owner;
-+ const char *name;
-+ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
++
++ struct socket *sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++
++ uint mtu;
++
++ char name[BTNAMSIZ];
++
++ atomic_t terminate;
++
++ wait_queue_head_t wait;
++
++ int ncontroller;
++ int num;
++ struct capi_ctr *ctrl;
++
++ struct list_head applications;
++
++ unsigned long blockids;
++ int msgnum;
++
++ struct sk_buff_head transmit;
++
++ struct sk_buff *reassembly[16];
+};
+
-+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);
++struct cmtp_application {
++ struct list_head list;
+
-+#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
++ unsigned long state;
++ int err;
+
- #endif
-
++ __u16 appl;
++ __u16 mapping;
++
++ __u16 msgnum;
++};
++
++struct cmtp_scb {
++ int id;
++ int data;
++};
++
++int cmtp_attach_device(struct cmtp_session *session);
++void cmtp_detach_device(struct cmtp_session *session);
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++
++static inline void cmtp_schedule(struct cmtp_session *session)
++{
++ struct sock *sk = session->sock->sk;
++
++ wake_up_interruptible(sk->sleep);
++}
++
++/* CMTP init defines */
++int cmtp_init_capi(void);
++int cmtp_init_sockets(void);
++void cmtp_cleanup_capi(void);
++void cmtp_cleanup_sockets(void);
++
++#endif /* __CMTP_H */
--- /dev/null
-+++ linux-2.4.21/include/linux/mtd/physmap.h
-@@ -0,0 +1,61 @@
-+/*
-+ * For boards with physically mapped flash and using
-+ * drivers/mtd/maps/physmap.c mapping driver.
-+ *
-+ * $Id$
-+ *
-+ * 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.
-+ *
-+ */
++++ linux-2.4.21/net/bluetooth/cmtp/core.c
+@@ -0,0 +1,515 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
-+#ifndef __LINUX_MTD_PHYSMAP__
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
+
+#include <linux/config.h>
++#include <linux/module.h>
+
-+#if defined(CONFIG_MTD_PHYSMAP)
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
+
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/map.h>
-+#include <linux/mtd/partitions.h>
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
+
-+/*
-+ * 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;
++#include "cmtp.h"
+
-+/*
-+ * Board needs to specify the exact mapping during their setup time.
-+ */
-+static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(cmtp_session_sem);
++static LIST_HEAD(cmtp_session_list);
++
++static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
+{
-+ physmap_map.phys = addr;
-+ physmap_map.size = size;
-+ physmap_map.bankwidth = bankwidth;
-+ physmap_map.set_vpp = set_vpp;
++ struct cmtp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &cmtp_session_list) {
++ session = list_entry(p, struct cmtp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
+}
+
-+#if defined(CONFIG_MTD_PARTITIONS)
++static void __cmtp_link_session(struct cmtp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &cmtp_session_list);
++}
+
-+/*
-+ * 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);
++static void __cmtp_unlink_session(struct cmtp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
+
-+#endif /* defined(CONFIG_MTD_PARTITIONS) */
-+#endif /* defined(CONFIG_MTD) */
++static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
+
-+#endif /* __LINUX_MTD_PHYSMAP__ */
++ ci->flags = session->flags;
++ ci->state = session->state;
+
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/plat-ram.h
-@@ -0,0 +1,35 @@
-+/* linux/include/mtd/plat-ram.h
-+ *
-+ * (c) 2004 Simtec Electronics
-+ * http://www.simtec.co.uk/products/SWLINUX/
-+ * Ben Dooks <ben@simtec.co.uk>
-+ *
-+ * Generic platform device based RAM map
-+ *
-+ * $Id$
-+ *
-+ * 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.
-+ *
-+ */
++ ci->num = session->num;
++}
+
-+#ifndef __LINUX_MTD_PLATRAM_H
-+#define __LINUX_MTD_PLATRAM_H __FILE__
+
-+#define PLATRAM_RO (0)
-+#define PLATRAM_RW (1)
++static inline int cmtp_alloc_block_id(struct cmtp_session *session)
++{
++ int i, id = -1;
+
-+struct platdata_mtd_ram {
-+ char *mapname;
-+ char **probes;
-+ struct mtd_partition *partitions;
-+ int nr_partitions;
-+ int bankwidth;
++ for (i = 0; i < 16; i++)
++ if (!test_and_set_bit(i, &session->blockids)) {
++ id = i;
++ break;
++ }
+
-+ /* control callbacks */
++ return id;
++}
+
-+ void (*set_rw)(struct device *dev, int to);
-+};
++static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
++{
++ clear_bit(id, &session->blockids);
++}
+
-+#endif /* __LINUX_MTD_PLATRAM_H */
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/xip.h
-@@ -0,0 +1,107 @@
-+/*
-+ * MTD primitives for XIP support
-+ *
-+ * Author: Nicolas Pitre
-+ * Created: Nov 2, 2004
-+ * Copyright: (C) 2004 MontaVista Software, Inc.
-+ *
-+ * This XIP support for MTD has been loosely inspired
-+ * by an earlier patch authored by David Woodhouse.
-+ *
-+ * 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$
-+ */
++static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
++{
++ struct sk_buff *skb = session->reassembly[id], *nskb;
++ int size;
+
-+#ifndef __LINUX_MTD_XIP_H__
-+#define __LINUX_MTD_XIP_H__
++ BT_DBG("session %p buf %p count %d", session, buf, count);
+
-+#include <linux/config.h>
++ size = (skb) ? skb->len + count : count;
+
-+#ifdef CONFIG_MTD_XIP
++ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for CAPI message");
++ return;
++ }
+
-+/*
-+ * Function that are modifying the flash state away from array mode must
-+ * obviously not be running from flash. The __xipram is therefore marking
-+ * those functions so they get relocated to ram.
-+ */
-+#define __xipram __attribute__ ((__section__ (".data")))
++ if (skb && (skb->len > 0))
++ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+
-+/*
-+ * We really don't want gcc to guess anything.
-+ * We absolutely _need_ proper inlining.
-+ */
-+#include <linux/compiler.h>
++ memcpy(skb_put(nskb, count), buf, count);
+
-+/*
-+ * Each architecture has to provide the following macros. They must access
-+ * the hardware directly and not rely on any other (XIP) functions since they
-+ * won't be available when used (flash not in array mode).
-+ *
-+ * xip_irqpending()
-+ *
-+ * return non zero when any hardware interrupt is pending.
-+ *
-+ * xip_currtime()
-+ *
-+ * return a platform specific time reference to be used with
-+ * xip_elapsed_since().
-+ *
-+ * xip_elapsed_since(x)
-+ *
-+ * return in usecs the elapsed timebetween now and the reference x as
-+ * returned by xip_currtime().
-+ *
-+ * note 1: convertion to usec can be approximated, as long as the
-+ * returned value is <= the real elapsed time.
-+ * note 2: this should be able to cope with a few seconds without
-+ * overflowing.
-+ */
++ session->reassembly[id] = nskb;
+
-+#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
++ if (skb)
++ kfree_skb(skb);
++}
+
-+#include <asm/hardware.h>
-+#ifdef CONFIG_ARCH_PXA
-+#include <asm/arch/pxa-regs.h>
-+#endif
++static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr, hdrlen, id;
++ __u16 len;
+
-+#define xip_irqpending() (ICIP & ICMR)
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
-+/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
-+#define xip_currtime() (OSCR)
-+#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
++ while (skb->len > 0) {
++ hdr = skb->data[0];
+
-+#else
++ switch (hdr & 0xc0) {
++ case 0x40:
++ hdrlen = 2;
++ len = skb->data[1];
++ break;
++ case 0x80:
++ hdrlen = 3;
++ len = skb->data[1] | (skb->data[2] << 8);
++ break;
++ default:
++ hdrlen = 1;
++ len = 0;
++ break;
++ }
+
-+#warning "missing IRQ and timer primitives for XIP MTD support"
-+#warning "some of the XIP MTD support code will be disabled"
-+#warning "your system will therefore be unresponsive when writing or erasing flash"
++ id = (hdr & 0x3c) >> 2;
+
-+#define xip_irqpending() (0)
-+#define xip_currtime() (0)
-+#define xip_elapsed_since(x) (0)
++ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
+
-+#endif
++ if (hdrlen + len > skb->len) {
++ BT_ERR("Wrong size or header information in CMTP frame");
++ break;
++ }
+
-+/*
-+ * xip_cpu_idle() is used when waiting for a delay equal or larger than
-+ * the system timer tick period. This should put the CPU into idle mode
-+ * to save power and to be woken up only when some interrupts are pending.
-+ * As above, this should not rely upon standard kernel code.
-+ */
++ if (len == 0) {
++ skb_pull(skb, hdrlen);
++ continue;
++ }
+
-+#if defined(CONFIG_CPU_XSCALE)
-+#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
-+#else
-+#define xip_cpu_idle() do { } while (0)
-+#endif
++ switch (hdr & 0x03) {
++ case 0x00:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ cmtp_recv_capimsg(session, session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ case 0x01:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ break;
++ default:
++ if (session->reassembly[id] != NULL)
++ kfree_skb(session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ }
+
-+#else
++ skb_pull(skb, hdrlen + len);
++ }
+
-+#define __xipram
++ kfree_skb(skb);
++ return 0;
++}
+
-+#endif /* CONFIG_MTD_XIP */
++static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
++{
++ struct socket *sock = session->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
+
-+#endif /* __LINUX_MTD_XIP_H__ */
---- /dev/null
-+++ linux-2.4.21/include/linux/pm-devices.h
-@@ -0,0 +1,41 @@
-+#ifndef _LINUX_PM_DEV_H
-+#define _LINUX_PM_DEV_H
++ BT_DBG("session %p data %p len %d", session, data, len);
+
-+/*
-+ * Copyright 2002 Montavista Software (mlocke@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, 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.
-+ */
++ if (!len)
++ return 0;
+
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
+
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
+
-+/*
-+ * Device types
-+ */
-+enum
++static int cmtp_process_transmit(struct cmtp_session *session)
+{
-+ PM_UNKNOWN_DEV = 0, /* generic */
-+ PM_SYS_DEV, /* system device (fan, KB controller, ...) */
-+ PM_PCI_DEV, /* PCI device */
-+ PM_USB_DEV, /* USB device */
-+ PM_SCSI_DEV, /* SCSI device */
-+ PM_ISA_DEV, /* ISA device */
-+ PM_MTD_DEV, /* Memory Technology Device */
-+ PM_TPANEL_DEV, /* Memory Technology Device */
-+ PM_STORAGE_DEV, /* Memory Technology Device */
-+ PM_NETWORK_DEV, /* Memory Technology Device */
-+ PM_PCMCIA_DEV, /* Memory Technology Device */
-+ PM_DISPLAY_DEV, /* Memory Technology Device */
-+ PM_SERIAL_DEV, /* Memory Technology Device */
-+ PM_BATTERY_DEV, /* Memory Technology Device */
-+};
++ struct sk_buff *skb, *nskb;
++ unsigned char *hdr;
++ unsigned int size, tail;
+
-+#endif
---- linux-2.4.21/include/linux/pm.h~pm
-+++ linux-2.4.21/include/linux/pm.h
-@@ -24,6 +24,7 @@
- #ifdef __KERNEL__
-
- #include <linux/config.h>
-+#include <linux/pm-devices.h>
- #include <linux/list.h>
-
- /*
-@@ -50,20 +51,6 @@
-
- typedef int pm_request_t;
-
--/*
-- * Device types
-- */
--enum
--{
-- PM_UNKNOWN_DEV = 0, /* generic */
-- PM_SYS_DEV, /* system device (fan, KB controller, ...) */
-- PM_PCI_DEV, /* PCI device */
-- PM_USB_DEV, /* USB device */
-- PM_SCSI_DEV, /* SCSI device */
-- PM_ISA_DEV, /* ISA device */
-- PM_MTD_DEV, /* Memory Technology Device */
--};
--
- typedef int pm_dev_t;
-
- /*
---- /dev/null
-+++ linux-2.4.21/include/linux/rbtree-24.h
-@@ -0,0 +1,133 @@
-+/*
-+ Red Black Trees
-+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
-+
-+ 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.
++ BT_DBG("session %p", session);
+
-+ 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.
++ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ while ((skb = skb_dequeue(&session->transmit))) {
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ if ((tail = (session->mtu - nskb->len)) < 5) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ tail = session->mtu;
++ }
++
++ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
++
++ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
++ skb_queue_head(&session->transmit, skb);
++ break;
++ }
++
++ if (size < 256) {
++ hdr = skb_put(nskb, 2);
++ hdr[0] = 0x40
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size;
++ } else {
++ hdr = skb_put(nskb, 3);
++ hdr[0] = 0x80
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size & 0xff;
++ hdr[2] = size >> 8;
++ }
++
++ memcpy(skb_put(nskb, size), skb->data, size);
++ skb_pull(skb, size);
++
++ if (skb->len > 0) {
++ skb_queue_head(&session->transmit, skb);
++ } else {
++ cmtp_free_block_id(session, scb->id);
++ if (scb->data) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ }
++ kfree_skb(skb);
++ }
++ }
++
++ cmtp_send_frame(session, nskb->data, nskb->len);
++
++ kfree_skb(nskb);
++
++ return skb_queue_len(&session->transmit);
++}
++
++static int cmtp_session(void *arg)
++{
++ struct cmtp_session *session = arg;
++ struct sock *sk = session->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("session %p", session);
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
++
++ sigfillset(¤t->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ cmtp_recv_frame(session, skb);
++ }
++
++ cmtp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ down_write(&cmtp_session_sem);
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK)))
++ cmtp_detach_device(session);
++
++ fput(session->sock->file);
++
++ __cmtp_unlink_session(session);
++
++ up_write(&cmtp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
++{
++ struct cmtp_session *session, *s;
++ bdaddr_t src, dst;
++ int i, err;
++
++ BT_DBG("");
++
++ baswap(&src, &bluez_pi(sock->sk)->src);
++ baswap(&dst, &bluez_pi(sock->sk)->dst);
++
++ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct cmtp_session));
++
++ down_write(&cmtp_session_sem);
++
++ s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
++
++ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
++
++ BT_DBG("mtu %d", session->mtu);
++
++ sprintf(session->name, "%s", batostr(&dst));
++
++ session->sock = sock;
++ session->state = BT_CONFIG;
++
++ init_waitqueue_head(&session->wait);
++
++ session->ctrl = NULL;
++ session->msgnum = CMTP_INITIAL_MSGNUM;
++
++ INIT_LIST_HEAD(&session->applications);
++
++ skb_queue_head_init(&session->transmit);
++
++ for (i = 0; i < 16; i++)
++ session->reassembly[i] = NULL;
++
++ session->flags = req->flags;
++
++ __cmtp_link_session(session);
++
++ err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
++ err = cmtp_attach_device(session);
++ if (err < 0)
++ goto detach;
++ }
++
++ up_write(&cmtp_session_sem);
++ return 0;
++
++detach:
++ cmtp_detach_device(session);
++
++unlink:
++ __cmtp_unlink_session(session);
++
++failed:
++ up_write(&cmtp_session_sem);
++ kfree(session);
++ return err;
++}
++
++int cmtp_del_connection(struct cmtp_conndel_req *req)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&req->bdaddr);
++ if (session) {
++ /* Flush the transmit queue */
++ skb_queue_purge(&session->transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++ } else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_connlist(struct cmtp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
+
-+ 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
++ down_read(&cmtp_session_sem);
+
-+ linux/include/linux/rbtree.h
++ list_for_each(p, &cmtp_session_list) {
++ struct cmtp_session *session;
++ struct cmtp_conninfo ci;
+
-+ 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...
++ session = list_entry(p, struct cmtp_session, list);
+
-+ 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.
++ __cmtp_copy_session(session, &ci);
+
-+-----------------------------------------------------------------------
-+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;
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
+
-+ while (n)
-+ {
-+ page = rb_entry(n, struct page, rb_page_cache);
++ if (++n >= req->cnum)
++ break;
+
-+ if (offset < page->offset)
-+ n = n->rb_left;
-+ else if (offset > page->offset)
-+ n = n->rb_right;
-+ else
-+ return page;
++ req->ci++;
+ }
-+ return NULL;
++ req->cnum = n;
++
++ up_read(&cmtp_session_sem);
++ return err;
+}
+
-+static inline struct page * __rb_insert_page_cache(struct inode * inode,
-+ unsigned long offset,
-+ rb_node_t * node)
++int cmtp_get_conninfo(struct cmtp_conninfo *ci)
+{
-+ rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
-+ rb_node_t * parent = NULL;
-+ struct page * page;
++ struct cmtp_session *session;
++ int err = 0;
+
-+ while (*p)
-+ {
-+ parent = *p;
-+ page = rb_entry(parent, struct page, rb_page_cache);
++ down_read(&cmtp_session_sem);
+
-+ if (offset < page->offset)
-+ p = &(*p)->rb_left;
-+ else if (offset > page->offset)
-+ p = &(*p)->rb_right;
-+ else
-+ return page;
-+ }
++ session = __cmtp_get_session(&ci->bdaddr);
++ if (session)
++ __cmtp_copy_session(session, ci);
++ else
++ err = -ENOENT;
+
-+ rb_link_node(node, parent, p);
++ up_read(&cmtp_session_sem);
++ return err;
++}
+
-+ return NULL;
++
++int __init init_cmtp(void)
++{
++ l2cap_load();
++
++ cmtp_init_capi();
++ cmtp_init_sockets();
++
++ BT_INFO("BlueZ CMTP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
+}
+
-+static inline struct page * rb_insert_page_cache(struct inode * inode,
-+ unsigned long offset,
-+ rb_node_t * node)
++void __exit exit_cmtp(void)
+{
-+ 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;
++ cmtp_cleanup_sockets();
++ cmtp_cleanup_capi();
+}
-+-----------------------------------------------------------------------
++
++module_init(init_cmtp);
++module_exit(exit_cmtp);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/sock.c
+@@ -0,0 +1,208 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
+*/
+
-+#ifndef _LINUX_RBTREE_H
-+#define _LINUX_RBTREE_H
++#include <linux/config.h>
++#include <linux/module.h>
+
++#include <linux/types.h>
++#include <linux/errno.h>
+#include <linux/kernel.h>
-+#include <linux/stddef.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
+
-+typedef struct rb_node_s
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int cmtp_sock_release(struct socket *sock)
+{
-+ 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;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
+}
-+rb_node_t;
+
-+typedef struct rb_root_s
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
-+ struct rb_node_s * rb_node;
-+}
-+rb_root_t;
++ struct cmtp_connadd_req ca;
++ struct cmtp_conndel_req cd;
++ struct cmtp_connlist_req cl;
++ struct cmtp_conninfo ci;
++ struct socket *nsock;
++ int err;
+
-+#define RB_ROOT (rb_root_t) { NULL, }
-+#define rb_entry(ptr, type, member) \
-+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++ BT_DBG("cmd %x arg %lx", cmd, arg);
+
-+extern void rb_insert_color(rb_node_t *, rb_root_t *);
-+extern void rb_erase(rb_node_t *, rb_root_t *);
++ switch (cmd) {
++ case CMTPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
-+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;
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
+
-+ *rb_link = node;
-+}
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
+
-+#endif /* _LINUX_RBTREE_H */
---- linux-2.4.21/include/linux/rbtree.h~mtd-cvs
-+++ linux-2.4.21/include/linux/rbtree.h
-@@ -1,133 +1,25 @@
- /*
-- Red Black Trees
-- (C) 1999 Andrea Arcangeli <andrea@suse.de>
--
-- 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 <linux/kernel.h>
--#include <linux/stddef.h>
--
--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;
-+ * 2.5 compatibility
-+ * $Id$
-+ */
-
--typedef struct rb_root_s
--{
-- struct rb_node_s * rb_node;
--}
--rb_root_t;
-+#ifndef __MTD_COMPAT_RBTREE_H__
-+#define __MTD_COMPAT_RBTREE_H__
-
--#define RB_ROOT (rb_root_t) { NULL, }
--#define rb_entry(ptr, type, member) \
-- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-+#include <linux/version.h>
-
--extern void rb_insert_color(rb_node_t *, rb_root_t *);
--extern void rb_erase(rb_node_t *, rb_root_t *);
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
-+#include_next <linux/rbtree.h>
-+#else
-+#define rb_node_s rb_node
-+#define rb_root_s rb_root
-
--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;
-+#include <linux/rbtree-24.h>
-
-- *rb_link = node;
--}
-+/* 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
-
--#endif /* _LINUX_RBTREE_H */
-+#endif /* __MTD_COMPAT_RBTREE_H__ */
---- /dev/null
-+++ linux-2.4.21/include/linux/rslib.h
-@@ -0,0 +1,105 @@
-+/*
-+ * include/linux/rslib.h
-+ *
-+ * Overview:
-+ * Generic Reed Solomon encoder / decoder library
-+ *
-+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * RS code lifted from reed solomon library written by Phil Karn
-+ * Copyright 2002 Phil Karn, KA9Q
-+ *
-+ * $Id$
-+ *
-+ * 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.
-+ */
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
+
-+#ifndef _RSLIB_H_
-+#define _RSLIB_H_
++ err = cmtp_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
+
-+#include <linux/list.h>
++ return err;
+
-+/**
-+ * struct rs_control - rs control structure
-+ *
-+ * @mm: Bits per symbol
-+ * @nn: Symbols per block (= (1<<mm)-1)
-+ * @alpha_to: log lookup table
-+ * @index_of: Antilog lookup table
-+ * @genpoly: Generator polynomial
-+ * @nroots: Number of generator roots = number of parity symbols
-+ * @fcr: First consecutive root, index form
-+ * @prim: Primitive element, index form
-+ * @iprim: prim-th root of 1, index form
-+ * @gfpoly: The primitive generator polynominal
-+ * @users: Users of this structure
-+ * @list: List entry for the rs control list
-+*/
-+struct rs_control {
-+ int mm;
-+ int nn;
-+ uint16_t *alpha_to;
-+ uint16_t *index_of;
-+ uint16_t *genpoly;
-+ int nroots;
-+ int fcr;
-+ int prim;
-+ int iprim;
-+ int gfpoly;
-+ int users;
-+ struct list_head list;
-+};
++ case CMTPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
-+/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit */
-+#ifdef CONFIG_REED_SOLOMON_ENC8
-+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
-+ uint16_t invmsk);
-+#endif
-+#ifdef CONFIG_REED_SOLOMON_DEC8
-+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
-+ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+ uint16_t *corr);
-+#endif
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
+
-+/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit */
-+#ifdef CONFIG_REED_SOLOMON_ENC16
-+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
-+ uint16_t invmsk);
-+#endif
-+#ifdef CONFIG_REED_SOLOMON_DEC16
-+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
-+ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+ uint16_t *corr);
-+#endif
++ return cmtp_del_connection(&cd);
+
-+/* Create or get a matching rs control structure */
-+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
-+ int nroots);
++ case CMTPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
+
-+/* Release a rs control structure */
-+void free_rs(struct rs_control *rs);
++ if (cl.cnum <= 0)
++ return -EINVAL;
+
-+/** modulo replacement for galois field arithmetics
-+ *
-+ * @rs: the rs control structure
-+ * @x: the value to reduce
-+ *
-+ * where
-+ * rs->mm = number of bits per symbol
-+ * rs->nn = (2^rs->mm) - 1
-+ *
-+ * Simple arithmetic modulo would return a wrong result for values
-+ * >= 3 * rs->nn
-+*/
-+static inline int rs_modnn(struct rs_control *rs, int x)
-+{
-+ while (x >= rs->nn) {
-+ x -= rs->nn;
-+ x = (x >> rs->mm) + (x & rs->nn);
++ err = cmtp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case CMTPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = cmtp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
+ }
-+ return x;
++
++ return -EINVAL;
+}
+
-+#endif
---- linux-2.4.21/include/linux/soundcard.h~ucb1x00
-+++ linux-2.4.21/include/linux/soundcard.h
-@@ -811,6 +811,7 @@
- #define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
- #define SOUND_MIXER_OUTSRC 0xfa /* Arg contains a bit for each input source to output */
- #define SOUND_MIXER_OUTMASK 0xf9 /* Arg contains a bit for each supported input source to output */
-+#define SOUND_MIXER_AC97 0xf8 /* directly access ac97 registers */
-
- /* Device mask bits */
-
-@@ -874,6 +875,7 @@
- #define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
- #define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
- #define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
-+#define SOUND_MIXER_READ_AC97 MIXER_READ(SOUND_MIXER_AC97)
-
- #define MIXER_WRITE(dev) _SIOWR('M', dev, int)
- #define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
-@@ -900,6 +902,7 @@
- #define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
-
- #define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
-+#define SOUND_MIXER_WRITE_AC97 MIXER_WRITE(SOUND_MIXER_AC97)
-
- typedef struct mixer_info
- {
---- /dev/null
-+++ linux-2.4.21/include/linux/suspend.h
-@@ -0,0 +1,10 @@
-+/* $Id$ */
++static struct proto_ops cmtp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: cmtp_sock_release,
++ ioctl: cmtp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
+
-+#ifndef __MTD_COMPAT_VERSION_H__
-+#include <linux/version.h>
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-+#include_next <linux/suspend.h>
-+#endif
++ BT_DBG("sock %p", sock);
+
-+#endif /* __MTD_COMPAT_VERSION_H__ */
---- linux-2.4.21/include/linux/tty.h~ramses-lcd
-+++ linux-2.4.21/include/linux/tty.h
-@@ -10,8 +10,8 @@
- * resizing).
- */
- #define MIN_NR_CONSOLES 1 /* must be at least 1 */
--#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */
--#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */
-+#define MAX_NR_CONSOLES 3 /* serial lines start at 64 */
-+#define MAX_NR_USER_CONSOLES 3 /* must be root to allocate above this */
- /* Note: the ioctl VT_GETSTATE does not work for
- consoles 16 and higher (since it returns a short) */
-
---- linux-2.4.21/include/linux/usb.h~ramses-usb
-+++ linux-2.4.21/include/linux/usb.h
-@@ -1079,7 +1079,7 @@
- void usb_show_string(struct usb_device *dev, char *id, int index);
-
- #ifdef DEBUG
--#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
-+#define dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
- #else
- #define dbg(format, arg...) do {} while (0)
- #endif
---- linux-2.4.21/include/linux/wireless.h~linux-iw241_we16-6
-+++ linux-2.4.21/include/linux/wireless.h
-@@ -1,7 +1,7 @@
- /*
- * This file define a set of standard wireless extensions
- *
-- * Version : 15 12.7.02
-+ * Version : 16 2.4.03
- *
- * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
-@@ -69,6 +69,8 @@
-
- /***************************** INCLUDES *****************************/
-
-+/* To minimise problems in user space, I might remove those headers
-+ * at some point. Jean II */
- #include <linux/types.h> /* for "caddr_t" et al */
- #include <linux/socket.h> /* for "struct sockaddr" et al */
- #include <linux/if.h> /* for IFNAMSIZ and co... */
-@@ -80,7 +82,7 @@
- * (there is some stuff that will be added in the future...)
- * I just plan to increment with each new version.
- */
--#define WIRELESS_EXT 15
-+#define WIRELESS_EXT 16
-
- /*
- * Changes :
-@@ -163,6 +165,16 @@
- * - Add IW_TXPOW_RANGE for range of Tx Powers
- * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
- * - Add IW_MODE_MONITOR for passive monitor
-+ *
-+ * V15 to V16
-+ * ----------
-+ * - Increase the number of bitrates in iw_range to 32 (for 802.11g)
-+ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
-+ * - Reshuffle struct iw_range for increases, add filler
-+ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
-+ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
-+ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
-+ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
- */
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &cmtp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family cmtp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: cmtp_sock_create
++};
++
++int cmtp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
++ BT_ERR("Can't register CMTP socket layer (%d)", err);
++ return err;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
++ BT_ERR("Can't unregister CMTP socket layer (%d)", err);
++
++ return;
++}
+--- linux-2.4.21/net/bluetooth/hci_core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_core.c
+@@ -218,6 +218,10 @@
- /**************************** CONSTANTS ****************************/
-@@ -196,9 +208,11 @@
- /* SIOCGIWSTATS is strictly used between user space and the kernel, and
- * is never passed to the driver (i.e. the driver will never see it). */
+ /* Mandatory initialization */
--/* Mobile IP support (statistics per MAC address) */
-+/* Spy support (statistics per MAC address - used for Mobile IP support) */
- #define SIOCSIWSPY 0x8B10 /* set spy addresses */
- #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
-+#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */
-+#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */
++ /* Reset */
++ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
++
+ /* Read Local Supported Features */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
- /* Access Point manipulation */
- #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
-@@ -294,7 +308,7 @@
- #define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
- #define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
+@@ -395,7 +399,7 @@
+ {
+ struct hci_inquiry_req ir;
+ struct hci_dev *hdev;
+- int err = 0, do_inquiry = 0;
++ int err = 0, do_inquiry = 0, max_rsp;
+ long timeo;
+ __u8 *buf, *ptr;
+
+@@ -408,6 +412,7 @@
+
+ hci_dev_lock_bh(hdev);
+ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
++ inquiry_cache_empty(hdev) ||
+ ir.flags & IREQ_CACHE_FLUSH) {
+ inquiry_cache_flush(hdev);
+ do_inquiry = 1;
+@@ -418,16 +423,19 @@
+ if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+ goto done;
+
++ /* for unlimited number of responses we will use buffer with 255 entries */
++ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;
++
+ /* cache_dump can't sleep. Therefore we allocate temp buffer and then
+ * copy it to the user space.
+ */
+- if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
++ if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ hci_dev_lock_bh(hdev);
+- ir.num_rsp = inquiry_cache_dump(hdev, ir.num_rsp, buf);
++ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
+ hci_dev_unlock_bh(hdev);
+
+ BT_DBG("num_rsp %d", ir.num_rsp);
+@@ -708,22 +716,20 @@
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
--#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */
-+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+ return -EINVAL;
+-
+- size = dev_num * sizeof(*dr) + sizeof(*dl);
- #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+- if (verify_area(VERIFY_WRITE, (void *) arg, size))
+- return -EFAULT;
++ size = sizeof(*dl) + dev_num * sizeof(*dr);
-@@ -306,13 +320,13 @@
- /* ----------------------- OTHER CONSTANTS ----------------------- */
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
++
+ dr = dl->dev_req;
- /* Maximum frequencies in the range struct */
--#define IW_MAX_FREQUENCIES 16
-+#define IW_MAX_FREQUENCIES 32
- /* Note : if you have something like 80 frequencies,
- * don't increase this constant and don't fill the frequency list.
- * The user will be able to set by channel anyway... */
+ read_lock_bh(&hdev_list_lock);
+@@ -738,12 +744,12 @@
+ read_unlock_bh(&hdev_list_lock);
- /* Maximum bit rates in the range struct */
--#define IW_MAX_BITRATES 8
-+#define IW_MAX_BITRATES 32
+ dl->dev_num = n;
+- size = n * sizeof(*dr) + sizeof(*dl);
++ size = sizeof(*dl) + n * sizeof(*dr);
- /* Maximum tx powers in the range struct */
- #define IW_MAX_TXPOWER 8
-@@ -320,8 +334,7 @@
- * a few of them in the struct iw_range. */
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
- /* Maximum of address that you may set with SPY */
--#define IW_MAX_SPY 8 /* set */
--#define IW_MAX_GET_SPY 64 /* get */
-+#define IW_MAX_SPY 8
+- return 0;
++ return err ? -EFAULT : 0;
+ }
- /* Maximum of address that you may get in the
- list of access points in range */
-@@ -354,7 +367,8 @@
- #define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
- #define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
- #define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
--#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
-+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
-+#define IW_ENCODE_TEMP 0x0400 /* Temporary key */
+ int hci_get_dev_info(unsigned long arg)
+--- linux-2.4.21/net/bluetooth/hci_event.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_event.c
+@@ -62,9 +62,22 @@
+ /* Command Complete OGF LINK_CTL */
+ static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+ {
++ __u8 status;
++
+ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
- /* Power management flags available (along with the value, if any) */
- #define IW_POWER_ON 0x0000 /* No details... */
-@@ -482,6 +496,17 @@
- __u32 beacon; /* Missed beacons/superframe */
- };
+ switch (ocf) {
++ case OCF_INQUIRY_CANCEL:
++ status = *((__u8 *) skb->data);
++
++ if (status) {
++ BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status);
++ } else {
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
+ default:
+ BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+ break;
+@@ -307,7 +320,7 @@
+ status, batostr(&cc->bdaddr), conn);
+
+ if (status) {
+- if (conn) {
++ if (conn && conn->state == BT_CONNECT) {
+ conn->state = BT_CLOSED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_del(conn);
+@@ -439,6 +452,29 @@
+ hci_dev_unlock(hdev);
+ }
-+/*
-+ * Quality range (for spy threshold)
-+ */
-+struct iw_thrspy
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
-+ struct sockaddr addr; /* Source address (hw/mac) */
-+ struct iw_quality qual; /* Quality of the link */
-+ struct iw_quality low; /* Low threshold */
-+ struct iw_quality high; /* High threshold */
-+};
++ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
+
- /* ------------------------ WIRELESS STATS ------------------------ */
- /*
- * Wireless statistics (used for /proc/net/wireless)
-@@ -534,7 +559,7 @@
- struct iw_quality qual; /* Quality part of statistics */
-
- struct sockaddr ap_addr; /* Access point address */
-- struct sockaddr addr; /* Destination address (hw) */
-+ struct sockaddr addr; /* Destination address (hw/mac) */
-
- struct iw_param param; /* Other small parameters */
- struct iw_point data; /* Other large parameters */
-@@ -582,17 +607,31 @@
- __u32 min_nwid; /* Minimal NWID we are able to set */
- __u32 max_nwid; /* Maximal NWID we are able to set */
-
-- /* Frequency */
-- __u16 num_channels; /* Number of channels [0; num - 1] */
-- __u8 num_frequency; /* Number of entry in the list */
-- struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
-- /* Note : this frequency list doesn't need to fit channel numbers */
-+ /* Old Frequency (backward compat - moved lower ) */
-+ __u16 old_num_channels;
-+ __u8 old_num_frequency;
-+ /* Filler to keep "version" at the same offset */
-+ __s32 old_freq[6];
-
- /* signal level threshold range */
- __s32 sensitivity;
-
- /* Quality of link & SNR stuff */
-+ /* Quality range (link, level, noise)
-+ * If the quality is absolute, it will be in the range [0 ; max_qual],
-+ * if the quality is dBm, it will be in the range [max_qual ; 0].
-+ * Don't forget that we use 8 bit arithmetics... */
- struct iw_quality max_qual; /* Quality of the link */
-+ /* This should contain the average/typical values of the quality
-+ * indicator. This should be the threshold between a "good" and
-+ * a "bad" link (example : monitor going from green to orange).
-+ * Currently, user space apps like quality monitors don't have any
-+ * way to calibrate the measurement. With this, they can split
-+ * the range between 0 and max_qual in different quality level
-+ * (using a geometric subdivision centered on the average).
-+ * I expect that people doing the user space apps will feedback
-+ * us on which value we need to put in each driver... */
-+ struct iw_quality avg_qual; /* Quality of the link */
-
- /* Rates */
- __u8 num_bitrates; /* Number of entries in the list */
-@@ -619,6 +658,8 @@
- __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
- __u8 num_encoding_sizes; /* Number of entry in the list */
- __u8 max_encoding_tokens; /* Max number of tokens */
-+ /* For drivers that need a "login/passwd" form */
-+ __u8 encoding_login_index; /* token index for login token */
-
- /* Transmit power */
- __u16 txpower_capa; /* What options are supported */
-@@ -638,18 +679,12 @@
- __s32 min_r_time; /* Minimal retry lifetime */
- __s32 max_r_time; /* Maximal retry lifetime */
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--) {
++ inquiry_info tmp;
++ bacpy(&tmp.bdaddr, &info->bdaddr);
++ tmp.pscan_rep_mode = info->pscan_rep_mode;
++ tmp.pscan_period_mode = info->pscan_period_mode;
++ tmp.pscan_mode = 0x00;
++ memcpy(tmp.dev_class, &info->dev_class, 3);
++ tmp.clock_offset = info->clock_offset;
++ info++;
++ inquiry_cache_update(hdev, &tmp);
++ }
++ hci_dev_unlock(hdev);
++}
++
+ /* Connect Request */
+ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+@@ -735,6 +771,10 @@
+ hci_inquiry_result_evt(hdev, skb);
+ break;
-- /* Average quality of link & SNR */
-- struct iw_quality avg_qual; /* Quality of the link */
-- /* This should contain the average/typical values of the quality
-- * indicator. This should be the threshold between a "good" and
-- * a "bad" link (example : monitor going from green to orange).
-- * Currently, user space apps like quality monitors don't have any
-- * way to calibrate the measurement. With this, they can split
-- * the range between 0 and max_qual in different quality level
-- * (using a geometric subdivision centered on the average).
-- * I expect that people doing the user space apps will feedback
-- * us on which value we need to put in each driver...
-- */
-+ /* Frequency */
-+ __u16 num_channels; /* Number of channels [0; num - 1] */
-+ __u8 num_frequency; /* Number of entry in the list */
-+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
-+ /* Note : this frequency list doesn't need to fit channel numbers,
-+ * because each entry contain its channel index */
++ case EVT_INQUIRY_RESULT_WITH_RSSI:
++ hci_inquiry_result_with_rssi_evt(hdev, skb);
++ break;
++
+ case EVT_CONN_REQUEST:
+ hci_conn_request_evt(hdev, skb);
+ break;
+--- linux-2.4.21/net/bluetooth/hci_sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_sock.c
+@@ -66,20 +66,20 @@
+ /* Packet types */
+ 0x10,
+ /* Events */
+- { 0xd9fe, 0x0 },
++ { 0x1000d9fe, 0x0000300c },
+ /* Commands */
+ {
+ { 0x0 },
+ /* OGF_LINK_CTL */
+- { 0x2a000002, 0x0, 0x0, 0x0 },
++ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
+ /* OGF_LINK_POLICY */
+- { 0x1200, 0x0, 0x0, 0x0 },
++ { 0x00005200, 0x00000000, 0x0000, 0x00 },
+ /* OGF_HOST_CTL */
+- { 0x80100000, 0x2a, 0x0, 0x0 },
++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
+ /* OGF_INFO_PARAM */
+- { 0x22a, 0x0, 0x0, 0x0 },
++ { 0x000002be, 0x00000000, 0x0000, 0x00 },
+ /* OGF_STATUS_PARAM */
+- { 0x2e, 0x0, 0x0, 0x0 }
++ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
+ }
};
- /*
--- /dev/null
-+++ linux-2.4.21/include/linux/workqueue.h
-@@ -0,0 +1,21 @@
-+/*
-+ * 2.5 compatibility
-+ * $Id$
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/Config.in
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
+
-+#ifndef __MTD_COMPAT_WORKQUEUE_H__
-+#define __MTD_COMPAT_WORKQUEUE_H__
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
+
-+#include <linux/version.h>
++O_TARGET := hidp.o
+
-+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
-+#include_next <linux/workqueue.h>
-+#else
-+#include <linux/tqueue.h>
-+#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
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
+
-+#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
++include $(TOPDIR)/Rules.make
--- /dev/null
-+++ linux-2.4.21/include/mtd/inftl-user.h
-@@ -0,0 +1,91 @@
-+/*
-+ * $Id$
-+ *
-+ * Parts of INFTL headers shared with userspace
-+ *
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/core.c
+@@ -0,0 +1,655 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
-+#ifndef __MTD_INFTL_USER_H__
-+#define __MTD_INFTL_USER_H__
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
+
-+#define OSAK_VERSION 0x5120
-+#define PERCENTUSED 98
++#include <linux/config.h>
++#include <linux/module.h>
+
-+#define SECTORSIZE 512
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
+
-+/* Block Control Information */
++#include <linux/input.h>
+
-+struct inftl_bci {
-+ uint8_t ECCsig[6];
-+ uint8_t Status;
-+ uint8_t Status1;
-+} __attribute__((packed));
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
+
-+struct inftl_unithead1 {
-+ uint16_t virtualUnitNo;
-+ uint16_t prevUnitNo;
-+ uint8_t ANAC;
-+ uint8_t NACs;
-+ uint8_t parityPerField;
-+ uint8_t discarded;
-+} __attribute__((packed));
++#include "hidp.h"
+
-+struct inftl_unithead2 {
-+ uint8_t parityPerField;
-+ uint8_t ANAC;
-+ uint16_t prevUnitNo;
-+ uint16_t virtualUnitNo;
-+ uint8_t NACs;
-+ uint8_t discarded;
-+} __attribute__((packed));
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
+
-+struct inftl_unittail {
-+ uint8_t Reserved[4];
-+ uint16_t EraseMark;
-+ uint16_t EraseMark1;
-+} __attribute__((packed));
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(hidp_session_sem);
++static LIST_HEAD(hidp_session_list);
++
++static unsigned char hidp_keycode[256] = {
++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
++ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
++ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
++ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
++ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
++ 122,123, 90, 91, 85, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
++ 150,158,159,128,136,177,178,176,142,152,173,140
++};
++
++static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
++{
++ struct hidp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &hidp_session_list) {
++ session = list_entry(p, struct hidp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __hidp_link_session(struct hidp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &hidp_session_list);
++}
++
++static void __hidp_unlink_session(struct hidp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->vendor = 0x0000;
++ ci->product = 0x0000;
++ ci->version = 0x0000;
++ memset(ci->name, 0, 128);
++
++ if (session->input) {
++ ci->vendor = session->input->idvendor;
++ ci->product = session->input->idproduct;
++ ci->version = session->input->idversion;
++ if (session->input->name)
++ strncpy(ci->name, session->input->name, 128);
++ else
++ strncpy(ci->name, "HID Boot Device", 128);
++ }
++}
++
++static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct hidp_session *session = dev->private;
++ struct sk_buff *skb;
++ unsigned char newleds;
++
++ BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
++
++ if (type != EV_LED)
++ return -1;
++
++ newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
++ (!!test_bit(LED_COMPOSE, dev->led) << 3) |
++ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
++ (!!test_bit(LED_CAPSL, dev->led) << 1) |
++ (!!test_bit(LED_NUML, dev->led));
++
++ if (session->leds == newleds)
++ return 0;
++
++ session->leds = newleds;
++
++ if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ *skb_put(skb, 1) = 0xa2;
++ *skb_put(skb, 1) = 0x01;
++ *skb_put(skb, 1) = newleds;
++
++ skb_queue_tail(&session->intr_transmit, skb);
++
++ hidp_schedule(session);
++
++ return 0;
++}
++
++static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
++{
++ struct input_dev *dev = session->input;
++ unsigned char *keys = session->keys;
++ unsigned char *udata = skb->data + 1;
++ signed char *sdata = skb->data + 1;
++ int i, size = skb->len - 1;
++
++ switch (skb->data[0]) {
++ case 0x01: /* Keyboard report */
++ for (i = 0; i < 8; i++)
++ input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
++
++ for (i = 2; i < 8; i++) {
++ if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
++ if (hidp_keycode[keys[i]])
++ input_report_key(dev, hidp_keycode[keys[i]], 0);
++ else
++ BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
++ }
++
++ if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
++ if (hidp_keycode[udata[i]])
++ input_report_key(dev, hidp_keycode[udata[i]], 1);
++ else
++ BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
++ }
++ }
++
++ memcpy(keys, udata, 8);
++ break;
++
++ case 0x02: /* Mouse report */
++ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
++ input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
++ input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
++ input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
++ input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
++
++ input_report_rel(dev, REL_X, sdata[1]);
++ input_report_rel(dev, REL_Y, sdata[2]);
++
++ if (size > 3)
++ input_report_rel(dev, REL_WHEEL, sdata[3]);
++ break;
++ }
++
++ input_event(dev, EV_RST, 0, 0);
++}
++
++static void hidp_idle_timeout(unsigned long arg)
++{
++ struct hidp_session *session = (struct hidp_session *) arg;
++
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++}
++
++static inline void hidp_set_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ mod_timer(&session->timer, jiffies + HZ * session->idle_to);
++}
++
++static inline void hidp_del_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ del_timer(&session->timer);
++}
++
++static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for message");
++ return;
++ }
++
++ *skb_put(skb, 1) = hdr;
++
++ skb_queue_tail(&session->ctrl_transmit, skb);
++
++ hidp_schedule(session);
++}
++
++static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ hdr = skb->data[0];
++ skb_pull(skb, 1);
++
++ if (hdr == 0xa1) {
++ hidp_set_timer(session);
++
++ if (session->input)
++ hidp_input_report(session, skb);
++ } else {
++ BT_DBG("Unsupported protocol header 0x%02x", hdr);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
++{
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++
++ BT_DBG("sock %p data %p len %d", sock, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ return sock_sendmsg(sock, &msg, len);
++}
++
++static int hidp_process_transmit(struct hidp_session *session)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
++ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->ctrl_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ while ((skb = skb_dequeue(&session->intr_transmit))) {
++ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->intr_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ return skb_queue_len(&session->ctrl_transmit) +
++ skb_queue_len(&session->intr_transmit);
++}
++
++static int hidp_session(void *arg)
++{
++ struct hidp_session *session = arg;
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++ struct sk_buff *skb;
++ int vendor = 0x0000, product = 0x0000;
++ wait_queue_t ctrl_wait, intr_wait;
++ unsigned long timeo = HZ;
++
++ BT_DBG("session %p", session);
++
++ if (session->input) {
++ vendor = session->input->idvendor;
++ product = session->input->idproduct;
++ }
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
++
++ sigfillset(¤t->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&ctrl_wait, current);
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ hidp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++
++ down_write(&hidp_session_sem);
++
++ hidp_del_timer(session);
++
++ if (intr_sk->state != BT_CONNECTED) {
++ init_waitqueue_entry(&ctrl_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ while (timeo && ctrl_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ timeo = HZ;
++ }
++
++ fput(session->ctrl_sock->file);
++
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (timeo && intr_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++
++ fput(session->intr_sock->file);
++
++ __hidp_unlink_session(session);
++
++ if (session->input) {
++ input_unregister_device(session->input);
++ kfree(session->input);
++ }
++
++ up_write(&hidp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
++{
++ struct input_dev *input = session->input;
++ int i;
++
++ input->private = session;
++
++ input->idbus = BUS_BLUETOOTH;
++ input->idvendor = req->vendor;
++ input->idproduct = req->product;
++ input->idversion = req->version;
++
++ if (req->subclass & 0x40) {
++ set_bit(EV_KEY, input->evbit);
++ set_bit(EV_LED, input->evbit);
++ set_bit(EV_REP, input->evbit);
++
++ set_bit(LED_NUML, input->ledbit);
++ set_bit(LED_CAPSL, input->ledbit);
++ set_bit(LED_SCROLLL, input->ledbit);
++ set_bit(LED_COMPOSE, input->ledbit);
++ set_bit(LED_KANA, input->ledbit);
++
++ for (i = 0; i < sizeof(hidp_keycode); i++)
++ set_bit(hidp_keycode[i], input->keybit);
++ clear_bit(0, input->keybit);
++ }
++
++ if (req->subclass & 0x80) {
++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
++ input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
++ input->relbit[0] |= BIT(REL_WHEEL);
++ }
++
++ input->event = hidp_input_event;
++
++ input_register_device(input);
++}
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
++{
++ struct hidp_session *session, *s;
++ int err;
++
++ BT_DBG("");
++
++ if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
++ bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
++ return -ENOTUNIQ;
++
++ session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct hidp_session));
++
++ session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!session->input) {
++ kfree(session);
++ return -ENOMEM;
++ }
++ memset(session->input, 0, sizeof(struct input_dev));
++
++ down_write(&hidp_session_sem);
++
++ s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
++
++ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
++ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
++
++ BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
++
++ session->ctrl_sock = ctrl_sock;
++ session->intr_sock = intr_sock;
++ session->state = BT_CONNECTED;
++
++ init_timer(&session->timer);
++
++ session->timer.function = hidp_idle_timeout;
++ session->timer.data = (unsigned long) session;
++
++ skb_queue_head_init(&session->ctrl_transmit);
++ skb_queue_head_init(&session->intr_transmit);
++
++ session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
++ session->idle_to = req->idle_to;
++
++ if (session->input)
++ hidp_setup_input(session, req);
++
++ __hidp_link_session(session);
++
++ hidp_set_timer(session);
++
++ err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (session->input) {
++ hidp_send_message(session, 0x70);
++ session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
++
++ session->leds = 0xff;
++ hidp_input_event(session->input, EV_LED, 0, 0);
++ }
++
++ up_write(&hidp_session_sem);
++ return 0;
++
++unlink:
++ hidp_del_timer(session);
++
++ __hidp_unlink_session(session);
++
++ if (session->input)
++ input_unregister_device(session->input);
++
++failed:
++ up_write(&hidp_session_sem);
++
++ if (session->input)
++ kfree(session->input);
++
++ kfree(session);
++ return err;
++}
++
++int hidp_del_connection(struct hidp_conndel_req *req)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&req->bdaddr);
++ if (session) {
++ if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
++ hidp_send_message(session, 0x15);
++ } else {
++ /* Flush the transmit queues */
++ skb_queue_purge(&session->ctrl_transmit);
++ skb_queue_purge(&session->intr_transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++ }
++ } else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_connlist(struct hidp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
+
-+union inftl_uci {
-+ struct inftl_unithead1 a;
-+ struct inftl_unithead2 b;
-+ struct inftl_unittail c;
-+};
++ down_read(&hidp_session_sem);
+
-+struct inftl_oob {
-+ struct inftl_bci b;
-+ union inftl_uci u;
-+};
++ list_for_each(p, &hidp_session_list) {
++ struct hidp_session *session;
++ struct hidp_conninfo ci;
+
++ session = list_entry(p, struct hidp_session, list);
+
-+/* INFTL Media Header */
++ __hidp_copy_session(session, &ci);
+
-+struct INFTLPartition {
-+ __u32 virtualUnits;
-+ __u32 firstUnit;
-+ __u32 lastUnit;
-+ __u32 flags;
-+ __u32 spareUnits;
-+ __u32 Reserved0;
-+ __u32 Reserved1;
-+} __attribute__((packed));
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
+
-+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));
++ if (++n >= req->cnum)
++ break;
+
-+/* Partition flag types */
-+#define INFTL_BINARY 0x20000000
-+#define INFTL_BDTL 0x40000000
-+#define INFTL_LAST 0x80000000
++ req->ci++;
++ }
++ req->cnum = n;
+
-+#endif /* __MTD_INFTL_USER_H__ */
++ up_read(&hidp_session_sem);
++ return err;
++}
+
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++ struct hidp_session *session;
++ int err = 0;
+
---- /dev/null
-+++ linux-2.4.21/include/mtd/jffs2-user.h
-@@ -0,0 +1,35 @@
-+/*
-+ * $Id$
-+ *
-+ * JFFS2 definitions for use in user space only
-+ */
++ down_read(&hidp_session_sem);
+
-+#ifndef __JFFS2_USER_H__
-+#define __JFFS2_USER_H__
++ session = __hidp_get_session(&ci->bdaddr);
++ if (session)
++ __hidp_copy_session(session, ci);
++ else
++ err = -ENOENT;
+
-+/* This file is blessed for inclusion by userspace */
-+#include <linux/jffs2.h>
-+#include <endian.h>
-+#include <byteswap.h>
++ up_read(&hidp_session_sem);
++ return err;
++}
+
-+#undef cpu_to_je16
-+#undef cpu_to_je32
-+#undef cpu_to_jemode
-+#undef je16_to_cpu
-+#undef je32_to_cpu
-+#undef jemode_to_cpu
++static int __init hidp_init(void)
++{
++ l2cap_load();
+
-+extern int target_endian;
++ hidp_init_sockets();
+
-+#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
-+#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
++ BT_INFO("BlueZ HIDP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
+
-+#define cpu_to_je16(x) ((jint16_t){t16(x)})
-+#define cpu_to_je32(x) ((jint32_t){t32(x)})
-+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
++ return 0;
++}
+
-+#define je16_to_cpu(x) (t16((x).v16))
-+#define je32_to_cpu(x) (t32((x).v32))
-+#define jemode_to_cpu(x) (t32((x).m))
++static void __exit hidp_exit(void)
++{
++ hidp_cleanup_sockets();
++}
+
-+#endif /* __JFFS2_USER_H__ */
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
--- /dev/null
-+++ linux-2.4.21/include/mtd/mtd-abi.h
-@@ -0,0 +1,119 @@
-+/*
-+ * $Id$
-+ *
-+ * Portions of MTD ABI definition which are shared by kernel and user space
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/hidp.h
+@@ -0,0 +1,122 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
-+#ifndef __MTD_ABI_H__
-+#define __MTD_ABI_H__
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
+
-+#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
-+ separate files was to avoid #ifdef __KERNEL__ */
-+#define __user
-+#endif
++#ifndef __HIDP_H
++#define __HIDP_H
+
-+struct erase_info_user {
-+ uint32_t start;
-+ uint32_t length;
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++/* HIDP ioctl defines */
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
++#define HIDP_VIRTUAL_CABLE_UNPLUG 0
++#define HIDP_BOOT_PROTOCOL_MODE 1
++#define HIDP_BLUETOOTH_VENDOR_ID 9
++
++struct hidp_connadd_req {
++ int ctrl_sock; // Connected control socket
++ int intr_sock; // Connteted interrupt socket
++ __u16 parser;
++ __u16 rd_size;
++ __u8 *rd_data;
++ __u8 country;
++ __u8 subclass;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ __u32 flags;
++ __u32 idle_to;
++ char name[128];
+};
+
-+struct mtd_oob_buf {
-+ uint32_t start;
-+ uint32_t length;
-+ unsigned char __user *ptr;
++struct hidp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
+};
+
-+#define MTD_ABSENT 0
-+#define MTD_RAM 1
-+#define MTD_ROM 2
-+#define MTD_NORFLASH 3
-+#define MTD_NANDFLASH 4
-+#define MTD_PEROM 5
-+#define MTD_DATAFLASH 6
-+#define MTD_OTHER 14
-+#define MTD_UNKNOWN 15
++struct hidp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ char name[128];
++};
+
-+#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
-+#define MTD_SET_BITS 2 // Bits can be set
-+#define MTD_ERASEABLE 4 // Has an erase function
-+#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
-+#define MTD_VOLATILE 16 // Set for RAMs
-+#define MTD_XIP 32 // eXecute-In-Place possible
-+#define MTD_OOB 64 // Out-of-band data (NAND flash)
-+#define MTD_ECC 128 // Device capable of automatic ECC
-+#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed
++struct hidp_connlist_req {
++ __u32 cnum;
++ struct hidp_conninfo *ci;
++};
+
-+// Some common devices / combinations of capabilities
-+#define MTD_CAP_ROM 0
-+#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
-+#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
-+#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
-+#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
++int hidp_del_connection(struct hidp_conndel_req *req);
++int hidp_get_connlist(struct hidp_connlist_req *req);
++int hidp_get_conninfo(struct hidp_conninfo *ci);
+
++/* HIDP session defines */
++struct hidp_session {
++ struct list_head list;
+
-+// Types of automatic ECC/Checksum available
-+#define MTD_ECC_NONE 0 // No automatic ECC available
-+#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
-+#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
++ struct socket *ctrl_sock;
++ struct socket *intr_sock;
+
-+/* ECC byte placement */
-+#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
-+#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
-+#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
-+#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
++ bdaddr_t bdaddr;
+
-+/* OTP mode selection */
-+#define MTD_OTP_OFF 0
-+#define MTD_OTP_FACTORY 1
-+#define MTD_OTP_USER 2
++ unsigned long state;
++ unsigned long flags;
++ unsigned long idle_to;
+
-+struct mtd_info_user {
-+ uint8_t type;
-+ uint32_t flags;
-+ uint32_t size; // Total size of the MTD
-+ uint32_t erasesize;
-+ uint32_t oobblock; // Size of OOB blocks (e.g. 512)
-+ uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
-+ uint32_t ecctype;
-+ uint32_t eccsize;
-+};
++ uint ctrl_mtu;
++ uint intr_mtu;
+
-+struct region_info_user {
-+ uint32_t offset; /* At which this region starts,
-+ * from the beginning of the MTD */
-+ uint32_t erasesize; /* For this region */
-+ uint32_t numblocks; /* Number of blocks in this region */
-+ uint32_t regionindex;
-+};
++ atomic_t terminate;
+
-+struct otp_info {
-+ uint32_t start;
-+ uint32_t length;
-+ uint32_t locked;
-+};
++ unsigned char keys[8];
++ unsigned char leds;
+
-+#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
-+#define MEMERASE _IOW('M', 2, struct erase_info_user)
-+#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
-+#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
-+#define MEMLOCK _IOW('M', 5, struct erase_info_user)
-+#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 MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
-+#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
-+#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
-+#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
-+#define OTPSELECT _IOR('M', 13, int)
-+#define OTPGETREGIONCOUNT _IOW('M', 14, int)
-+#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
-+#define OTPLOCK _IOR('M', 16, struct otp_info)
++ struct input_dev *input;
+
-+struct nand_oobinfo {
-+ uint32_t useecc;
-+ uint32_t eccbytes;
-+ uint32_t oobfree[8][2];
-+ uint32_t eccpos[32];
++ struct timer_list timer;
++
++ struct sk_buff_head ctrl_transmit;
++ struct sk_buff_head intr_transmit;
+};
+
-+#endif /* __MTD_ABI_H__ */
++static inline void hidp_schedule(struct hidp_session *session)
++{
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++
++ wake_up_interruptible(ctrl_sk->sleep);
++ wake_up_interruptible(intr_sk->sleep);
++}
++
++/* HIDP init defines */
++extern int __init hidp_init_sockets(void);
++extern void __exit hidp_cleanup_sockets(void);
++
++#endif /* __HIDP_H */
--- /dev/null
-+++ linux-2.4.21/include/mtd/mtd-user.h
-@@ -0,0 +1,20 @@
-+/*
-+ * $Id$
-+ *
-+ * MTD ABI header for use by user space only.
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/sock.c
+@@ -0,0 +1,212 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
-+#ifndef __MTD_USER_H__
-+#define __MTD_USER_H__
++ 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;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
+
-+#include <stdint.h>
++#include <linux/config.h>
++#include <linux/module.h>
+
-+/* This file is blessed for inclusion by userspace */
-+#include <mtd/mtd-abi.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
+
-+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;
++#include "hidp.h"
+
-+#endif /* __MTD_USER_H__ */
---- /dev/null
-+++ linux-2.4.21/include/mtd/nftl-user.h
-@@ -0,0 +1,76 @@
-+/*
-+ * $Id$
-+ *
-+ * Parts of NFTL headers shared with userspace
-+ *
-+ */
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
+
-+#ifndef __MTD_NFTL_USER_H__
-+#define __MTD_NFTL_USER_H__
++static int hidp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
+
-+/* Block Control Information */
++ BT_DBG("sock %p sk %p", sock, sk);
+
-+struct nftl_bci {
-+ unsigned char ECCSig[6];
-+ uint8_t Status;
-+ uint8_t Status1;
-+}__attribute__((packed));
++ if (!sk)
++ return 0;
+
-+/* Unit Control Information */
++ sock_orphan(sk);
++ sock_put(sk);
+
-+struct nftl_uci0 {
-+ uint16_t VirtUnitNum;
-+ uint16_t ReplUnitNum;
-+ uint16_t SpareVirtUnitNum;
-+ uint16_t SpareReplUnitNum;
-+} __attribute__((packed));
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
+
-+struct nftl_uci1 {
-+ uint32_t WearInfo;
-+ uint16_t EraseMark;
-+ uint16_t EraseMark1;
-+} __attribute__((packed));
++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct hidp_connadd_req ca;
++ struct hidp_conndel_req cd;
++ struct hidp_connlist_req cl;
++ struct hidp_conninfo ci;
++ struct socket *csock;
++ struct socket *isock;
++ int err;
+
-+struct nftl_uci2 {
-+ uint16_t FoldMark;
-+ uint16_t FoldMark1;
-+ uint32_t unused;
-+} __attribute__((packed));
++ BT_DBG("cmd %x arg %lx", cmd, arg);
+
-+union nftl_uci {
-+ struct nftl_uci0 a;
-+ struct nftl_uci1 b;
-+ struct nftl_uci2 c;
-+};
++ switch (cmd) {
++ case HIDPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
-+struct nftl_oob {
-+ struct nftl_bci b;
-+ union nftl_uci u;
-+};
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
+
-+/* NFTL Media Header */
++ csock = sockfd_lookup(ca.ctrl_sock, &err);
++ if (!csock)
++ return err;
+
-+struct NFTLMediaHeader {
-+ char DataOrgID[6];
-+ uint16_t NumEraseUnits;
-+ uint16_t FirstPhysicalEUN;
-+ uint32_t FormattedSize;
-+ unsigned char UnitSizeFactor;
-+} __attribute__((packed));
++ isock = sockfd_lookup(ca.intr_sock, &err);
++ if (!isock) {
++ fput(csock->file);
++ return err;
++ }
+
-+#define MAX_ERASE_ZONES (8192 - 512)
++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++ fput(csock->file);
++ fput(isock->file);
++ return -EBADFD;
++ }
+
-+#define ERASE_MARK 0x3c69
-+#define SECTOR_FREE 0xff
-+#define SECTOR_USED 0x55
-+#define SECTOR_IGNORE 0x11
-+#define SECTOR_DELETED 0x00
++ err = hidp_add_connection(&ca, csock, isock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else {
++ fput(csock->file);
++ fput(isock->file);
++ }
+
-+#define FOLD_MARK_IN_PROGRESS 0x5555
++ return err;
+
-+#define ZONE_GOOD 0xff
-+#define ZONE_BAD_ORIGINAL 0
-+#define ZONE_BAD_MARKED 7
++ case HIDPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
+
-+#endif /* __MTD_NFTL_USER_H__ */
---- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
-+++ linux-2.4.21/include/net/iw_handler.h
-@@ -1,7 +1,7 @@
- /*
- * This file define the new driver API for Wireless Extensions
- *
-- * Version : 4 21.6.02
-+ * Version : 5 4.12.02
- *
- * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
-@@ -206,7 +206,7 @@
- * will be needed...
- * I just plan to increment with each new version.
- */
--#define IW_HANDLER_VERSION 4
-+#define IW_HANDLER_VERSION 5
-
- /*
- * Changes :
-@@ -220,10 +220,18 @@
- * V3 to V4
- * --------
- * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
-+ *
-+ * V4 to V5
-+ * --------
-+ * - Add new spy support : struct iw_spy_data & prototypes
- */
-
- /**************************** CONSTANTS ****************************/
-
-+/* Enable enhanced spy support. Disable to reduce footprint */
-+#define IW_WIRELESS_SPY
-+#define IW_WIRELESS_THRSPY
++ return hidp_del_connection(&cd);
+
- /* Special error message for the driver to indicate that we
- * should do a commit after return from the iw_handler */
- #define EIWCOMMIT EINPROGRESS
-@@ -315,6 +323,9 @@
- * We will automatically export that to user space... */
- struct iw_priv_args * private_args;
-
-+ /* Driver enhanced spy support */
-+ long spy_offset; /* Spy data offset */
++ case HIDPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
+
- /* In the long term, get_wireless_stats will move from
- * 'struct net_device' to here, to minimise bloat. */
- };
-@@ -350,6 +361,33 @@
-
- /* Need to think of short header translation table. Later. */
-
-+/* --------------------- ENHANCED SPY SUPPORT --------------------- */
-+/*
-+ * In the old days, the driver was handling spy support all by itself.
-+ * Now, the driver can delegate this task to Wireless Extensions.
-+ * It needs to include this struct in its private part and use the
-+ * standard spy iw_handler.
-+ */
++ if (cl.cnum <= 0)
++ return -EINVAL;
+
-+/*
-+ * Instance specific spy data, i.e. addresses spied and quality for them.
-+ */
-+struct iw_spy_data
-+{
-+#ifdef IW_WIRELESS_SPY
-+ /* --- Standard spy support --- */
-+ int spy_number;
-+ u_char spy_address[IW_MAX_SPY][ETH_ALEN];
-+ struct iw_quality spy_stat[IW_MAX_SPY];
-+#ifdef IW_WIRELESS_THRSPY
-+ /* --- Enhanced spy support (event) */
-+ struct iw_quality spy_thr_low; /* Low threshold */
-+ struct iw_quality spy_thr_high; /* High threshold */
-+ u_char spy_thr_under[IW_MAX_SPY];
-+#endif /* IW_WIRELESS_THRSPY */
-+#endif /* IW_WIRELESS_SPY */
-+};
++ err = hidp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
+
- /**************************** PROTOTYPES ****************************/
- /*
- * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
-@@ -376,6 +414,31 @@
- /* We may need a function to send a stream of events to user space.
- * More on that later... */
-
-+/* Standard handler for SIOCSIWSPY */
-+extern int iw_handler_set_spy(struct net_device * dev,
-+ struct iw_request_info * info,
-+ union iwreq_data * wrqu,
-+ char * extra);
-+/* Standard handler for SIOCGIWSPY */
-+extern int iw_handler_get_spy(struct net_device * dev,
-+ struct iw_request_info * info,
-+ union iwreq_data * wrqu,
-+ char * extra);
-+/* Standard handler for SIOCSIWTHRSPY */
-+extern int iw_handler_set_thrspy(struct net_device * dev,
-+ struct iw_request_info *info,
-+ union iwreq_data * wrqu,
-+ char * extra);
-+/* Standard handler for SIOCGIWTHRSPY */
-+extern int iw_handler_get_thrspy(struct net_device * dev,
-+ struct iw_request_info *info,
-+ union iwreq_data * wrqu,
-+ char * extra);
-+/* Driver call to update spy records */
-+extern void wireless_spy_update(struct net_device * dev,
-+ unsigned char * address,
-+ struct iw_quality * wstats);
++ return err;
+
- /************************* INLINE FUNTIONS *************************/
- /*
- * Function that are so simple that it's more efficient inlining them
---- linux-2.4.21/init/do_mounts.c~small-nocramdisk
-+++ linux-2.4.21/init/do_mounts.c
-@@ -16,8 +16,6 @@
- #include <linux/ext2_fs.h>
- #include <linux/romfs_fs.h>
-
--#define BUILD_CRAMDISK
--
- extern int get_filesystem_list(char * buf);
-
- extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
---- linux-2.4.21/kernel/ksyms.c~ramses
-+++ linux-2.4.21/kernel/ksyms.c
-@@ -585,6 +585,11 @@
-
- EXPORT_SYMBOL(tasklist_lock);
- EXPORT_SYMBOL(pidhash);
-+#ifdef CONFIG_ARCH_RAMSES
-+#include <asm/arch/ramses.h>
-+EXPORT_SYMBOL(ramses_control_shadow);
-+EXPORT_SYMBOL(ramses_flags);
-+#endif
-
- /* debug */
- EXPORT_SYMBOL(dump_stack);
---- linux-2.4.21/kernel/pm.c~pm
-+++ linux-2.4.21/kernel/pm.c
-@@ -234,7 +234,7 @@
- struct list_head *entry;
-
- down(&pm_devs_lock);
-- entry = pm_devs.next;
-+ entry = (rqst==PM_RESUME) ? pm_devs.prev : pm_devs.next;
- while (entry != &pm_devs) {
- struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
- if (dev->callback) {
-@@ -249,7 +249,7 @@
- return status;
- }
- }
-- entry = entry->next;
-+ entry = (rqst==PM_RESUME) ? entry->prev : entry->next;
- }
- up(&pm_devs_lock);
- return 0;
---- linux-2.4.21/lib/Config.in~mtd-cvs
-+++ linux-2.4.21/lib/Config.in
-@@ -35,4 +35,20 @@
- fi
- fi
-
-+if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
-+ "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
-+ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
-+ define_tristate CONFIG_REED_SOLOMON y
-+ define_tristate CONFIG_REED_SOLOMON_DEC16 y
-+else
-+ if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
-+ "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
-+ "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
-+ define_tristate CONFIG_REED_SOLOMON m
-+ define_tristate CONFIG_REED_SOLOMON_DEC16 y
-+ else
-+ define_tristate CONFIG_REED_SOLOMON n
-+ fi
-+fi
++ case HIDPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
+
- endmenu
---- linux-2.4.21/lib/Makefile~mtd-cvs
-+++ linux-2.4.21/lib/Makefile
-@@ -22,6 +22,7 @@
-
- subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
- subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
-+subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
-
- # Include the subdirs, if necessary.
- obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/Makefile
-@@ -0,0 +1,12 @@
-+#
-+# This is a modified version of reed solomon lib,
-+#
++ err = hidp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
+
-+O_TARGET:= reed_solomon.o
++static struct proto_ops hidp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: hidp_sock_release,
++ ioctl: hidp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
+
-+export-objs := rslib.o
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
+
-+obj-y := rslib.o
-+obj-m := $(O_TARGET)
++ BT_DBG("sock %p", sock);
+
-+include $(TOPDIR)/Rules.make
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/decode_rs.c
-@@ -0,0 +1,272 @@
-+/*
-+ * lib/reed_solomon/decode_rs.c
-+ *
-+ * Overview:
-+ * Generic Reed Solomon encoder / decoder library
-+ *
-+ * Copyright 2002, Phil Karn, KA9Q
-+ * May be used under the terms of the GNU General Public License (GPL)
-+ *
-+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * $Id$
-+ *
-+ */
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
+
-+/* Generic data width independent code which is included by the
-+ * wrappers.
-+ */
-+{
-+ int deg_lambda, el, deg_omega;
-+ int i, j, r, k, pad;
-+ int nn = rs->nn;
-+ int nroots = rs->nroots;
-+ int fcr = rs->fcr;
-+ int prim = rs->prim;
-+ int iprim = rs->iprim;
-+ uint16_t *alpha_to = rs->alpha_to;
-+ uint16_t *index_of = rs->index_of;
-+ uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
-+ /* Err+Eras Locator poly and syndrome poly The maximum value
-+ * of nroots is 8. So the necessary stack size will be about
-+ * 220 bytes max.
-+ */
-+ uint16_t lambda[nroots + 1], syn[nroots];
-+ uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
-+ uint16_t root[nroots], reg[nroots + 1], loc[nroots];
-+ int count = 0;
-+ uint16_t msk = (uint16_t) rs->nn;
++ sock->ops = &hidp_sock_ops;
+
-+ /* Check length parameter for validity */
-+ pad = nn - nroots - len;
-+ if (pad < 0 || pad >= nn)
-+ return -ERANGE;
-+
-+ /* Does the caller provide the syndrome ? */
-+ if (s != NULL)
-+ goto decode;
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
+
-+ /* form the syndromes; i.e., evaluate data(x) at roots of
-+ * g(x) */
-+ for (i = 0; i < nroots; i++)
-+ syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
++ MOD_INC_USE_COUNT;
+
-+ for (j = 1; j < len; j++) {
-+ for (i = 0; i < nroots; i++) {
-+ if (syn[i] == 0) {
-+ syn[i] = (((uint16_t) data[j]) ^
-+ invmsk) & msk;
-+ } else {
-+ syn[i] = ((((uint16_t) data[j]) ^
-+ invmsk) & msk) ^
-+ alpha_to[rs_modnn(rs, index_of[syn[i]] +
-+ (fcr + i) * prim)];
-+ }
-+ }
-+ }
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
+
-+ for (j = 0; j < nroots; j++) {
-+ for (i = 0; i < nroots; i++) {
-+ if (syn[i] == 0) {
-+ syn[i] = ((uint16_t) par[j]) & msk;
-+ } else {
-+ syn[i] = (((uint16_t) par[j]) & msk) ^
-+ alpha_to[rs_modnn(rs, index_of[syn[i]] +
-+ (fcr+i)*prim)];
-+ }
-+ }
-+ }
-+ s = syn;
++ sk->destruct = NULL;
++ sk->protocol = protocol;
+
-+ /* Convert syndromes to index form, checking for nonzero condition */
-+ syn_error = 0;
-+ for (i = 0; i < nroots; i++) {
-+ syn_error |= s[i];
-+ s[i] = index_of[s[i]];
-+ }
++ return 0;
++}
+
-+ if (!syn_error) {
-+ /* if syndrome is zero, data[] is a codeword and there are no
-+ * errors to correct. So return data[] unmodified
-+ */
-+ count = 0;
-+ goto finish;
-+ }
++static struct net_proto_family hidp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: hidp_sock_create
++};
+
-+ decode:
-+ memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
-+ lambda[0] = 1;
++int __init hidp_init_sockets(void)
++{
++ int err;
+
-+ if (no_eras > 0) {
-+ /* Init lambda to be the erasure locator polynomial */
-+ lambda[1] = alpha_to[rs_modnn(rs,
-+ prim * (nn - 1 - eras_pos[0]))];
-+ for (i = 1; i < no_eras; i++) {
-+ u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
-+ for (j = i + 1; j > 0; j--) {
-+ tmp = index_of[lambda[j - 1]];
-+ if (tmp != nn) {
-+ lambda[j] ^=
-+ alpha_to[rs_modnn(rs, u + tmp)];
-+ }
-+ }
-+ }
-+ }
++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++ BT_ERR("Can't register HIDP socket layer (%d)", err);
+
-+ for (i = 0; i < nroots + 1; i++)
-+ b[i] = index_of[lambda[i]];
++ return err;
++}
+
-+ /*
-+ * Begin Berlekamp-Massey algorithm to determine error+erasure
-+ * locator polynomial
-+ */
-+ r = no_eras;
-+ el = no_eras;
-+ while (++r <= nroots) { /* r is the step number */
-+ /* Compute discrepancy at the r-th step in poly-form */
-+ discr_r = 0;
-+ for (i = 0; i < r; i++) {
-+ if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
-+ discr_r ^=
-+ alpha_to[rs_modnn(rs,
-+ index_of[lambda[i]] +
-+ s[r - i - 1])];
-+ }
-+ }
-+ discr_r = index_of[discr_r]; /* Index form */
-+ if (discr_r == nn) {
-+ /* 2 lines below: B(x) <-- x*B(x) */
-+ memmove (&b[1], b, nroots * sizeof (b[0]));
-+ b[0] = nn;
-+ } else {
-+ /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
-+ t[0] = lambda[0];
-+ for (i = 0; i < nroots; i++) {
-+ if (b[i] != nn) {
-+ t[i + 1] = lambda[i + 1] ^
-+ alpha_to[rs_modnn(rs, discr_r +
-+ b[i])];
-+ } else
-+ t[i + 1] = lambda[i + 1];
-+ }
-+ if (2 * el <= r + no_eras - 1) {
-+ el = r + no_eras - el;
-+ /*
-+ * 2 lines below: B(x) <-- inv(discr_r) *
-+ * lambda(x)
-+ */
-+ for (i = 0; i <= nroots; i++) {
-+ b[i] = (lambda[i] == 0) ? nn :
-+ rs_modnn(rs, index_of[lambda[i]]
-+ - discr_r + nn);
-+ }
-+ } else {
-+ /* 2 lines below: B(x) <-- x*B(x) */
-+ memmove(&b[1], b, nroots * sizeof(b[0]));
-+ b[0] = nn;
-+ }
-+ memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
-+ }
-+ }
++void __exit hidp_cleanup_sockets(void)
++{
++ int err;
+
-+ /* Convert lambda to index form and compute deg(lambda(x)) */
-+ deg_lambda = 0;
-+ for (i = 0; i < nroots + 1; i++) {
-+ lambda[i] = index_of[lambda[i]];
-+ if (lambda[i] != nn)
-+ deg_lambda = i;
-+ }
-+ /* Find roots of error+erasure locator polynomial by Chien search */
-+ memcpy(®[1], &lambda[1], nroots * sizeof(reg[0]));
-+ count = 0; /* Number of roots of lambda(x) */
-+ for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
-+ q = 1; /* lambda[0] is always 0 */
-+ for (j = deg_lambda; j > 0; j--) {
-+ if (reg[j] != nn) {
-+ reg[j] = rs_modnn(rs, reg[j] + j);
-+ q ^= alpha_to[reg[j]];
-+ }
-+ }
-+ if (q != 0)
-+ continue; /* Not a root */
-+ /* store root (index-form) and error location number */
-+ root[count] = i;
-+ loc[count] = k;
-+ /* If we've already found max possible roots,
-+ * abort the search to save time
-+ */
-+ if (++count == deg_lambda)
-+ break;
-+ }
-+ if (deg_lambda != count) {
-+ /*
-+ * deg(lambda) unequal to number of roots => uncorrectable
-+ * error detected
-+ */
-+ count = -1;
-+ goto finish;
++ if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++ BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+--- linux-2.4.21/net/bluetooth/l2cap.c~bluetooth
++++ linux-2.4.21/net/bluetooth/l2cap.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id$
+ */
+-#define VERSION "2.1"
++#define VERSION "2.3"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -284,7 +284,7 @@
+ l2cap_disconn_req req;
+
+ sk->state = BT_DISCONN;
+- l2cap_sock_set_timer(sk, HZ * 5);
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+@@ -309,11 +309,9 @@
+ static void l2cap_sock_close(struct sock *sk)
+ {
+ l2cap_sock_clear_timer(sk);
+-
+ lock_sock(sk);
+ __l2cap_sock_close(sk, ECONNRESET);
+ release_sock(sk);
+-
+ l2cap_sock_kill(sk);
+ }
+
+@@ -527,7 +525,8 @@
+ goto done;
+
+ wait:
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -760,32 +759,39 @@
+ static int l2cap_sock_shutdown(struct socket *sock, int how)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk) return 0;
+
+- l2cap_sock_clear_timer(sk);
+-
+ lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- __l2cap_sock_close(sk, ECONNRESET);
+- release_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ l2cap_sock_clear_timer(sk);
++ __l2cap_sock_close(sk, 0);
+
+- return 0;
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
+ }
-+ /*
-+ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
-+ * x**nroots). in index form. Also find deg(omega).
-+ */
-+ deg_omega = deg_lambda - 1;
-+ for (i = 0; i <= deg_omega; i++) {
-+ tmp = 0;
-+ for (j = i; j >= 0; j--) {
-+ if ((s[i - j] != nn) && (lambda[j] != nn))
-+ tmp ^=
-+ alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
-+ }
-+ omega[i] = index_of[tmp];
++ release_sock(sk);
++ return err;
+ }
+
+ static int l2cap_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk) return 0;
+
++ err = l2cap_sock_shutdown(sock, 2);
++
+ sock_orphan(sk);
+- l2cap_sock_close(sk);
+- return 0;
++ l2cap_sock_kill(sk);
++ return err;
+ }
+
+ /* --------- L2CAP channels --------- */
+@@ -917,10 +923,12 @@
+ hci_conn_put(conn->hcon);
+ }
+
+- sk->state = BT_CLOSED;
+- sk->err = err;
++ sk->state = BT_CLOSED;
+ sk->zapped = 1;
+
++ if (err)
++ sk->err = err;
++
+ if (parent)
+ parent->data_ready(parent, 0);
+ else
+@@ -956,6 +964,22 @@
+ read_unlock(&l->lock);
+ }
+
++/* Notify sockets that we cannot guaranty reliability anymore */
++static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
++ sk->err = err;
+ }
++ read_unlock(&l->lock);
++}
+
-+ /*
-+ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
-+ * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
-+ */
-+ for (j = count - 1; j >= 0; j--) {
-+ num1 = 0;
-+ for (i = deg_omega; i >= 0; i--) {
-+ if (omega[i] != nn)
-+ num1 ^= alpha_to[rs_modnn(rs, omega[i] +
-+ i * root[j])];
+ static void l2cap_chan_ready(struct sock *sk)
+ {
+ struct sock *parent = bluez_pi(sk)->parent;
+@@ -1320,15 +1344,18 @@
+ {
+ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+ void *ptr = rsp->data;
++ u16 flags = 0;
+
+ BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
+
+ if (result)
+ *result = l2cap_conf_output(sk, &ptr);
++ else
++ flags |= 0x0001;
+
+ rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp->result = __cpu_to_le16(result ? *result : 0);
+- rsp->flags = __cpu_to_le16(0);
++ rsp->flags = __cpu_to_le16(flags);
+
+ return ptr - data;
+ }
+@@ -1441,7 +1468,7 @@
+ case L2CAP_CR_SUCCESS:
+ sk->state = BT_CONFIG;
+ l2cap_pi(sk)->dcid = dcid;
+- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+
+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+ break;
+@@ -1476,7 +1503,7 @@
+
+ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+
+- if (flags & 0x01) {
++ if (flags & 0x0001) {
+ /* Incomplete config. Send empty response. */
+ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+ goto unlock;
+@@ -1489,12 +1516,12 @@
+ goto unlock;
+
+ /* Output config done */
+- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
+ sk->state = BT_CONNECTED;
+ l2cap_chan_ready(sk);
+- } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
++ } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
+ char req[64];
+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+ }
+@@ -1520,18 +1547,34 @@
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return -ENOENT;
+
+- if (result) {
+- l2cap_disconn_req req;
++ switch (result) {
++ case L2CAP_CONF_SUCCESS:
++ break;
+
+- /* They didn't like our options. Well... we do not negotiate.
+- * Close channel.
+- */
++ case L2CAP_CONF_UNACCEPT:
++ if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
++ char req[128];
++ /*
++ It does not make sense to adjust L2CAP parameters
++ that are currently defined in the spec. We simply
++ resend config request that we sent earlier. It is
++ stupid :) but it helps qualification testing
++ which expects at least some response from us.
++ */
++ l2cap_send_req(conn, L2CAP_CONF_REQ,
++ l2cap_build_conf_req(sk, req), req);
++ goto done;
++ }
++ default:
+ sk->state = BT_DISCONN;
++ sk->err = ECONNRESET;
+ l2cap_sock_set_timer(sk, HZ * 5);
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ {
++ l2cap_disconn_req req;
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+ }
-+ num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
-+ den = 0;
+ goto done;
+ }
+
+@@ -1539,9 +1582,9 @@
+ goto done;
+
+ /* Input config done */
+- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ sk->state = BT_CONNECTED;
+ l2cap_chan_ready(sk);
+ }
+@@ -1592,13 +1635,42 @@
+
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return 0;
+- l2cap_chan_del(sk, ECONNABORTED);
++ l2cap_chan_del(sk, 0);
+ bh_unlock_sock(sk);
+
+ l2cap_sock_kill(sk);
+ return 0;
+ }
+
++static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_req *req = (l2cap_info_req *) data;
++ l2cap_info_rsp rsp;
++ u16 type;
+
-+ /* lambda[i+1] for i even is the formal derivative
-+ * lambda_pr of lambda[i] */
-+ for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
-+ if (lambda[i + 1] != nn) {
-+ den ^= alpha_to[rs_modnn(rs, lambda[i + 1] +
-+ i * root[j])];
-+ }
-+ }
-+ /* Apply error to data */
-+ if (num1 != 0 && loc[j] >= pad) {
-+ uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] +
-+ index_of[num2] +
-+ nn - index_of[den])];
-+ /* Store the error correction pattern, if a
-+ * correction buffer is available */
-+ if (corr) {
-+ corr[j] = cor;
-+ } else {
-+ /* If a data buffer is given and the
-+ * error is inside the message,
-+ * correct it */
-+ if (data && (loc[j] < (nn - nroots)))
-+ data[loc[j] - pad] ^= cor;
-+ }
-+ }
-+ }
++ type = __le16_to_cpu(req->type);
+
-+finish:
-+ if (eras_pos != NULL) {
-+ for (i = 0; i < count; i++)
-+ eras_pos[i] = loc[i] - pad;
-+ }
-+ return count;
++ BT_DBG("type 0x%4.4x", type);
+
++ rsp.type = __cpu_to_le16(type);
++ rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
++ return 0;
+}
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/encode_rs.c
-@@ -0,0 +1,54 @@
-+/*
-+ * lib/reed_solomon/encode_rs.c
-+ *
-+ * Overview:
-+ * Generic Reed Solomon encoder / decoder library
-+ *
-+ * Copyright 2002, Phil Karn, KA9Q
-+ * May be used under the terms of the GNU General Public License (GPL)
-+ *
-+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * $Id$
-+ *
-+ */
+
-+/* Generic data width independent code which is included by the
-+ * wrappers.
-+ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
-+ */
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
+{
-+ int i, j, pad;
-+ int nn = rs->nn;
-+ int nroots = rs->nroots;
-+ uint16_t *alpha_to = rs->alpha_to;
-+ uint16_t *index_of = rs->index_of;
-+ uint16_t *genpoly = rs->genpoly;
-+ uint16_t fb;
-+ uint16_t msk = (uint16_t) rs->nn;
++ l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++ u16 type, result;
+
-+ /* Check length parameter for validity */
-+ pad = nn - nroots - len;
-+ if (pad < 0 || pad >= nn)
-+ return -ERANGE;
++ type = __le16_to_cpu(rsp->type);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
+
-+ for (i = 0; i < len; i++) {
-+ fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
-+ /* feedback term is non-zero */
-+ if (fb != nn) {
-+ for (j = 1; j < nroots; j++) {
-+ par[j] ^= alpha_to[rs_modnn(rs, fb +
-+ genpoly[nroots - j])];
-+ }
-+ }
-+ /* Shift */
-+ memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
-+ if (fb != nn) {
-+ par[nroots - 1] = alpha_to[rs_modnn(rs,
-+ fb + genpoly[0])];
-+ } else {
-+ par[nroots - 1] = 0;
-+ }
-+ }
+ return 0;
+}
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/rslib.c
-@@ -0,0 +1,335 @@
-+/*
-+ * lib/reed_solomon/rslib.c
-+ *
-+ * Overview:
-+ * Generic Reed Solomon encoder / decoder library
-+ *
-+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * Reed Solomon code lifted from reed solomon library written by Phil Karn
-+ * Copyright 2002 Phil Karn, KA9Q
-+ *
-+ * $Id$
-+ *
-+ * 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.
-+ *
-+ * Description:
-+ *
-+ * The generic Reed Solomon library provides runtime configurable
-+ * encoding / decoding of RS codes.
-+ * Each user must call init_rs to get a pointer to a rs_control
-+ * structure for the given rs parameters. This structure is either
-+ * generated or a already available matching control structure is used.
-+ * If a structure is generated then the polynomial arrays for
-+ * fast encoding / decoding are built. This can take some time so
-+ * make sure not to call this function from a time critical path.
-+ * Usually a module / driver should initialize the necessary
-+ * rs_control structure on module / driver init and release it
-+ * on exit.
-+ * The encoding puts the calculated syndrome into a given syndrome
-+ * buffer.
-+ * The decoding is a two step process. The first step calculates
-+ * the syndrome over the received (data + syndrome) and calls the
-+ * second stage, which does the decoding / error correction itself.
-+ * Many hw encoders provide a syndrome calculation over the received
-+ * data + syndrome and can call the second stage directly.
-+ *
-+ */
+
-+#include <linux/errno.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/rslib.h>
-+#include <linux/slab.h>
-+#include <asm/semaphore.h>
+ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+ {
+ __u8 *data = skb->data;
+@@ -1606,6 +1678,8 @@
+ l2cap_cmd_hdr cmd;
+ int err = 0;
+
++ l2cap_raw_recv(conn, skb);
++
+ while (len >= L2CAP_CMD_HDR_SIZE) {
+ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+ data += L2CAP_CMD_HDR_SIZE;
+@@ -1621,6 +1695,10 @@
+ }
+
+ switch (cmd.code) {
++ case L2CAP_COMMAND_REJ:
++ /* FIXME: We should process this */
++ break;
++
+ case L2CAP_CONN_REQ:
+ err = l2cap_connect_req(conn, &cmd, data);
+ break;
+@@ -1645,23 +1723,23 @@
+ err = l2cap_disconnect_rsp(conn, &cmd, data);
+ break;
+
+- case L2CAP_COMMAND_REJ:
+- /* FIXME: We should process this */
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+ case L2CAP_ECHO_REQ:
+ l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+ break;
+
+ case L2CAP_ECHO_RSP:
++ break;
++
+ case L2CAP_INFO_REQ:
++ err = l2cap_information_req(conn, &cmd, data);
++ break;
++
+ case L2CAP_INFO_RSP:
+- l2cap_raw_recv(conn, skb);
++ err = l2cap_information_rsp(conn, &cmd, data);
+ break;
+
+ default:
+- BT_ERR("Uknown signaling command 0x%2.2x", cmd.code);
++ BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+ err = -EINVAL;
+ break;
+ };
+@@ -1670,7 +1748,7 @@
+ l2cap_cmd_rej rej;
+ BT_DBG("error %d", err);
+
+- /* FIXME: Map err to a valid reason. */
++ /* FIXME: Map err to a valid reason */
+ rej.reason = __cpu_to_le16(0);
+ l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+ }
+@@ -1943,26 +2021,36 @@
+ kfree_skb(conn->rx_skb);
+ conn->rx_skb = NULL;
+ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
+ }
+
+ if (skb->len < 2) {
+- BT_ERR("Frame is too small (len %d)", skb->len);
++ BT_ERR("Frame is too short (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+ hdr = (l2cap_hdr *) skb->data;
+ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
+
+- BT_DBG("Start: total len %d, frag len %d", len, skb->len);
+-
+ if (len == skb->len) {
+ /* Complete frame received */
+ l2cap_recv_frame(conn, skb);
+ return 0;
+ }
+
+- /* Allocate skb for the complete frame (with header) */
+- if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
++ BT_DBG("Start: total len %d, frag len %d", len, skb->len);
++
++ if (skb->len > len) {
++ BT_ERR("Frame is too long (len %d, expected len %d)",
++ skb->len, len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ /* Allocate skb for the complete frame including header */
++ conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!conn->rx_skb)
+ goto drop;
+
+ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+@@ -1972,15 +2060,17 @@
+
+ if (!conn->rx_len) {
+ BT_ERR("Unexpected continuation frame (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+ if (skb->len > conn->rx_len) {
+- BT_ERR("Fragment is too large (len %d, expect %d)",
++ BT_ERR("Fragment is too long (len %d, expected %d)",
+ skb->len, conn->rx_len);
+ kfree_skb(conn->rx_skb);
+ conn->rx_skb = NULL;
+ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
+ goto drop;
+ }
+
+@@ -2114,6 +2204,16 @@
+ BT_ERR("Can't unregister L2CAP protocol");
+ }
+
++void l2cap_load(void)
++{
++ /* Dummy function to trigger automatic L2CAP module loading by
++ other modules that use L2CAP sockets but do not use any other
++ symbols from it. */
++ return;
++}
++
++EXPORT_SYMBOL(l2cap_load);
++
+ module_init(l2cap_init);
+ module_exit(l2cap_cleanup);
+
+--- linux-2.4.21/net/bluetooth/rfcomm/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/core.c
+@@ -51,7 +51,7 @@
+ #include <net/bluetooth/l2cap.h>
+ #include <net/bluetooth/rfcomm.h>
+
+-#define VERSION "1.0"
++#define VERSION "1.1"
+
+ #ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+ #undef BT_DBG
+@@ -198,10 +198,11 @@
+
+ d->state = BT_OPEN;
+ d->flags = 0;
++ d->mscex = 0;
+ d->mtu = RFCOMM_DEFAULT_MTU;
+ d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
+
+- d->credits = RFCOMM_MAX_CREDITS;
++ d->cfc = RFCOMM_CFC_DISABLED;
+ d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+ }
+
+@@ -274,13 +275,13 @@
+ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+ {
+ struct rfcomm_session *s;
+- u8 dlci = __dlci(0, channel);
+ int err = 0;
++ u8 dlci;
+
+- BT_DBG("dlc %p state %ld %s %s channel %d dlci %d",
+- d, d->state, batostr(src), batostr(dst), channel, dlci);
++ BT_DBG("dlc %p state %ld %s %s channel %d",
++ d, d->state, batostr(src), batostr(dst), channel);
+
+- if (dlci < 1 || dlci > 62)
++ if (channel < 1 || channel > 30)
+ return -EINVAL;
+
+ if (d->state != BT_OPEN && d->state != BT_CLOSED)
+@@ -293,20 +294,23 @@
+ return err;
+ }
+
++ dlci = __dlci(!s->initiator, channel);
++
+ /* Check if DLCI already exists */
+ if (rfcomm_dlc_get(s, dlci))
+ return -EBUSY;
+
+ rfcomm_dlc_clear_state(d);
+
+- d->dlci = dlci;
+- d->addr = __addr(s->initiator, dlci);
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ d->priority = 7;
+
+- d->state = BT_CONFIG;
++ d->state = BT_CONFIG;
+ rfcomm_dlc_link(s, d);
+
+- d->mtu = s->mtu;
+- d->credits = s->credits;
++ d->mtu = s->mtu;
++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
+
+ if (s->state == BT_CONNECTED)
+ rfcomm_send_pn(s, 1, d);
+@@ -406,7 +410,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig |= RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -417,7 +421,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig &= ~RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -470,8 +474,8 @@
+ s->state = state;
+ s->sock = sock;
+
+- s->mtu = RFCOMM_DEFAULT_MTU;
+- s->credits = RFCOMM_MAX_CREDITS;
++ s->mtu = RFCOMM_DEFAULT_MTU;
++ s->cfc = RFCOMM_CFC_UNKNOWN;
+
+ list_add(&s->list, &session_list);
+
+@@ -707,7 +711,7 @@
+ hdr->len = __len8(sizeof(*mcc) + 1);
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
++ mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->len = __len8(1);
+
+ /* Type that we didn't like */
+@@ -733,16 +737,16 @@
+ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
++ mcc->type = __mcc_type(cr, RFCOMM_PN);
+ mcc->len = __len8(sizeof(*pn));
+
+ pn = (void *) ptr; ptr += sizeof(*pn);
+ pn->dlci = d->dlci;
+- pn->priority = 0;
++ pn->priority = d->priority;
+ pn->ack_timer = 0;
+ pn->max_retrans = 0;
+
+- if (d->credits) {
++ if (s->cfc) {
+ pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+ pn->credits = RFCOMM_DEFAULT_CREDITS;
+ } else {
+@@ -842,7 +846,51 @@
+
+ msc = (void *) ptr; ptr += sizeof(*msc);
+ msc->dlci = __addr(1, dlci);
+- msc->v24_sig = v24_sig;
++ msc->v24_sig = v24_sig | 0x01;
+
-+/* This list holds all currently allocated rs control structures */
-+static LIST_HEAD (rslist);
-+/* Protection for the list */
-+static DECLARE_MUTEX(rslistlock);
++ *ptr = __fcs(buf); ptr++;
+
-+/**
-+ * rs_init - Initialize a Reed-Solomon codec
-+ *
-+ * @symsize: symbol size, bits (1-8)
-+ * @gfpoly: Field generator polynomial coefficients
-+ * @fcr: first root of RS code generator polynomial, index form
-+ * @prim: primitive element to generate polynomial roots
-+ * @nroots: RS code generator polynomial degree (number of roots)
-+ *
-+ * Allocate a control structure and the polynom arrays for faster
-+ * en/decoding. Fill the arrays according to the given parameters
-+ */
-+static struct rs_control *rs_init(int symsize, int gfpoly, int fcr,
-+ int prim, int nroots)
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
+{
-+ struct rs_control *rs;
-+ int i, j, sr, root, iprim;
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
+
-+ /* Allocate the control structure */
-+ rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
-+ if (rs == NULL)
-+ return NULL;
++ BT_DBG("%p cr %d", s, cr);
+
-+ INIT_LIST_HEAD(&rs->list);
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
+
-+ rs->mm = symsize;
-+ rs->nn = (1 << symsize) - 1;
-+ rs->fcr = fcr;
-+ rs->prim = prim;
-+ rs->nroots = nroots;
-+ rs->gfpoly = gfpoly;
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
++ mcc->len = __len8(0);
+
-+ /* Allocate the arrays */
-+ rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
-+ if (rs->alpha_to == NULL)
-+ goto errrs;
++ *ptr = __fcs(buf); ptr++;
+
-+ rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
-+ if (rs->index_of == NULL)
-+ goto erralp;
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
+
-+ rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
-+ if(rs->genpoly == NULL)
-+ goto erridx;
++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
+
-+ /* Generate Galois field lookup tables */
-+ rs->index_of[0] = rs->nn; /* log(zero) = -inf */
-+ rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */
-+ sr = 1;
-+ for (i = 0; i < rs->nn; i++) {
-+ rs->index_of[sr] = i;
-+ rs->alpha_to[i] = sr;
-+ sr <<= 1;
-+ if (sr & (1 << symsize))
-+ sr ^= gfpoly;
-+ sr &= rs->nn;
-+ }
-+ /* If it's not primitive, exit */
-+ if(sr != 1)
-+ goto errpol;
++ BT_DBG("%p cr %d", s, cr);
+
-+ /* Find prim-th root of 1, used in decoding */
-+ for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
-+ /* prim-th root of 1, index form */
-+ rs->iprim = iprim / prim;
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
+
-+ /* Form RS code generator polynomial from its roots */
-+ rs->genpoly[0] = 1;
-+ for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
-+ rs->genpoly[i + 1] = 1;
-+ /* Multiply rs->genpoly[] by @**(root + x) */
-+ for (j = i; j > 0; j--) {
-+ if (rs->genpoly[j] != 0) {
-+ rs->genpoly[j] = rs->genpoly[j -1] ^
-+ rs->alpha_to[rs_modnn(rs,
-+ rs->index_of[rs->genpoly[j]] + root)];
-+ } else
-+ rs->genpoly[j] = rs->genpoly[j - 1];
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCON);
++ mcc->len = __len8(0);
+
+ *ptr = __fcs(buf); ptr++;
+
+@@ -1076,6 +1124,8 @@
+ d->state = BT_CONNECTED;
+ d->state_change(d, 0);
+ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+ } else {
+ rfcomm_send_dm(s, dlci);
+ }
+@@ -1085,29 +1135,23 @@
+
+ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
+ {
++ struct rfcomm_session *s = d->session;
++
+ BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
+ d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
+
+- if (cr) {
+- if (pn->flow_ctrl == 0xf0) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++ d->tx_credits = pn->credits;
+ } else {
+- if (pn->flow_ctrl == 0xe0) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ }
+
++ d->priority = pn->priority;
++
++ d->mtu = s->mtu = btohs(pn->mtu);
++
+ return 0;
+ }
+
+@@ -1133,7 +1177,7 @@
+ switch (d->state) {
+ case BT_CONFIG:
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_CONNECT;
+ rfcomm_send_sabm(s, d->dlci);
+ break;
+@@ -1144,7 +1188,7 @@
+
+ if (!cr)
+ return 0;
+-
++
+ /* PN request for non existing DLC.
+ * Assume incomming connection. */
+ if (rfcomm_connect_ind(s, channel, &d)) {
+@@ -1153,7 +1197,7 @@
+ rfcomm_dlc_link(s, d);
+
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_OPEN;
+ rfcomm_send_pn(s, 0, d);
+ } else {
+@@ -1198,6 +1242,14 @@
+ }
+ /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
+ no flow control lines, normal XON/XOFF chars */
++ if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
++ bit_rate = rpn->bit_rate;
++ if (bit_rate != RFCOMM_RPN_BR_115200) {
++ BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
++ bit_rate = RFCOMM_RPN_BR_115200;
++ rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
++ }
++ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
+ data_bits = __get_rpn_data_bits(rpn->line_settings);
+ if (data_bits != RFCOMM_RPN_DATA_8) {
+@@ -1223,23 +1275,26 @@
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+- if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+- BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
+- rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ flow_ctrl = rpn->flow_ctrl;
++ if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
++ BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+ rpn_mask ^= RFCOMM_RPN_PM_FLOW;
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
+- if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
+- BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
+- rpn->xon_char = RFCOMM_RPN_XON_CHAR;
++ xon_char = rpn->xon_char;
++ if (xon_char != RFCOMM_RPN_XON_CHAR) {
++ BT_DBG("RPN XON char mismatch 0x%x", xon_char);
++ xon_char = RFCOMM_RPN_XON_CHAR;
+ rpn_mask ^= RFCOMM_RPN_PM_XON;
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
+- if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+- BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
+- rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
++ xoff_char = rpn->xoff_char;
++ if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
++ BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
+ rpn_mask ^= RFCOMM_RPN_PM_XOFF;
+ }
+ }
+@@ -1280,12 +1335,12 @@
+
+ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+
+- if (!cr)
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d)
+ return 0;
+
+- d = rfcomm_dlc_get(s, dlci);
+- if (d) {
+- if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
++ if (cr) {
++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ else
+ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1296,7 +1351,11 @@
+ rfcomm_dlc_unlock(d);
+
+ rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
+- }
++
++ d->mscex |= RFCOMM_MSCEX_RX;
++ } else
++ d->mscex |= RFCOMM_MSCEX_TX;
++
+ return 0;
+ }
+
+@@ -1330,6 +1389,20 @@
+ rfcomm_recv_msc(s, cr, skb);
+ break;
+
++ case RFCOMM_FCOFF:
++ if (cr) {
++ set_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcoff(s, 0);
+ }
-+ /* rs->genpoly[0] can never be zero */
-+ rs->genpoly[0] =
-+ rs->alpha_to[rs_modnn(rs,
-+ rs->index_of[rs->genpoly[0]] + root)];
-+ }
-+ /* convert rs->genpoly[] to index form for quicker encoding */
-+ for (i = 0; i <= nroots; i++)
-+ rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
-+ return rs;
++ break;
+
-+ /* Error exit */
-+errpol:
-+ kfree(rs->genpoly);
-+erridx:
-+ kfree(rs->index_of);
-+erralp:
-+ kfree(rs->alpha_to);
-+errrs:
-+ kfree(rs);
-+ return NULL;
-+}
++ case RFCOMM_FCON:
++ if (cr) {
++ clear_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcon(s, 0);
++ }
++ break;
++
+ case RFCOMM_TEST:
+ if (cr)
+ rfcomm_send_test(s, 0, skb->data, skb->len);
+@@ -1358,7 +1431,7 @@
+ goto drop;
+ }
+
+- if (pf && d->credits) {
++ if (pf && d->cfc) {
+ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+
+ d->tx_credits += credits;
+@@ -1463,20 +1536,20 @@
+ struct sk_buff *skb;
+ int err;
+
+- BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d",
+- d, d->state, d->credits, d->rx_credits, d->tx_credits);
++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
++ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
+
+ /* Send pending MSC */
+ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
+ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+
+- if (d->credits) {
++ if (d->cfc) {
+ /* CFC enabled.
+ * Give them some credits */
+ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
+- d->rx_credits <= (d->credits >> 2)) {
+- rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
+- d->rx_credits = d->credits;
++ d->rx_credits <= (d->cfc >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
++ d->rx_credits = d->cfc;
+ }
+ } else {
+ /* CFC disabled.
+@@ -1497,7 +1570,7 @@
+ d->tx_credits--;
+ }
+
+- if (d->credits && !d->tx_credits) {
++ if (d->cfc && !d->tx_credits) {
+ /* We're out of TX credits.
+ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1520,7 +1593,11 @@
+ continue;
+ }
+
+- if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
++ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
++ continue;
+
++ if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
++ d->mscex == RFCOMM_MSCEX_OK)
+ rfcomm_process_tx(d);
+ }
+ }
+@@ -1577,9 +1654,10 @@
+ nsock->sk->state_change = rfcomm_l2state_change;
+
+ s = rfcomm_session_add(nsock, BT_OPEN);
+- if (s)
++ if (s) {
+ rfcomm_session_hold(s);
+- else
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++ } else
+ sock_release(nsock);
+ }
+
+@@ -1815,6 +1893,8 @@
+ /* ---- Initialization ---- */
+ int __init rfcomm_init(void)
+ {
++ l2cap_load();
+
-+/**
-+ * free_rs - Free the rs control structure, if its not longer used
-+ *
-+ * @rs: the control structure which is not longer used by the
-+ * caller
-+ */
-+void free_rs(struct rs_control *rs)
-+{
-+ down(&rslistlock);
-+ rs->users--;
-+ if(!rs->users) {
-+ list_del(&rs->list);
-+ kfree(rs->alpha_to);
-+ kfree(rs->index_of);
-+ kfree(rs->genpoly);
-+ kfree(rs);
+ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+ rfcomm_init_sockets();
+--- linux-2.4.21/net/bluetooth/rfcomm/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/sock.c
+@@ -188,8 +188,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted dlcs */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ rfcomm_sock_close(sk);
++ rfcomm_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -211,15 +213,10 @@
+ sock_put(sk);
+ }
+
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void rfcomm_sock_close(struct sock *sk)
++static void __rfcomm_sock_close(struct sock *sk)
+ {
+ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+- lock_sock(sk);
+-
+ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+ switch (sk->state) {
+@@ -236,11 +233,17 @@
+ default:
+ sk->zapped = 1;
+ break;
+- };
+ }
-+ up(&rslistlock);
+}
-+
-+/**
-+ * init_rs - Find a matching or allocate a new rs control structure
-+ *
-+ * @symsize: the symbol size (number of bits)
-+ * @gfpoly: the extended Galois field generator polynomial coefficients,
-+ * with the 0th coefficient in the low order bit. The polynomial
-+ * must be primitive;
-+ * @fcr: the first consecutive root of the rs code generator polynomial
-+ * in index form
-+ * @prim: primitive element to generate polynomial roots
-+ * @nroots: RS code generator polynomial degree (number of roots)
+
++/* Close socket.
++ * Must be called on unlocked socket.
+ */
-+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
-+ int nroots)
++static void rfcomm_sock_close(struct sock *sk)
+{
-+ struct list_head *tmp;
-+ struct rs_control *rs;
++ lock_sock(sk);
++ __rfcomm_sock_close(sk);
+ release_sock(sk);
+-
+- rfcomm_sock_kill(sk);
+ }
+
+ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+@@ -374,7 +377,8 @@
+
+ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+ if (!err)
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ release_sock(sk);
+ return err;
+@@ -558,9 +562,6 @@
+ int target, err = 0, copied = 0;
+ long timeo;
+
+- if (sk->state != BT_CONNECTED)
+- return -EINVAL;
+-
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+@@ -635,23 +636,6 @@
+ return copied ? : err;
+ }
+
+-static int rfcomm_sock_shutdown(struct socket *sock, int how)
+-{
+- struct sock *sk = sock->sk;
+-
+- BT_DBG("sock %p, sk %p", sock, sk);
+-
+- if (!sk) return 0;
+-
+- lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- if (sk->state == BT_CONNECTED)
+- rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
+- release_sock(sk);
+-
+- return 0;
+-}
+-
+ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+ {
+ struct sock *sk = sock->sk;
+@@ -711,19 +695,42 @@
+ return err;
+ }
+
++static int rfcomm_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
+
-+ /* Sanity checks */
-+ if (symsize < 1)
-+ return NULL;
-+ if (fcr < 0 || fcr >= (1<<symsize))
-+ return NULL;
-+ if (prim <= 0 || prim >= (1<<symsize))
-+ return NULL;
-+ if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
-+ return NULL;
-+
-+ down(&rslistlock);
++ BT_DBG("sock %p, sk %p", sock, sk);
+
-+ /* Walk through the list and look for a matching entry */
-+ list_for_each(tmp, &rslist) {
-+ rs = list_entry(tmp, struct rs_control, list);
-+ if (symsize != rs->mm)
-+ continue;
-+ if (gfpoly != rs->gfpoly)
-+ continue;
-+ if (fcr != rs->fcr)
-+ continue;
-+ if (prim != rs->prim)
-+ continue;
-+ if (nroots != rs->nroots)
-+ continue;
-+ /* We have a matching one already */
-+ rs->users++;
-+ goto out;
-+ }
++ if (!sk) return 0;
+
-+ /* Create a new one */
-+ rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
-+ if (rs) {
-+ rs->users = 1;
-+ list_add(&rs->list, &rslist);
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ __rfcomm_sock_close(sk);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
+ }
-+out:
-+ up(&rslistlock);
-+ return rs;
++ release_sock(sk);
++ return err;
+}
+
-+#ifdef CONFIG_REED_SOLOMON_ENC8
-+/**
-+ * encode_rs8 - Calculate the parity for data values (8bit data width)
-+ *
-+ * @rs: the rs control structure
-+ * @data: data field of a given type
-+ * @len: data length
-+ * @par: parity data, must be initialized by caller (usually all 0)
-+ * @invmsk: invert data mask (will be xored on data)
-+ *
-+ * The parity uses a uint16_t data type to enable
-+ * symbol size > 8. The calling code must take care of encoding of the
-+ * syndrome result for storage itself.
-+ */
-+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
-+ uint16_t invmsk)
-+{
-+#include "encode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(encode_rs8);
-+#endif
+ static int rfcomm_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+- sock_orphan(sk);
+- rfcomm_sock_close(sk);
++ err = rfcomm_sock_shutdown(sock, 2);
+
+- return 0;
++ sock_orphan(sk);
++ rfcomm_sock_kill(sk);
++ return err;
+ }
+
+ /* ---- RFCOMM core layer callbacks ----
+--- linux-2.4.21/net/bluetooth/rfcomm/tty.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/tty.c
+@@ -109,6 +109,13 @@
+
+ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
+ {
++ /* The reason this isn't actually a race, as you no
++ doubt have a little voice screaming at you in your
++ head, is that the refcount should never actually
++ reach zero unless the device has already been taken
++ off the list, in rfcomm_dev_del(). And if that's not
++ true, we'll hit the BUG() in rfcomm_dev_destruct()
++ anyway. */
+ if (atomic_dec_and_test(&dev->refcnt))
+ rfcomm_dev_destruct(dev);
+ }
+@@ -132,10 +139,13 @@
+ struct rfcomm_dev *dev;
+
+ read_lock(&rfcomm_dev_lock);
+
-+#ifdef CONFIG_REED_SOLOMON_DEC8
-+/**
-+ * decode_rs8 - Decode codeword (8bit data width)
-+ *
-+ * @rs: the rs control structure
-+ * @data: data field of a given type
-+ * @par: received parity data field
-+ * @len: data length
-+ * @s: syndrome data field (if NULL, syndrome is calculated)
-+ * @no_eras: number of erasures
-+ * @eras_pos: position of erasures, can be NULL
-+ * @invmsk: invert data mask (will be xored on data, not on parity!)
-+ * @corr: buffer to store correction bitmask on eras_pos
-+ *
-+ * The syndrome and parity uses a uint16_t data type to enable
-+ * symbol size > 8. The calling code must take care of decoding of the
-+ * syndrome result and the received parity before calling this code.
-+ */
-+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
-+ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+ uint16_t *corr)
-+{
-+#include "decode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(decode_rs8);
-+#endif
+ dev = __rfcomm_dev_get(id);
++ if (dev)
++ rfcomm_dev_hold(dev);
+
-+#ifdef CONFIG_REED_SOLOMON_ENC16
-+/**
-+ * encode_rs16 - Calculate the parity for data values (16bit data width)
-+ *
-+ * @rs: the rs control structure
-+ * @data: data field of a given type
-+ * @len: data length
-+ * @par: parity data, must be initialized by caller (usually all 0)
-+ * @invmsk: invert data mask (will be xored on data, not on parity!)
-+ *
-+ * Each field in the data array contains up to symbol size bits of valid data.
-+ */
-+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
-+ uint16_t invmsk)
-+{
-+#include "encode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(encode_rs16);
-+#endif
+ read_unlock(&rfcomm_dev_lock);
+
+- if (dev) rfcomm_dev_hold(dev);
+ return dev;
+ }
+
+@@ -260,9 +270,9 @@
+ skb->destructor = rfcomm_wfree;
+ }
+
+-static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
+ {
+- if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
+ struct sk_buff *skb = alloc_skb(size, priority);
+ if (skb) {
+ rfcomm_set_owner_w(skb, dev);
+@@ -328,12 +338,14 @@
+
+ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
+
+- if (!capable(CAP_NET_ADMIN))
+- return -EPERM;
+-
+ if (!(dev = rfcomm_dev_get(req.dev_id)))
+ return -ENODEV;
+
++ if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
++ rfcomm_dev_put(dev);
++ return -EPERM;
++ }
+
-+#ifdef CONFIG_REED_SOLOMON_DEC16
-+/**
-+ * decode_rs16 - Decode codeword (16bit data width)
-+ *
-+ * @rs: the rs control structure
-+ * @data: data field of a given type
-+ * @par: received parity data field
-+ * @len: data length
-+ * @s: syndrome data field (if NULL, syndrome is calculated)
-+ * @no_eras: number of erasures
-+ * @eras_pos: position of erasures, can be NULL
-+ * @invmsk: invert data mask (will be xored on data, not on parity!)
-+ * @corr: buffer to store correction bitmask on eras_pos
-+ *
-+ * Each field in the data array contains up to symbol size bits of valid data.
-+ */
-+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
-+ uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+ uint16_t *corr)
+ if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+ rfcomm_dlc_close(dev->dlc, 0);
+
+@@ -347,7 +359,7 @@
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ u16 dev_num;
+
+ BT_DBG("");
+@@ -355,14 +367,11 @@
+ if (get_user(dev_num, (u16 *) arg))
+ return -EFAULT;
+
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
+ return -EINVAL;
+
+ size = sizeof(*dl) + dev_num * sizeof(*di);
+
+- if (verify_area(VERIFY_WRITE, (void *)arg, size))
+- return -EFAULT;
+-
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
+
+@@ -387,9 +396,10 @@
+ dl->dev_num = n;
+ size = sizeof(*dl) + n * sizeof(*di);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
+- return 0;
++
++ return err ? -EFAULT : 0;
+ }
+
+ static int rfcomm_get_dev_info(unsigned long arg)
+@@ -486,7 +496,8 @@
+ rfcomm_dev_del(dev);
+
+ /* We have to drop DLC lock here, otherwise
+- * rfcomm_dev_put() will dead lock if it's the last refference */
++ rfcomm_dev_put() will dead lock if it's
++ the last reference. */
+ rfcomm_dlc_unlock(dlc);
+ rfcomm_dev_put(dev);
+ rfcomm_dlc_lock(dlc);
+@@ -541,6 +552,10 @@
+
+ BT_DBG("tty %p id %d", tty, id);
+
++ /* We don't leak this refcount. For reasons which are not entirely
++ clear, the TTY layer will call our ->close() method even if the
++ open fails. We decrease the refcount there, and decreasing it
++ here too would cause breakage. */
+ dev = rfcomm_dev_get(id);
+ if (!dev)
+ return -ENODEV;
+@@ -627,9 +642,9 @@
+ size = min_t(uint, count, dlc->mtu);
+
+ if (from_user)
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
+ else
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
+
+ if (!skb)
+ break;
+@@ -653,6 +668,27 @@
+ return sent ? sent : err;
+ }
+
++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
-+#include "decode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(decode_rs16);
-+#endif
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
+
-+EXPORT_SYMBOL_GPL(init_rs);
-+EXPORT_SYMBOL_GPL(free_rs);
++ BT_DBG("tty %p char %x", tty, ch);
+
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
-+MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
++ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
+
---- linux-2.4.21/mm/swap.c~swap-performance
-+++ linux-2.4.21/mm/swap.c
-@@ -28,7 +28,7 @@
- int page_cluster;
++ if (!skb)
++ return;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ *(char *)skb_put(skb, 1) = ch;
++
++ if ((rfcomm_dlc_send(dlc, skb)) < 0)
++ kfree_skb(skb);
++}
++
+ static int rfcomm_tty_write_room(struct tty_struct *tty)
+ {
+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+@@ -879,6 +915,7 @@
+
+ open: rfcomm_tty_open,
+ close: rfcomm_tty_close,
++ put_char: rfcomm_tty_put_char,
+ write: rfcomm_tty_write,
+ write_room: rfcomm_tty_write_room,
+ chars_in_buffer: rfcomm_tty_chars_in_buffer,
+--- linux-2.4.21/net/bluetooth/sco.c~bluetooth
++++ linux-2.4.21/net/bluetooth/sco.c
+@@ -332,8 +332,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted channels */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ sco_sock_close(sk);
++ sco_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -388,8 +390,6 @@
+ };
- pager_daemon_t pager_daemon = {
-- 512, /* base number for calculating the number of tries */
-+ 128, /* base number for calculating the number of tries */
- SWAP_CLUSTER_MAX, /* minimum number of tries */
- 8, /* do swap I/O in clusters of this size */
- };
---- linux-2.4.21/mm/vmalloc.c~vmalloc
-+++ linux-2.4.21/mm/vmalloc.c
-@@ -183,6 +183,9 @@
- return NULL;
+ release_sock(sk);
+-
+- sco_sock_kill(sk);
+ }
- size += PAGE_SIZE;
-+#ifdef VMALLOC_ALIGN
-+ size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
-+#endif
- if (!size) {
- kfree (area);
- return NULL;
+ static void sco_sock_init(struct sock *sk, struct sock *parent)
+@@ -508,7 +508,8 @@
+ if ((err = sco_connect(sk)))
+ goto done;
+
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -712,16 +713,23 @@
+ static int sco_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
++ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+- sock_orphan(sk);
+ sco_sock_close(sk);
++ if (sk->linger) {
++ lock_sock(sk);
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ release_sock(sk);
++ }
+
+- return 0;
++ sock_orphan(sk);
++ sco_sock_kill(sk);
++ return err;
+ }
+
+ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+--- linux-2.4.21/net/bluetooth/syms.c~bluetooth
++++ linux-2.4.21/net/bluetooth/syms.c
+@@ -78,4 +78,4 @@
+ EXPORT_SYMBOL(bluez_sock_poll);
+ EXPORT_SYMBOL(bluez_accept_enqueue);
+ EXPORT_SYMBOL(bluez_accept_dequeue);
+-EXPORT_SYMBOL(bluez_sock_w4_connect);
++EXPORT_SYMBOL(bluez_sock_wait_state);
--- linux-2.4.21/net/core/wireless.c~linux-iw241_we16-6
+++ linux-2.4.21/net/core/wireless.c
@@ -2,7 +2,7 @@
/* Oh, well. At least we tried. */
--- linux-2.4.21/net/netsyms.c~linux-iw241_we16-6
+++ linux-2.4.21/net/netsyms.c
-@@ -601,6 +601,11 @@
+@@ -160,6 +160,7 @@
+ EXPORT_SYMBOL(put_cmsg);
+ EXPORT_SYMBOL(sock_kmalloc);
+ EXPORT_SYMBOL(sock_kfree_s);
++EXPORT_SYMBOL(sockfd_lookup);
+
+ #ifdef CONFIG_FILTER
+ EXPORT_SYMBOL(sk_run_filter);
+@@ -601,6 +602,11 @@
#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
#include <net/iw_handler.h>
EXPORT_SYMBOL(wireless_send_event);