From 99c724568b2c8e6d1b9857435ebe601854615f53 Mon Sep 17 00:00:00 2001 From: "nslu2-linux.adm@bkbits.net" Date: Wed, 30 Mar 2005 13:19:04 +0000 Subject: Merge bk://oe-devel.bkbits.net/openembedded into bkbits.net:/repos/n/nslu2-linux/openembedded 2005/03/30 14:00:48+02:00 mn-solutions.de!schurig MNCI "Ramses": newest bluez for the kernel 2005/03/30 13:59:44+02:00 mn-solutions.de!schurig bluez-utils install the binaries into /{s}bin, not /usr/{s}bin, this patch fixes the scripts 2005/03/30 13:54:17+02:00 utwente.nl!koen Merge bk://oe-devel@oe-devel.bkbits.net/openembedded into bitbake.utwente.nl:/home/koen/OE/openembedded 2005/03/30 13:53:47+02:00 utwente.nl!koen preferred-gpe-versions.inc: add working minimo CVSDATE BKrev: 424aa748Ew_6rzmEpNqOmQsr4KaxYw --- packages/bluez-utils/bluez-utils-common_2.14.inc | 1 + packages/bluez-utils/files/base.patch | 0 packages/bluez-utils/files/hcid.conf | 2 +- packages/bluez-utils/files/openslug/hcid.conf | 70 + .../bluez-utils/files/ramses/bluetooth.default | 0 packages/bluez-utils/files/ramses/hcid.conf | 0 .../mnci-combined.patch | 47483 +++++++++++-------- packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb | 2 +- 8 files changed, 27785 insertions(+), 19773 deletions(-) create mode 100644 packages/bluez-utils/files/base.patch create mode 100644 packages/bluez-utils/files/ramses/bluetooth.default create mode 100644 packages/bluez-utils/files/ramses/hcid.conf (limited to 'packages') diff --git a/packages/bluez-utils/bluez-utils-common_2.14.inc b/packages/bluez-utils/bluez-utils-common_2.14.inc index bc92e28755..08489c7427 100644 --- a/packages/bluez-utils/bluez-utils-common_2.14.inc +++ b/packages/bluez-utils/bluez-utils-common_2.14.inc @@ -11,6 +11,7 @@ FILES_${PN}-ciptool = "/bin/ciptool" FILES_${PN}-bluepin = "/bin/bluepin" SRC_URI = "http://bluez.sourceforge.net/download/bluez-utils-${PV}.tar.gz \ + file://base.patch;patch=1 \ file://hcid.conf \ file://bluetooth.default" diff --git a/packages/bluez-utils/files/base.patch b/packages/bluez-utils/files/base.patch new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/bluez-utils/files/hcid.conf b/packages/bluez-utils/files/hcid.conf index 6a1e06f131..547602d4a9 100644 --- a/packages/bluez-utils/files/hcid.conf +++ b/packages/bluez-utils/files/hcid.conf @@ -23,7 +23,7 @@ options { pairing multi; # PIN helper - pin_helper /usr/bin/bluepin; + pin_helper /bin/bluepin; # D-Bus PIN helper # dbus_pin_helper; diff --git a/packages/bluez-utils/files/openslug/hcid.conf b/packages/bluez-utils/files/openslug/hcid.conf index e69de29bb2..21cf44a699 100644 --- a/packages/bluez-utils/files/openslug/hcid.conf +++ b/packages/bluez-utils/files/openslug/hcid.conf @@ -0,0 +1,70 @@ +# +# HCI daemon configuration file. +# +# $Id$ +# + +# HCId options +options { + # Automatically initialize new devices + autoinit yes; + + # Security Manager mode + # none - Security manager disabled + # auto - Use local PIN for incoming connections + # user - Always ask user for a PIN + # + security auto; + + # Pairing mode + # none - Pairing disabled + # multi - Allow pairing with already paired devices + # once - Pair once and deny successive attempts + pairing multi; + + # PIN helper + # pin_helper /bin/bluepin; + + # D-Bus PIN helper + # dbus_pin_helper; +} + +# Default settings for HCI devices +device { + # Local device name + # %d - device id + # %h - host name + name "%h"; + + # Local device class + class 0x820100; + + # Default packet type + #pkt_type DH1,DM1,HV1; + + # Inquiry and Page scan + iscan enable; pscan enable; + + # Default link mode + # none - no specific policy + # accept - always accept incoming connections + # master - become master on incoming connections, + # deny role switch on outgoing connections + # + lm accept,master; + + # Default link policy + # none - no specific policy + # rswitch - allow role switch + # hold - allow hold mode + # sniff - allow sniff mode + # park - allow park mode + # + #lp hold,sniff; + # + lp rswitch,hold,sniff,park; + + # Authentication and Encryption + auth enable; + encrypt enable; +} diff --git a/packages/bluez-utils/files/ramses/bluetooth.default b/packages/bluez-utils/files/ramses/bluetooth.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/bluez-utils/files/ramses/hcid.conf b/packages/bluez-utils/files/ramses/hcid.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch index 2f80d090a2..d617ac80ce 100644 --- a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch +++ b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch @@ -28,6 +28,7 @@ # vmalloc.patch # usb-sl811.patch # orinoco013e.patch +# bluetooth.patch # ramses.patch # ramses-ac97.patch # ramses-keyb.patch @@ -49,6 +50,201 @@ # 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 . ++ + 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 . +- +- If you want to compile BlueZ Core as module (bluez.o) say M here. ++ For more information, see . + + 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 . ++ 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 . +- + 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 @@ @@ -1660,6 +1856,314 @@ + + + +--- 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 ++ ++ 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 ++ * ++ * Sample code on how to use request_firmware() from drivers. ++ * ++ * Note that register_firmware() is currently useless. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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 @@ @@ -1783,7 +2287,7 @@ --- /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 +# @@ -2422,6 +2926,7 @@ +# 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 + +# @@ -2913,26 +3418,27 @@ +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 @@ -2958,6 +3464,7 @@ +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 @@ @@ -4075,7 +4582,7 @@ +#endif +#define USE_UCB +//#define PFI_LED -+//#define PFI_TURNOFF ++#define PFI_TURNOFF + +#include +#include @@ -4927,833 +5434,2237 @@ 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 + * -+ * 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 +#include -+#include + +#include -+#include -+#include -+#include +#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include ++#include ++#include + -+#include "ds1337.h" ++#include ++#include + -+//#define DEBUG 1 ++#include ++#include + -+#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 "); + -+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 "); ++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 + #include + +-#define __KERNEL_SYSCALLS__ +- + #include + #include + #include +@@ -48,6 +46,8 @@ + #include + #include + ++#include + -+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) -+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) + #include + #include + #include +@@ -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 + + #include +-#include + #include + #include + #include +@@ -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 + + #include +-#include + #include + #include + #include +--- 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 + #include +@@ -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 "); ++MODULE_AUTHOR("Maxim Krasnyansky , Marcel Holtmann "); + 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 ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 @@ -6736,7 +8647,7 @@ 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} }; @@ -6820,12 +8731,16 @@ + 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, + } }; @@ -6856,7 +8771,7 @@ #ifndef PREPARE_FUNC #define PREPARE_FUNC(dev) (dev->prepare) -@@ -403,39 +279,21 @@ +@@ -403,39 +283,21 @@ #endif @@ -6905,7 +8820,7 @@ return readl((unsigned long) info->iomem_base + (offset<iomem_reg_shift)); default: -@@ -447,17 +305,14 @@ +@@ -447,17 +309,14 @@ int value) { switch (info->io_type) { @@ -6926,7 +8841,7 @@ writel(value, (unsigned long) info->iomem_base + (offset<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; @@ -6936,7 +8851,7 @@ 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; @@ -6946,7 +8861,7 @@ 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)); @@ -6958,7 +8873,7 @@ } 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++; @@ -6970,7 +8885,7 @@ } if (status & UART_MSR_DCTS) icount->cts++; -@@ -810,120 +650,23 @@ +@@ -810,120 +654,23 @@ } } @@ -7092,7 +9007,7 @@ 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); @@ -7217,7 +9132,7 @@ /* * ------------------------------------------------------------------- -@@ -1107,22 +753,6 @@ +@@ -1107,22 +757,6 @@ if (!info) continue; save_flags(flags); cli(); @@ -7240,7 +9155,7 @@ 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(); @@ -7252,7 +9167,7 @@ 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; } @@ -7303,7 +9218,7 @@ 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; @@ -7313,7 +9228,7 @@ 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 @@ -7336,7 +9251,7 @@ 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); } @@ -7363,7 +9278,7 @@ case (long)&STUART: CKEN |= CKEN5_STUART; break; } } -@@ -1344,6 +926,7 @@ +@@ -1344,6 +930,7 @@ /* * Clear the interrupt registers. */ @@ -7371,7 +9286,7 @@ (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]) { @@ -7390,7 +9305,7 @@ } 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; @@ -7403,7 +9318,7 @@ { 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)) @@ -7423,7 +9338,7 @@ /* * And clear the interrupt registers again for luck. */ -@@ -1469,7 +1027,6 @@ +@@ -1469,7 +1031,6 @@ /* * Set up the tty->alt_speed kludge */ @@ -7431,7 +9346,7 @@ 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; } @@ -7439,7 +9354,7 @@ /* * and set the speed of the serial port -@@ -1516,6 +1072,30 @@ +@@ -1516,6 +1076,30 @@ state->irq); #endif @@ -7470,7 +9385,7 @@ 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 */ @@ -7484,7 +9399,7 @@ 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); @@ -7501,7 +9416,7 @@ #ifdef CONFIG_ARCH_PXA if (state->type == PORT_PXA #ifdef CONFIG_SERIAL_CONSOLE -@@ -1634,37 +1197,6 @@ +@@ -1634,37 +1201,6 @@ restore_flags(flags); } @@ -7539,7 +9454,7 @@ /* * 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 */ @@ -7552,7 +9467,7 @@ 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; @@ -7563,7 +9478,7 @@ 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; @@ -7573,7 +9488,7 @@ 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; @@ -7583,7 +9498,7 @@ 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); @@ -7592,7 +9507,7 @@ restore_flags(flags); } -@@ -1912,9 +1426,6 @@ +@@ -1912,9 +1430,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; @@ -7602,7 +9517,7 @@ 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); @@ -7614,7 +9529,7 @@ } return ret; } -@@ -1991,8 +1497,6 @@ +@@ -1991,8 +1501,6 @@ { struct async_struct *info = (struct async_struct *)tty->driver_data; @@ -7623,7 +9538,7 @@ 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; @@ -7632,7 +9547,7 @@ 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; @@ -7641,7 +9556,7 @@ 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; @@ -7658,7 +9573,7 @@ } } -@@ -2064,9 +1559,6 @@ +@@ -2064,9 +1563,6 @@ tty->ldisc.chars_in_buffer(tty)); #endif @@ -7668,7 +9583,7 @@ 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 @@ -7678,7 +9593,7 @@ 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; @@ -7686,7 +9601,7 @@ 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; @@ -7696,7 +9611,7 @@ 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]) && @@ -7704,7 +9619,7 @@ (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; @@ -7723,7 +9638,7 @@ 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; @@ -7756,7 +9671,7 @@ 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; @@ -7764,7 +9679,7 @@ change_speed(info, 0); } } else -@@ -2414,60 +1880,14 @@ +@@ -2414,60 +1884,14 @@ return 0; } @@ -7825,7 +9740,7 @@ 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); } @@ -7947,7 +9862,7 @@ 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; @@ -7960,7 +9875,7 @@ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && -@@ -2616,45 +1915,6 @@ +@@ -2616,45 +1919,6 @@ } switch (cmd) { @@ -8006,7 +9921,7 @@ 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); @@ -8016,7 +9931,7 @@ 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; @@ -8032,7 +9947,7 @@ /* * 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; @@ -8072,7 +9987,7 @@ default: return -ENOIOCTLCMD; } -@@ -2801,18 +2082,6 @@ +@@ -2801,18 +2086,6 @@ tty->hw_stopped = 0; rs_start(tty); } @@ -8091,7 +10006,7 @@ } /* -@@ -2831,9 +2100,6 @@ +@@ -2831,9 +2104,6 @@ struct serial_state *state; unsigned long flags; @@ -8101,7 +10016,7 @@ 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; @@ -8113,7 +10028,7 @@ 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 @@ -8126,7 +10041,7 @@ #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 @@ -8137,7 +10052,7 @@ } /* -@@ -2998,9 +2264,6 @@ +@@ -2998,9 +2268,6 @@ struct async_struct * info = (struct async_struct *)tty->driver_data; struct serial_state *state = info->state; @@ -8147,7 +10062,7 @@ 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); @@ -8160,7 +10075,7 @@ } /* -@@ -3114,14 +2373,10 @@ +@@ -3114,14 +2377,10 @@ set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { @@ -8175,7 +10090,7 @@ break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && -@@ -3223,16 +2478,12 @@ +@@ -3223,16 +2482,12 @@ } tty->driver_data = info; info->tty = tty; @@ -8192,7 +10107,7 @@ /* * 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); @@ -8205,7 +10120,7 @@ } /* -@@ -3313,17 +2560,14 @@ +@@ -3313,17 +2564,14 @@ int ret; unsigned long flags; @@ -8229,7 +10144,7 @@ /* * 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; @@ -8237,7 +10152,7 @@ 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, @@ -8254,7 +10169,7 @@ 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 @@ */ /* @@ -8573,7 +10488,7 @@ } /* -@@ -3894,1760 +3224,8 @@ +@@ -3894,1762 +3228,10 @@ restore_flags(flags); } @@ -8827,15 +10742,15 @@ - 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 @@ -9194,7 +11109,7 @@ -#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 */ @@ -9209,7 +11124,7 @@ - 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 */ @@ -10331,10 +12246,12 @@ - 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); @@ -10347,7 +12264,7 @@ 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); @@ -10356,7 +12273,7 @@ } /** -@@ -5785,7 +3360,6 @@ +@@ -5785,7 +3364,6 @@ int i; struct async_struct *info; @@ -10364,7 +12281,7 @@ 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) { @@ -10422,7 +12339,7 @@ } module_init(rs_init); -@@ -5946,7 +3510,7 @@ +@@ -5946,7 +3514,7 @@ static struct async_struct *info; struct serial_state *state; unsigned cval; @@ -10431,7 +12348,7 @@ int bits = 8; int parity = 'n'; int doflow = 0; -@@ -5954,6 +3518,8 @@ +@@ -5954,6 +3522,8 @@ int quot = 0; char *s; @@ -10440,7 +12357,7 @@ 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; @@ -10460,7 +12377,7 @@ if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) -@@ -6082,10 +3641,15 @@ +@@ -6082,10 +3645,15 @@ */ void __init serial_console_init(void) { @@ -11451,7 +13368,15 @@ 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 @@ @@ -11462,7 +13387,7 @@ # Object file lists. -@@ -21,10 +21,12 @@ +@@ -21,10 +21,13 @@ obj-$(CONFIG_INPUT) += input.o obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o @@ -11470,11 +13395,48 @@ 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 @@ @@ -12320,6 +14282,437 @@ + +#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 ++ * ++ * Changes/Revisions: ++ * 0.1 20/06/2002 ++ * - first public version ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 @@ +/* @@ -12563,6 +14956,106 @@ + +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 @@ @@ -70016,6 +72509,17 @@ /* 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 @@ @@ -78365,92 +80869,379 @@ + 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 @@ -78459,20 +81250,25 @@ + } + + 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; + @@ -78481,778 +81277,1802 @@ + 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 + -+ 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 + * +- * 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 + #include + #include ++#include ++#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 + * +- * 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 + #include ++#include + #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 +- * +- * 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 + * +- * 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 + #include +-#include /* for min() */ + #include +-#include + #include ++#include ++#include + #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 ++#include + -+ 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 -+ -+/*#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 + +-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 +- * +- * 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 + * +- * 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 + #include ++#include + #include +-#include /* For completion */ ++#include + #include + #include + #include ++#include + #include "nodelist.h" +-#include "crc32.h" ++ ++/* Urgh. Please tell me there's a nicer way of doing these. */ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) ++typedef int mknod_arg_t; ++#define NAMEI_COMPAT(x) ((void *)x) ++#else ++typedef dev_t mknod_arg_t; ++#define NAMEI_COMPAT(x) (x) +#endif -+static 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_sizeinocache->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 - * -- * 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 - #include - #include -+#include -+#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 - * -- * 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 - #include -+#include - #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 - * @@ -79289,307 +83109,480 @@ + * $Id$ * */ - --#ifndef __KERNEL__ -+#if !defined(__KERNEL__) && !defined(__ECOS) - #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" - #endif - - #include ++ #include --#include /* for min() */ #include + #include -#include - #include -+#include -+#include +-#include ++#include ++#include ++#include ++#include #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 -+#include -+ -+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; icleanmarker_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 +- 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. * @@ -79631,1454 +83624,2740 @@ * */ ++#include #include +-#include /* for min() */ #include -+#include #include --#include /* For completion */ ++#include + #include ++#include +#include #include - #include - #include -+#include #include "nodelist.h" -#include "crc32.h" -+ -+/* Urgh. Please tell me there's a nicer way of doing these. */ -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) -+typedef int mknod_arg_t; -+#define NAMEI_COMPAT(x) ((void *)x) -+#else -+typedef dev_t mknod_arg_t; -+#define NAMEI_COMPAT(x) (x) -+#endif - static int jffs2_readdir (struct file *, void *, filldir_t); + 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 ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id$ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "nodelist.h" ++ ++ ++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ++{ ++ struct jffs2_full_dnode *old_metadata, *new_metadata; ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_raw_inode *ri; ++ unsigned short dev; ++ unsigned char *mdata = NULL; ++ int mdatalen = 0; ++ unsigned int ivalid; ++ uint32_t phys_ofs, alloclen; ++ int ret; ++ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ++ ret = inode_change_ok(inode, iattr); ++ if (ret) ++ return ret; ++ ++ /* Special cases - we don't want more than one data node ++ for these types on the medium at any time. So setattr ++ must read the original data associated with the node ++ (i.e. the device numbers or the target name) and write ++ it out again with the appropriate data attached */ ++ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { ++ /* For these, we don't actually need to read the old node */ ++ dev = old_encode_dev(inode->i_rdev); ++ mdata = (char *)&dev; ++ mdatalen = sizeof(dev); ++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); ++ } else if (S_ISLNK(inode->i_mode)) { ++ mdatalen = f->metadata->size; ++ mdata = kmalloc(f->metadata->size, GFP_USER); ++ if (!mdata) ++ return -ENOMEM; ++ ret = jffs2_read_dnode(c, f, 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 +- * +- * 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 + * +- * 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 + #include + #include +-#include +-#include +-#include + #include ++#include ++#include ++#include + #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 -- * -- * 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 - * -- * 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 - #include - #include --#include --#include -+#include -+#include -+#include -+#include - #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; icleanmarker_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. * @@ -81120,1285 +86399,1550 @@ * */ -+#include - #include --#include /* for min() */ - #include - #include -+#include - #include -+#include -+#include - #include - #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 +- * +- * 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 + * +- * 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 +- * +- * 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 -+ * + * +- * 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 -+#include -+#include + * + */ + + #include +-#include +#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include + #include + #include ++#include +#include -+#include "nodelist.h" -+ -+ -+static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ++#include ++#include + #include "nodelist.h" + + void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) +@@ -78,7 +58,7 @@ + /* Put a new tmp_dnode_info into the list, keeping the list in + order of increasing version + */ +-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) ++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) + { + struct jffs2_tmp_dnode_info **prev = list; + +@@ -89,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 +- * +- * 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 + * +- * 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 + #include +- ++#include ++#include + #include + #include + ++#ifdef __ECOS ++#include "os-ecos.h" ++#else ++#include /* 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. * @@ -82441,1717 +87985,1153 @@ */ #include - #include #include -#include --#include + #include -#include - #include -+#include +#include -+#include ++#include /* 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 :)) */ + 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 ++ * ++ * 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 + -+ 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 -- * -- * 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 - * -- * 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 -- * -- * 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 - * -- * 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 - * @@ -84167,7 +89147,7 @@ - * 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 @@ -84189,660 +89169,678 @@ * */ - #include --#include -+#include - #include - #include -+#include -+#include -+#include -+#include - #include "nodelist.h" - - void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) -@@ -78,7 +58,7 @@ - /* Put a new tmp_dnode_info into the list, keeping the list in - order of increasing version - */ --void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) -+static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) - { - struct jffs2_tmp_dnode_info **prev = list; - -@@ -89,93 +69,156 @@ - *prev = tn; - } + #ifndef __PUSHPULL_H__ + #define __PUSHPULL_H__ ++ ++#include ++ + 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 ++ (C) 2002 David Woodhouse ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. + -+ 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 ++#include ++ ++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \ ++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE)) ++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * right = node->rb_right; ++ ++ if ((node->rb_right = right->rb_left)) ++ right->rb_left->rb_parent = node; ++ right->rb_left = node; ++ ++ if ((right->rb_parent = node->rb_parent)) ++ { ++ if (node == node->rb_parent->rb_left) ++ node->rb_parent->rb_left = right; ++ else ++ node->rb_parent->rb_right = right; + } ++ else ++ root->rb_node = right; ++ node->rb_parent = right; +} + -+/* 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 +- * +- * 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 + * +- * 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 + #include +-#include ++#include ++#include + #include ++#include + #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. * @@ -84884,561 +89882,1012 @@ * */ -+#ifndef __JFFS2_NODELIST_H__ -+#define __JFFS2_NODELIST_H__ -+ - #include +-/* Given an inode, probably with existing list of fragments, add the new node +- * to the fragment list. +- */ + #include + #include #include -- -+#include -+#include - #include - #include ++#include ++#include + #include +-#include ++#include + #include "nodelist.h" +-#include "crc32.h" -+#ifdef __ECOS -+#include "os-ecos.h" -+#else -+#include /* 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. * @@ -85479,925 +90928,1267 @@ + * $Id$ * */ - #include ++#include #include -#include #include --#include + #include ++#include +#include -+#include /* 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; inr_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 :)) */ - 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 + * @@ -86407,223 +92198,166 @@ + * + */ + -+#ifndef __JFFS2_OS_LINUX_H__ -+#define __JFFS2_OS_LINUX_H__ ++#include ++#include ++#include +#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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. * @@ -86643,7 +92377,7 @@ - * 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 @@ -86665,683 +92399,658 @@ * */ - #ifndef __PUSHPULL_H__ - #define __PUSHPULL_H__ -+ -+#include -+ - struct pushpull { - unsigned char *buf; - unsigned int buflen; -@@ -44,9 +23,36 @@ - unsigned int reserve; - }; - --void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned); --int pushbit(struct pushpull *pp, int bit, int use_reserved); --int pushedbits(struct pushpull *pp); -+ -+static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) -+{ -+ pp->buf = buf; -+ pp->buflen = buflen; -+ pp->ofs = ofs; -+ pp->reserve = reserve; -+} -+ -+static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) -+{ -+ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { -+ return -ENOSPC; -+ } -+ -+ if (bit) { -+ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); -+ } -+ else { -+ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); -+ } -+ pp->ofs++; -+ -+ return 0; -+} -+ -+static inline int pushedbits(struct pushpull *pp) -+{ -+ return pp->ofs; -+} - - static inline int pullbit(struct pushpull *pp) - { ---- /dev/null -+++ linux-2.4.21/fs/jffs2/rbtree.c -@@ -0,0 +1,363 @@ -+/* -+ Red Black Trees -+ (C) 1999 Andrea Arcangeli -+ (C) 2002 David Woodhouse -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ -+ $Id$ -+*/ -+ -+#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */ -+#error "Licence problem. eCos has its own rbtree code." -+#endif -+ -+#include -+#include + #include + #include + #include +-#include + #include + #include + #include + #include ++#include + #include + #include + #include +-#include ++#include ++#include ++#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; inr_blocks; i++) { +- INIT_LIST_HEAD(&c->blocks[i].list); +- c->blocks[i].offset = i * c->sector_size; +- c->blocks[i].free_size = c->sector_size; +- c->blocks[i].dirty_size = 0; +- c->blocks[i].used_size = 0; +- c->blocks[i].first_node = NULL; +- c->blocks[i].last_node = NULL; + } ++ 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 -- * -- * 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 - * -- * 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 - #include --#include -+#include -+#include - #include -+#include - #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 ++ * ++ * 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 ++#include ++#include ++#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 - * - * The original JFFS, from which the design for JFFS2 was derived, @@ -87356,8 +93065,7 @@ - * 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 * @@ -87378,3117 +93086,2232 @@ * */ --/* Given an inode, probably with existing list of fragments, add the new node -- * to the fragment list. -- */ +@@ -39,72 +15,49 @@ #include #include #include -+#include -+#include - #include -#include -+#include ++#include #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 ++ * ++ * Created by David Woodhouse ++ * Modified debugged and enhanced by Thomas Gleixner ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id$ ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "nodelist.h" ++ ++/* For testing write failures */ ++#undef BREAKME ++#undef BREAKMEHEADER ++ ++#ifdef BREAKME ++static unsigned char *brokenbuf; ++#endif ++ ++/* max. erase failures before we mark a block bad */ ++#define MAX_ERASE_FAILURES 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 -- * -- * 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 - * -- * 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 -+#include - #include --#include - #include - #include -+#include -+#include - #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; inr_blocks; i++) { - struct jffs2_eraseblock *jeb = &c->blocks[i]; - -- ret = jffs2_scan_eraseblock(c, jeb); -+ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); ++/* ++* 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 +- * +- * 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 + * +- * 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 + #include +-#include ++#include ++#include ++#include + #include + #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; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); +- totlen += thislen; +- if (ret || thislen != vecs[i].iov_len) +- break; +- to += vecs[i].iov_len; - } -+ if ((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 -+ * -+ * For licensing information, see the file 'LICENCE' in this directory. -+ * -+ * $Id$ -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#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 -- * -- * 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 - * -- * 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 - #include - #include --#include - #include - #include - #include - #include -+#include - #include - #include - #include --#include -+#include -+#include -+#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; inr_blocks; i++) { -- INIT_LIST_HEAD(&c->blocks[i].list); -- c->blocks[i].offset = i * c->sector_size; -- c->blocks[i].free_size = c->sector_size; -- c->blocks[i].dirty_size = 0; -- c->blocks[i].used_size = 0; -- c->blocks[i].first_node = NULL; -- c->blocks[i].last_node = NULL; - } -+ 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. + * @@ -90502,9263 +95325,12373 @@ + * + */ + -+ +#include -+#include -+#include ++#include +#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; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); ++ totlen += thislen; ++ if (ret || thislen != vecs[i].iov_len) ++ break; ++ to += vecs[i].iov_len; + } -+ -+ 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 -- * -- * 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 - * -- * 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 - #include - #include --#include -+#include - #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 ++ * linux/include/asm-arm/arch-pxa/ramses.h + * -+ * Created by David Woodhouse -+ * Modified debugged and enhanced by Thomas Gleixner ++ * 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 ++ * Initial code + * ++ * 2002-10-08: adaption from PXA IDP to Ramses ++ * + */ + -+#include -+#include -+#include -+#include -+#include -+#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<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 + -+ 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 + -+ 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 ++#include ++#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 +- * +- * 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 + * +- * 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 ++/* 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 +-#endif ++#include ++#include ++#include + + struct jffs2_inode_info { +-#ifdef THISSUCKS +- struct pipe_inode_info pipecrap; +-#endif + /* We need an internal semaphore similar to inode->i_sem. + Unfortunately, we can't used the existing one, because + either the GC would deadlock, or we'd have to release it +@@ -26,10 +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 + #include ++#include + #include + #include ++#include ++#include + #include +- +-#define INOCACHE_HASHSIZE 1 ++#include + + #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 ++ * ++ * 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 + -+ /* 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 ++#include + #include + #include + #include + #include ++#include + #include + +-/* +- * 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 +-#include +-#ifndef LINUX_VERSION_CODE + #include +-#endif +- +-#ifndef VERSION_CODE +-# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) +-#endif +-#ifndef KERNEL_VERSION +-# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) +-# error "This kernel is too old: not supported by this file" +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +-#include /* used later in this header */ +- +-#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +-#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) +- +-typedef struct wait_queue * wait_queue_head_t; +- +-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL} +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue +-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX +-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED +- +-/* from sysdep-2.1.h */ +-# include +-# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1) +-# define verify_area_20 verify_area +-# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0) +-# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n)) +-# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n)) +-# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0) +-# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n)) +-# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n)) +-//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0) +-# define Put_user(val,add) (put_user((val),(add)), 0) +-# define __PUT_USER(val,add) PUT_USER((val),(add)) +-# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add)) +-# define GET_USER(dest,add) ((dest)=get_user((add)), 0) +-# define __GET_USER(dest,add) GET_USER((dest),(add)) +-# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add)) +- +-#define ioremap(offset,size) vremap(offset,size) +-#define iounmap(adr) /* */ +- +-#define EXPORT_SYMBOL(s) /* */ +-#define EXPORT_SYMBOL_NOVERS(s) /* */ +- +-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */ +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10) +- +-# include +-# ifdef __LITTLE_ENDIAN +-# define cpu_to_le16(x) (x) +-# define cpu_to_le32(x) (x) +-# define cpu_to_be16(x) htons((x)) +-# define cpu_to_be32(x) htonl((x)) +-# else +-# define cpu_to_be16(x) (x) +-# define cpu_to_be32(x) (x) +- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);} +- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) | +- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} +-# endif +- +-# define le16_to_cpu(x) cpu_to_le16(x) +-# define le32_to_cpu(x) cpu_to_le32(x) +-# define be16_to_cpu(x) cpu_to_be16(x) +-# define be32_to_cpu(x) cpu_to_be32(x) +- +-#endif +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43) +-# define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) +-# define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) +-# define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) +-# define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) +- +- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);} +- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);} +- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);} +- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);} +- +-# define le16_to_cpup(x) cpu_to_le16p(x) +-# define le32_to_cpup(x) cpu_to_le32p(x) +-# define be16_to_cpup(x) cpu_to_be16p(x) +-# define be32_to_cpup(x) cpu_to_be32p(x) +- +-# define le16_to_cpus(x) cpu_to_le16s(x) +-# define le32_to_cpus(x) cpu_to_le32s(x) +-# define be16_to_cpus(x) cpu_to_be16s(x) +-# define be32_to_cpus(x) cpu_to_be32s(x) +-#endif +- +-// from 2.2, linux/types.h +-#ifndef __BIT_TYPES_DEFINED__ +-#define __BIT_TYPES_DEFINED__ +- +-typedef __u8 u_int8_t; +-typedef __s8 int8_t; +-typedef __u16 u_int16_t; +-typedef __s16 int16_t; +-typedef __u32 u_int32_t; +-typedef __s32 int32_t; +- +-#endif /* !(__BIT_TYPES_DEFINED__) */ +- +-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +- typedef struct { } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { } +-#else +- typedef struct { int gcc_is_buggy; } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +-#endif +- +-#define spin_lock_init(lock) do { } while(0) +-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */ +-#define spin_trylock(lock) (1) +-#define spin_unlock_wait(lock) do { } while(0) +-#define spin_unlock(lock) do { } while(0) +-#define spin_lock_irq(lock) cli() +-#define spin_unlock_irq(lock) sti() +- +-#define spin_lock_irqsave(lock, flags) \ +- do { save_flags(flags); cli(); } while (0) +-#define spin_unlock_irqrestore(lock, flags) \ +- restore_flags(flags) +- +-// Doesn't work when tqueue.h is included. +-// #define queue_task queue_task_irq_off +-#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer) +-#define signal_pending(current) (current->signal & ~current->blocked) +-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +-#define time_after(t1,t2) (((long)t1-t2) > 0) +- +-#else +- #include +-#endif // LINUX_VERSION_CODE < 0x020100 +- +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +-#include +-#endif +- +-/* Modularization issues */ +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18) +-# define __USE_OLD_SYMTAB__ +-# define EXPORT_NO_SYMBOLS register_symtab(NULL); +-# define REGISTER_SYMTAB(tab) register_symtab(tab) +-#else +-# define REGISTER_SYMTAB(tab) /* nothing */ +-#endif + +-#ifdef __USE_OLD_SYMTAB__ +-# define __MODULE_STRING(s) /* nothing */ +-# define MODULE_PARM(v,t) /* nothing */ +-# define MODULE_PARM_DESC(v,t) /* nothing */ +-# define MODULE_AUTHOR(n) /* nothing */ +-# define MODULE_DESCRIPTION(d) /* nothing */ +-# define MODULE_SUPPORTED_DEVICE(n) /* nothing */ +-#endif +- +-/* +- * "select" changed in 2.1.23. The implementation is twin, but this +- * header is new +- */ +-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22) +-# include +-#else +-# define __USE_OLD_SELECT__ ++#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 ++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 +-#endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0) +-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0) +-#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q ++#ifndef yield ++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32) +-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0) +-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn) +-#define blk_init_queue(q, rq) do {q = rq;} while(0) ++#ifndef minor ++#define major(d) (MAJOR(to_kdev_t(d))) ++#define minor(d) (MINOR(to_kdev_t(d))) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-#ifdef CONFIG_MODULES +-#define __MOD_INC_USE_COUNT(mod) \ +- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) +-#define __MOD_DEC_USE_COUNT(mod) \ +- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) +-#else +-#define __MOD_INC_USE_COUNT(mod) +-#define __MOD_DEC_USE_COUNT(mod) +-#endif ++#ifndef mk_kdev ++#define mk_kdev(ma,mi) MKDEV(ma,mi) ++#define kdev_t_to_nr(x) (x) + #endif + ++#define need_resched() (current->need_resched) ++#define cond_resched() do { if need_resched() { yield(); } } while(0) + +-#ifndef HAVE_INTER_MODULE +-static inline void *inter_module_get(char *x) {return NULL;} +-static inline void *inter_module_get_request(char *x, char *y) {return NULL;} +-static inline void inter_module_put(const char *x) {} +-static inline void inter_module_register(const char *x, struct module *y, const void *z) {} +-static inline void inter_module_unregister(const char *x) {} ++#endif /* < 2.4.20 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) ++#define iminor(i) minor((i)->i_rdev) ++#define imajor(i) major((i)->i_rdev) ++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) ++#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) ++#define old_valid_dev(d) (1) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue ++#include + ++#ifdef __rh_config_h__ ++#define sigmask_lock sighand->siglock ++#define sig sighand + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- +-static inline int try_inc_mod_count(struct module *mod) ++static inline void __daemonize_modvers(void) + { +-#ifdef CONFIG_MODULES +- if (mod) +- __MOD_INC_USE_COUNT(mod); +-#endif +- return 1; +-} +-#endif +- +- +-/* Yes, I'm aware that it's a fairly ugly hack. +- Until the __constant_* macros appear in Linus' own kernels, this is +- the way it has to be done. +- DW 19/1/00 +- */ +- +-#include +- +-#ifndef __constant_cpu_to_le16 +- +-#ifdef __BIG_ENDIAN +-#define __constant_cpu_to_le64(x) ___swab64((x)) +-#define __constant_le64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_le32(x) ___swab32((x)) +-#define __constant_le32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_le16(x) ___swab16((x)) +-#define __constant_le16_to_cpu(x) ___swab16((x)) +-#define __constant_cpu_to_be64(x) ((__u64)(x)) +-#define __constant_be64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_be32(x) ((__u32)(x)) +-#define __constant_be32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_be16(x) ((__u16)(x)) +-#define __constant_be16_to_cpu(x) ((__u16)(x)) +-#else +-#ifdef __LITTLE_ENDIAN +-#define __constant_cpu_to_le64(x) ((__u64)(x)) +-#define __constant_le64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_le32(x) ((__u32)(x)) +-#define __constant_le32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_le16(x) ((__u16)(x)) +-#define __constant_le16_to_cpu(x) ((__u16)(x)) +-#define __constant_cpu_to_be64(x) ___swab64((x)) +-#define __constant_be64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_be32(x) ___swab32((x)) +-#define __constant_be32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_be16(x) ___swab16((x)) +-#define __constant_be16_to_cpu(x) ___swab16((x)) +-#else +-#error No (recognised) endianness defined (unless it,s PDP) +-#endif /* __LITTLE_ENDIAN */ +-#endif /* __BIG_ENDIAN */ +- +-#endif /* ifndef __constant_cpu_to_le16 */ +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- #define mod_init_t int __init +- #define mod_exit_t void +-#else +- #define mod_init_t static int __init +- #define mod_exit_t static void __exit +-#endif ++ 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 +-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0) +-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0) +-#else +-#include +-#include +-#endif ++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 -- * -- * 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 - * -- * 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 - #include --#include -+#include -+#include -+#include - #include - #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 - -- 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; iwrite(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 ++#include + +-#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 +-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 */ +-/* $Id$ */ ++/* ++ * Linux driver for Disk-On-Chip devices ++ * ++ * Copyright (C) 1999 Machine Vision Holdings, Inc. ++ * Copyright (C) 2001-2003 David Woodhouse ++ * Copyright (C) 2002-2003 Greg Ungerer ++ * Copyright (C) 2002-2003 SnapGear Inc ++ * ++ * $Id$ ++ * ++ * Released under GPL ++ */ + + #ifndef __MTD_DOC2000_H__ + #define __MTD_DOC2000_H__ + + #include ++#include + + #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 + #include + #include ++#include -- 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 ++#include ++#include + - 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 + - /* 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 +-#include -- 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 + #include +-#include +-#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ 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; ifldrv->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; iinocache->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; isem); -+ 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; imagic = 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= 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; idata_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 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 + #include + #include +-#include + #include + #include + +-#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 ++#include + + #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 +- * Steven J. Hill ++ * Steven J. Hill + * Thomas Gleixner + * +- * $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 +-#include ++#include ++#include ++#include + +-/* +- * 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 -+ * -+ * 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 -+#include -+#include "nodelist.h" -+ -+/* This ought to be in core MTD code. All registered MTD devices -+ without writev should have this put in place. Bug the MTD -+ maintainer */ -+static inline int mtd_fake_writev(struct mtd_info *mtd, const struct 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; iwrite(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); -+ totlen += thislen; -+ if (ret || thislen != vecs[i].iov_len) -+ break; -+ to += vecs[i].iov_len; -+ } -+ if (retlen) -+ *retlen = totlen; -+ return ret; -+} -+ -+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct 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 */ +-/* $Id$ */ ++/* ++ * $Id$ ++ * ++ * (C) 1999-2003 David Woodhouse ++ */ - #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 +-#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 - #undef NR_IRQS - #define NR_IRQS (SA1111_IRQ_MAX + 1) +-#ifdef __KERNEL__ ++#include - #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 -+ * 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 + -+#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 ++#include ++#include + +/* -+ * 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 ++ * ++ * 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 ++ ++#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< + -+/***************************************************************** -+ * -+ * 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 ++#ifdef CONFIG_ARCH_PXA ++#include +#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 ++/* 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 ++#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 ++#include + #include - #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 -- * -- * 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 - * -- * 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 -+/* 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 --#endif -+#include -+#include -+#include - - struct jffs2_inode_info { --#ifdef THISSUCKS -- struct pipe_inode_info pipecrap; --#endif - /* We need an internal semaphore similar to inode->i_sem. - Unfortunately, we can't used the existing one, because - either the GC would deadlock, or we'd have to release it -@@ -26,10 +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 - #include -+#include - #include - #include -+#include -+#include - #include -- --#define INOCACHE_HASHSIZE 1 -+#include + 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 ++ ++ 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 -+ * -+ * 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 ++ 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 ++#include + -+ /* 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 -+#include - #include - #include - #include - #include -+#include - #include - --/* -- * 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 +- +- 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 +-#include - --#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 - /* 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 ++#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 - /* 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 + -+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 = 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 ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include_next ++#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 ++ * ++ * 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 - + * 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 /* for "caddr_t" et al */ + #include /* for "struct sockaddr" et al */ + #include /* 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 ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) ++#include_next ++#else ++#include ++#define work_struct tq_struct ++#define schedule_work(x) schedule_task(x) ++#define flush_scheduled_work flush_scheduled_tasks ++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z) ++#endif ++ ++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */ +--- /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 ++#include ++#include ++ ++#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 + -+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 + -+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 --#include --#ifndef LINUX_VERSION_CODE - #include --#endif -- --#ifndef VERSION_CODE --# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) --#endif --#ifndef KERNEL_VERSION --# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c) --#endif -- --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) --# error "This kernel is too old: not supported by this file" --#endif -- --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) --#include /* used later in this header */ -- --#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) --#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) -- --typedef struct wait_queue * wait_queue_head_t; -- --#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL} --#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL --#define init_waitqueue_head init_waitqueue --#define DECLARE_MUTEX(x) struct semaphore x = MUTEX --#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED -- --/* from sysdep-2.1.h */ --# include --# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1) --# define verify_area_20 verify_area --# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0) --# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n)) --# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n)) --# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0) --# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n)) --# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n)) --//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0) --# define Put_user(val,add) (put_user((val),(add)), 0) --# define __PUT_USER(val,add) PUT_USER((val),(add)) --# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add)) --# define GET_USER(dest,add) ((dest)=get_user((add)), 0) --# define __GET_USER(dest,add) GET_USER((dest),(add)) --# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add)) -- --#define ioremap(offset,size) vremap(offset,size) --#define iounmap(adr) /* */ -- --#define EXPORT_SYMBOL(s) /* */ --#define EXPORT_SYMBOL_NOVERS(s) /* */ -- --/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */ -- --#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10) -- --# include --# ifdef __LITTLE_ENDIAN --# define cpu_to_le16(x) (x) --# define cpu_to_le32(x) (x) --# define cpu_to_be16(x) htons((x)) --# define cpu_to_be32(x) htonl((x)) --# else --# define cpu_to_be16(x) (x) --# define cpu_to_be32(x) (x) -- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);} -- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) | -- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} --# endif -- --# define le16_to_cpu(x) cpu_to_le16(x) --# define le32_to_cpu(x) cpu_to_le32(x) --# define be16_to_cpu(x) cpu_to_be16(x) --# define be32_to_cpu(x) cpu_to_be32(x) -- --#endif -- --#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43) --# define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) --# define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) --# define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) --# define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) -- -- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);} -- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);} -- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);} -- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);} -- --# define le16_to_cpup(x) cpu_to_le16p(x) --# define le32_to_cpup(x) cpu_to_le32p(x) --# define be16_to_cpup(x) cpu_to_be16p(x) --# define be32_to_cpup(x) cpu_to_be32p(x) -- --# define le16_to_cpus(x) cpu_to_le16s(x) --# define le32_to_cpus(x) cpu_to_le32s(x) --# define be16_to_cpus(x) cpu_to_be16s(x) --# define be32_to_cpus(x) cpu_to_be32s(x) --#endif -- --// from 2.2, linux/types.h --#ifndef __BIT_TYPES_DEFINED__ --#define __BIT_TYPES_DEFINED__ -- --typedef __u8 u_int8_t; --typedef __s8 int8_t; --typedef __u16 u_int16_t; --typedef __s16 int16_t; --typedef __u32 u_int32_t; --typedef __s32 int32_t; -- --#endif /* !(__BIT_TYPES_DEFINED__) */ -- --#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) -- typedef struct { } spinlock_t; -- #define SPIN_LOCK_UNLOCKED (spinlock_t) { } --#else -- typedef struct { int gcc_is_buggy; } spinlock_t; -- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } --#endif -- --#define spin_lock_init(lock) do { } while(0) --#define spin_lock(lock) (void)(lock) /* Not "unused variable". */ --#define spin_trylock(lock) (1) --#define spin_unlock_wait(lock) do { } while(0) --#define spin_unlock(lock) do { } while(0) --#define spin_lock_irq(lock) cli() --#define spin_unlock_irq(lock) sti() -- --#define spin_lock_irqsave(lock, flags) \ -- do { save_flags(flags); cli(); } while (0) --#define spin_unlock_irqrestore(lock, flags) \ -- restore_flags(flags) -- --// Doesn't work when tqueue.h is included. --// #define queue_task queue_task_irq_off --#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer) --#define signal_pending(current) (current->signal & ~current->blocked) --#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) --#define time_after(t1,t2) (((long)t1-t2) > 0) -- --#else -- #include --#endif // LINUX_VERSION_CODE < 0x020100 -- -- --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) --#include --#endif -- --/* Modularization issues */ --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18) --# define __USE_OLD_SYMTAB__ --# define EXPORT_NO_SYMBOLS register_symtab(NULL); --# define REGISTER_SYMTAB(tab) register_symtab(tab) --#else --# define REGISTER_SYMTAB(tab) /* nothing */ --#endif - --#ifdef __USE_OLD_SYMTAB__ --# define __MODULE_STRING(s) /* nothing */ --# define MODULE_PARM(v,t) /* nothing */ --# define MODULE_PARM_DESC(v,t) /* nothing */ --# define MODULE_AUTHOR(n) /* nothing */ --# define MODULE_DESCRIPTION(d) /* nothing */ --# define MODULE_SUPPORTED_DEVICE(n) /* nothing */ --#endif -- --/* -- * "select" changed in 2.1.23. The implementation is twin, but this -- * header is new -- */ --#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22) --# include --#else --# define __USE_OLD_SELECT__ -+#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 -+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 --#endif - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) --#define init_MUTEX(x) do {*(x) = MUTEX;} while (0) --#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0) --#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) --#define RQFUNC_ARG void --#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) --#else --#define RQFUNC_ARG request_queue_t *q -+#ifndef yield -+#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) - #endif - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32) --#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0) --#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn) --#define blk_init_queue(q, rq) do {q = rq;} while(0) -+#ifndef minor -+#define major(d) (MAJOR(to_kdev_t(d))) -+#define minor(d) (MINOR(to_kdev_t(d))) - #endif - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) --#ifdef CONFIG_MODULES --#define __MOD_INC_USE_COUNT(mod) \ -- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) --#define __MOD_DEC_USE_COUNT(mod) \ -- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) --#else --#define __MOD_INC_USE_COUNT(mod) --#define __MOD_DEC_USE_COUNT(mod) --#endif -+#ifndef mk_kdev -+#define mk_kdev(ma,mi) MKDEV(ma,mi) -+#define kdev_t_to_nr(x) (x) - #endif - -+#define need_resched() (current->need_resched) -+#define cond_resched() do { if need_resched() { yield(); } } while(0) - --#ifndef HAVE_INTER_MODULE --static inline void *inter_module_get(char *x) {return NULL;} --static inline void *inter_module_get_request(char *x, char *y) {return NULL;} --static inline void inter_module_put(const char *x) {} --static inline void inter_module_register(const char *x, struct module *y, const void *z) {} --static inline void inter_module_unregister(const char *x) {} -+#endif /* < 2.4.20 */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) -+#define iminor(i) minor((i)->i_rdev) -+#define imajor(i) major((i)->i_rdev) -+#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) -+#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) -+#define old_valid_dev(d) (1) - #endif - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) - --#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL --#define init_waitqueue_head init_waitqueue -+#include - -+#ifdef __rh_config_h__ -+#define sigmask_lock sighand->siglock -+#define sig sighand - #endif - --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -- --static inline int try_inc_mod_count(struct module *mod) -+static inline void __daemonize_modvers(void) - { --#ifdef CONFIG_MODULES -- if (mod) -- __MOD_INC_USE_COUNT(mod); --#endif -- return 1; --} --#endif -- -- --/* Yes, I'm aware that it's a fairly ugly hack. -- Until the __constant_* macros appear in Linus' own kernels, this is -- the way it has to be done. -- DW 19/1/00 -- */ -- --#include -- --#ifndef __constant_cpu_to_le16 -- --#ifdef __BIG_ENDIAN --#define __constant_cpu_to_le64(x) ___swab64((x)) --#define __constant_le64_to_cpu(x) ___swab64((x)) --#define __constant_cpu_to_le32(x) ___swab32((x)) --#define __constant_le32_to_cpu(x) ___swab32((x)) --#define __constant_cpu_to_le16(x) ___swab16((x)) --#define __constant_le16_to_cpu(x) ___swab16((x)) --#define __constant_cpu_to_be64(x) ((__u64)(x)) --#define __constant_be64_to_cpu(x) ((__u64)(x)) --#define __constant_cpu_to_be32(x) ((__u32)(x)) --#define __constant_be32_to_cpu(x) ((__u32)(x)) --#define __constant_cpu_to_be16(x) ((__u16)(x)) --#define __constant_be16_to_cpu(x) ((__u16)(x)) --#else --#ifdef __LITTLE_ENDIAN --#define __constant_cpu_to_le64(x) ((__u64)(x)) --#define __constant_le64_to_cpu(x) ((__u64)(x)) --#define __constant_cpu_to_le32(x) ((__u32)(x)) --#define __constant_le32_to_cpu(x) ((__u32)(x)) --#define __constant_cpu_to_le16(x) ((__u16)(x)) --#define __constant_le16_to_cpu(x) ((__u16)(x)) --#define __constant_cpu_to_be64(x) ___swab64((x)) --#define __constant_be64_to_cpu(x) ___swab64((x)) --#define __constant_cpu_to_be32(x) ___swab32((x)) --#define __constant_be32_to_cpu(x) ___swab32((x)) --#define __constant_cpu_to_be16(x) ___swab16((x)) --#define __constant_be16_to_cpu(x) ___swab16((x)) --#else --#error No (recognised) endianness defined (unless it,s PDP) --#endif /* __LITTLE_ENDIAN */ --#endif /* __BIG_ENDIAN */ -- --#endif /* ifndef __constant_cpu_to_le16 */ -- --#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -- #define mod_init_t int __init -- #define mod_exit_t void --#else -- #define mod_init_t static int __init -- #define mod_exit_t static void __exit --#endif -+ 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 --#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0) --#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0) --#else --#include --#include --#endif -+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 -- --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 -+#include - --#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 --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 - + * 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 */ --/* $Id$ */ -+/* -+ * Linux driver for Disk-On-Chip devices -+ * -+ * Copyright (C) 1999 Machine Vision Holdings, Inc. -+ * Copyright (C) 2001-2003 David Woodhouse -+ * Copyright (C) 2002-2003 Greg Ungerer -+ * Copyright (C) 2002-2003 SnapGear Inc -+ * -+ * $Id$ + /* + * 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 -+#include - - #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 + #include -@@ -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 + #include + #include ++#include + #include + + #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 ++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 - #include - #include -+#include ++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 + * -+ * $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 -+#include -+#include ++ Limit the concurrent number of kmod modprobes to catch loops from ++ "modprobe needs a service that is in a module". ++ Keith Owens December 1999 + -+#include ++ Unblock all signals when we exec a usermode process. ++ Shuu Yamaguchi 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#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 "); ++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 --#include - - #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 - #include --#include --#include -+#include -+#include -+#include -+#include -+#include -+#include ++#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; iproc_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; itimeout.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; idata); ++ 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; iname, 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; inn; ++ 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= 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 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 - #include - #include --#include - #include - #include - --#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 -+#include - - #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 ++#include ++#include ++#include ++#include ++#include ++#include + - /* 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 -- * Steven J. Hill -+ * Steven J. Hill - * Thomas Gleixner - * -- * $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 --#include -+#include -+#include -+#include - --/* -- * 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<= (1<= (1< 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 + #include +@@ -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 */ --/* $Id$ */ -+/* -+ * $Id$ -+ * -+ * (C) 1999-2003 David Woodhouse -+ */ +- 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 --#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 + current->nice = -15; --#ifdef __KERNEL__ -+#include +- 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 ++ ++ 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ ++ 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 ++#include ++ ++#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 + -+#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 ++#include + -+#if defined(CONFIG_MTD_PHYSMAP) ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#include -+#include -+#include ++#include ++#include + -+/* -+ * The map_info for physmap. Board can override size, buswidth, phys, -+ * (*set_vpp)(), etc in their initial setup routine. -+ */ -+extern struct map_info physmap_map; ++#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 -+ * -+ * 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 ++ 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 ++ 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 -+#ifdef CONFIG_ARCH_PXA -+#include -+#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 -+#include - #include - - /* -@@ -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 -+ -+ 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 "); ++ ++ 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 "); ++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 ++ ++ 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 ++#include + ++#include ++#include +#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+typedef struct rb_node_s ++#include ++#include ++ ++#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 -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- -- linux/include/linux/rbtree.h -- -- To use rbtrees you'll have to implement your own insert and search cores. -- This will avoid us to use callbacks and to drop drammatically performances. -- I know it's not the cleaner way, but in C (not in C++) to get -- performances and genericity... -- -- Some example of insert and search follows here. The search is a plain -- normal search over an ordered tree. The insert instead must be implemented -- int two steps: as first thing the code must insert the element in -- order as a red leaf in the tree, then the support library function -- rb_insert_color() must be called. Such function will do the -- not trivial work to rebalance the rbtree if necessary. -- ------------------------------------------------------------------------- --static inline struct page * rb_search_page_cache(struct inode * inode, -- unsigned long offset) --{ -- rb_node_t * n = inode->i_rb_page_cache.rb_node; -- struct page * page; -- -- while (n) -- { -- page = rb_entry(n, struct page, rb_page_cache); -- -- if (offset < page->offset) -- n = n->rb_left; -- else if (offset > page->offset) -- n = n->rb_right; -- else -- return page; -- } -- return NULL; --} -- --static inline struct page * __rb_insert_page_cache(struct inode * inode, -- unsigned long offset, -- rb_node_t * node) --{ -- rb_node_t ** p = &inode->i_rb_page_cache.rb_node; -- rb_node_t * parent = NULL; -- struct page * page; -- -- while (*p) -- { -- parent = *p; -- page = rb_entry(parent, struct page, rb_page_cache); -- -- if (offset < page->offset) -- p = &(*p)->rb_left; -- else if (offset > page->offset) -- p = &(*p)->rb_right; -- else -- return page; -- } -- -- rb_link_node(node, parent, p); -- -- return NULL; --} -- --static inline struct page * rb_insert_page_cache(struct inode * inode, -- unsigned long offset, -- rb_node_t * node) --{ -- struct page * ret; -- if ((ret = __rb_insert_page_cache(inode, offset, node))) -- goto out; -- rb_insert_color(node, &inode->i_rb_page_cache); -- out: -- return ret; --} ------------------------------------------------------------------------- --*/ -- --#ifndef _LINUX_RBTREE_H --#define _LINUX_RBTREE_H -- --#include --#include -- --typedef struct rb_node_s --{ -- struct rb_node_s * rb_parent; -- int rb_color; --#define RB_RED 0 --#define RB_BLACK 1 -- struct rb_node_s * rb_right; -- struct rb_node_s * rb_left; --} --rb_node_t; -+ * 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 - --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 -+#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 - -- *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 ++ return err; + -+/** -+ * struct rs_control - rs control structure -+ * -+ * @mm: Bits per symbol -+ * @nn: Symbols per block (= (1<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 ++static int cmtp_sock_create(struct socket *sock, int protocol) ++{ ++ struct sock *sk; + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#include_next -+#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 - - * 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 /* for "caddr_t" et al */ - #include /* for "struct sockaddr" et al */ - #include /* 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 ++O_TARGET := hidp.o + -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) -+#include_next -+#else -+#include -+#define work_struct tq_struct -+#define schedule_work(x) schedule_task(x) -+#define flush_scheduled_work flush_scheduled_tasks -+#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z) -+#endif ++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 + -+#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 ++#include + -+#define SECTORSIZE 512 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+/* Block Control Information */ ++#include + -+struct inftl_bci { -+ uint8_t ECCsig[6]; -+ uint8_t Status; -+ uint8_t Status1; -+} __attribute__((packed)); ++#include ++#include + -+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 -+#include -+#include ++ 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 "); + -+#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 "); ++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 + -+#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 ++#include ++ ++/* 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 + -+#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 ++#include ++#include + -+/* This file is blessed for inclusion by userspace */ -+#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+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 - - * 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 - #include - --#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 -+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 + #include +@@ -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 -+#include -+#include -+#include -+#include -+#include -+#include + 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 + #include + +-#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<= (1<= (1< 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 @@ @@ -100115,7 +108048,15 @@ /* 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 EXPORT_SYMBOL(wireless_send_event); diff --git a/packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb b/packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb index fc249fee6f..3e8d26e385 100644 --- a/packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb +++ b/packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb @@ -5,7 +5,7 @@ LICENSE = "GPL" KV = "2.4.21" RMKV = "2" PXAV = "1" -PR = "r4" +PR = "r5" SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-${KV}.tar.bz2 \ http://lorien.handhelds.org/ftp.arm.linux.org.uk/kernel/v2.4/patch-${KV}-rmk${RMKV}.bz2;patch=1 \ -- cgit v1.2.3